Compare commits

..

3 Commits

Author SHA1 Message Date
Haoyu Wang
6bb072818f feat(cli/skills): add support for generating agent skills from toolset 2026-02-03 13:30:12 -05:00
Twisha Bansal
3f1908a822 docs: fix JS quickstart (#2406)
## Description

Response is being re-assigned

## PR Checklist

> Thank you for opening a Pull Request! Before submitting your PR, there
are a
> few things you can do to make sure it goes smoothly:

- [ ] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [ ] Make sure to open an issue as a

[bug/issue](https://github.com/googleapis/genai-toolbox/issues/new/choose)
  before writing your code! That way we can discuss the change, evaluate
  designs, and agree on the general idea
- [ ] Ensure the tests and linter pass
- [ ] Code coverage does not decrease (if any source code was changed)
- [ ] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #<issue_number_goes_here>
2026-02-03 11:36:16 +05:30
Yuan Teoh
eef7a94977 docs: close notice shortcode (#2404)
fix notice shortcode closing brackets.
2026-02-02 17:38:15 -08:00
88 changed files with 2009 additions and 1398 deletions

View File

@@ -35,6 +35,7 @@ import (
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/auth" "github.com/googleapis/genai-toolbox/internal/auth"
"github.com/googleapis/genai-toolbox/internal/cli/invoke" "github.com/googleapis/genai-toolbox/internal/cli/invoke"
"github.com/googleapis/genai-toolbox/internal/cli/skills"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/log" "github.com/googleapis/genai-toolbox/internal/log"
"github.com/googleapis/genai-toolbox/internal/prebuiltconfigs" "github.com/googleapis/genai-toolbox/internal/prebuiltconfigs"
@@ -401,6 +402,8 @@ func NewCommand(opts ...Option) *Command {
// Register subcommands for tool invocation // Register subcommands for tool invocation
baseCmd.AddCommand(invoke.NewCommand(cmd)) baseCmd.AddCommand(invoke.NewCommand(cmd))
// Register subcommands for skill generation
baseCmd.AddCommand(skills.NewCommand(cmd))
return cmd return cmd
} }

179
cmd/skill_generate_test.go Normal file
View File

@@ -0,0 +1,179 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// 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
//
// 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
// limitations under the License.
package cmd
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"time"
)
func TestGenerateSkill(t *testing.T) {
// Create a temporary directory for tests
tmpDir := t.TempDir()
outputDir := filepath.Join(tmpDir, "skills")
// Create a tools.yaml file with a sqlite tool
toolsFileContent := `
sources:
my-sqlite:
kind: sqlite
database: test.db
tools:
hello-sqlite:
kind: sqlite-sql
source: my-sqlite
description: "hello tool"
statement: "SELECT 'hello' as greeting"
`
toolsFilePath := filepath.Join(tmpDir, "tools.yaml")
if err := os.WriteFile(toolsFilePath, []byte(toolsFileContent), 0644); err != nil {
t.Fatalf("failed to write tools file: %v", err)
}
args := []string{
"skills-generate",
"--tools-file", toolsFilePath,
"--output-dir", outputDir,
"--name", "hello-sqlite",
"--description", "hello tool",
}
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
_, got, err := invokeCommandWithContext(ctx, args)
if err != nil {
t.Fatalf("command failed: %v\nOutput: %s", err, got)
}
// Verify generated directory structure
skillPath := filepath.Join(outputDir, "hello-sqlite")
if _, err := os.Stat(skillPath); os.IsNotExist(err) {
t.Fatalf("skill directory not created: %s", skillPath)
}
// Check SKILL.md
skillMarkdown := filepath.Join(skillPath, "SKILL.md")
content, err := os.ReadFile(skillMarkdown)
if err != nil {
t.Fatalf("failed to read SKILL.md: %v", err)
}
expectedFrontmatter := `---
name: hello-sqlite
description: hello tool
---`
if !strings.HasPrefix(string(content), expectedFrontmatter) {
t.Errorf("SKILL.md does not have expected frontmatter format.\nExpected prefix:\n%s\nGot:\n%s", expectedFrontmatter, string(content))
}
if !strings.Contains(string(content), "## Usage") {
t.Errorf("SKILL.md does not contain '## Usage' section")
}
if !strings.Contains(string(content), "## Scripts") {
t.Errorf("SKILL.md does not contain '## Scripts' section")
}
if !strings.Contains(string(content), "### hello-sqlite") {
t.Errorf("SKILL.md does not contain '### hello-sqlite' tool header")
}
// Check script file
scriptFilename := "hello-sqlite.js"
scriptPath := filepath.Join(skillPath, "scripts", scriptFilename)
if _, err := os.Stat(scriptPath); os.IsNotExist(err) {
t.Fatalf("script file not created: %s", scriptPath)
}
scriptContent, err := os.ReadFile(scriptPath)
if err != nil {
t.Fatalf("failed to read script file: %v", err)
}
if !strings.Contains(string(scriptContent), "hello-sqlite") {
t.Errorf("script file does not contain expected tool name")
}
// Check assets
assetPath := filepath.Join(skillPath, "assets", "hello-sqlite.yaml")
if _, err := os.Stat(assetPath); os.IsNotExist(err) {
t.Fatalf("asset file not created: %s", assetPath)
}
assetContent, err := os.ReadFile(assetPath)
if err != nil {
t.Fatalf("failed to read asset file: %v", err)
}
if !strings.Contains(string(assetContent), "hello-sqlite") {
t.Errorf("asset file does not contain expected tool name")
}
}
func TestGenerateSkill_NoConfig(t *testing.T) {
tmpDir := t.TempDir()
outputDir := filepath.Join(tmpDir, "skills")
args := []string{
"skills-generate",
"--output-dir", outputDir,
"--name", "test",
"--description", "test",
}
_, _, err := invokeCommandWithContext(context.Background(), args)
if err == nil {
t.Fatal("expected command to fail when no configuration is provided and tools.yaml is missing")
}
// Should not have created the directory if no config was processed
if _, err := os.Stat(outputDir); !os.IsNotExist(err) {
t.Errorf("output directory should not have been created")
}
}
func TestGenerateSkill_MissingArguments(t *testing.T) {
tmpDir := t.TempDir()
toolsFilePath := filepath.Join(tmpDir, "tools.yaml")
if err := os.WriteFile(toolsFilePath, []byte("tools: {}"), 0644); err != nil {
t.Fatalf("failed to write tools file: %v", err)
}
tests := []struct {
name string
args []string
}{
{
name: "missing name",
args: []string{"skills-generate", "--tools-file", toolsFilePath, "--description", "test"},
},
{
name: "missing description",
args: []string{"skills-generate", "--tools-file", toolsFilePath, "--name", "test"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
_, got, err := invokeCommandWithContext(context.Background(), tt.args)
if err == nil {
t.Fatalf("expected command to fail due to missing arguments, but it succeeded\nOutput: %s", got)
}
})
}
}

View File

@@ -53,7 +53,7 @@ export async function main() {
for (const query of queries) { for (const query of queries) {
conversationHistory.push({ role: "user", content: [{ text: query }] }); conversationHistory.push({ role: "user", content: [{ text: query }] });
const response = await ai.generate({ let response = await ai.generate({
messages: conversationHistory, messages: conversationHistory,
tools: tools, tools: tools,
}); });

View File

@@ -0,0 +1,111 @@
---
title: "Generate Agent Skills"
type: docs
weight: 10
description: >
How to generate agent skills from a toolset.
---
The `skills-generate` command allows you to convert a **toolset** into an **Agent Skill**. A toolset is a collection of tools, and the generated skill will contain metadata and execution scripts for all tools within that toolset, complying with the [Agent Skill specification](https://agentskills.io/specification).
## Before you begin
1. Make sure you have the `toolbox` executable in your PATH.
2. Make sure you have [Node.js](https://nodejs.org/) installed on your system.
## Generating a Skill from a Toolset
A skill package consists of a `SKILL.md` file (with required YAML frontmatter) and a set of Node.js scripts. Each tool defined in your toolset maps to a corresponding script in the generated Node.js scripts (`.js`) that work across different platforms (Linux, macOS, Windows).
### Command Usage
The basic syntax for the command is:
```bash
toolbox [--tools-file <path> | --prebuilt <name>] skills-generate \
--name <skill-name> \
--toolset <toolset-name> \
--description <description> \
--output-dir <output-directory>
```
- `--name`: Name of the generated skill.
- `--description`: Description of the generated skill.
- `--toolset`: (Optional) Name of the toolset to convert into a skill. If not provided, all tools will be included.
- `--output-dir`: (Optional) Directory to output generated skills (default: "skills").
{{< notice note >}}
**Note:** The `<skill-name>` must follow the Agent Skill [naming convention](https://agentskills.io/specification): it must contain only lowercase alphanumeric characters and hyphens, cannot start or end with a hyphen, and cannot contain consecutive hyphens (e.g., `my-skill`, `data-processing`).
{{< /notice >}}
### Example: Custom Tools File
1. Create a `tools.yaml` file with a toolset and some tools:
```yaml
tools:
tool_a:
description: "First tool"
run:
command: "echo 'Tool A'"
tool_b:
description: "Second tool"
run:
command: "echo 'Tool B'"
toolsets:
my_toolset:
tools:
- tool_a
- tool_b
```
2. Generate the skill:
```bash
toolbox --tools-file tools.yaml skills-generate \
--name "my-skill" \
--toolset "my_toolset" \
--description "A skill containing multiple tools" \
--output-dir "generated-skills"
```
3. The generated skill directory structure:
```text
generated-skills/
└── my-skill/
├── SKILL.md
├── assets/
│ ├── tool_a.yaml
│ └── tool_b.yaml
└── scripts/
├── tool_a.js
└── tool_b.js
```
In this example, the skill contains two Node.js scripts (`tool_a.js` and `tool_b.js`), each mapping to a tool in the original toolset.
### Example: Prebuilt Configuration
You can also generate skills from prebuilt toolsets:
```bash
toolbox --prebuilt alloydb-postgres-admin skills-generate \
--name "alloydb-postgres-admin" \
--description "skill for performing administrative operations on alloydb"
```
## Installing the Generated Skill in Gemini CLI
Once you have generated a skill, you can install it into the Gemini CLI using the `gemini skills install` command.
### Installation Command
Provide the path to the directory containing the generated skill:
```bash
gemini skills install /path/to/generated-skills/my-skill
```
Alternatively, use ~/.gemini/skills as the `--output-dir` to generate the skill straight to the Gemini CLI.

View File

@@ -13,14 +13,14 @@ The `invoke` command allows you to invoke tools defined in your configuration di
{{< notice tip >}} {{< notice tip >}}
**Keep configurations minimal:** The `invoke` command initializes *all* resources (sources, tools, etc.) defined in your configuration files during execution. To ensure fast response times, consider using a minimal configuration file containing only the tools you need for the specific invocation. **Keep configurations minimal:** The `invoke` command initializes *all* resources (sources, tools, etc.) defined in your configuration files during execution. To ensure fast response times, consider using a minimal configuration file containing only the tools you need for the specific invocation.
{{< notice tip >}} {{< /notice >}}
## Prerequisites ## Before you begin
- You have the `toolbox` binary installed or built. 1. Make sure you have the `toolbox` binary installed or built.
- You have a valid tool configuration file (e.g., `tools.yaml`). 2. Make sure you have a valid tool configuration file (e.g., `tools.yaml`).
## Basic Usage ### Command Usage
The basic syntax for the command is: The basic syntax for the command is:

View File

@@ -32,7 +32,8 @@ description: >
## Sub Commands ## Sub Commands
### `invoke` <details>
<summary><code>invoke</code></summary>
Executes a tool directly with the provided parameters. This is useful for testing tool configurations and parameters without needing a full client setup. Executes a tool directly with the provided parameters. This is useful for testing tool configurations and parameters without needing a full client setup.
@@ -42,8 +43,36 @@ Executes a tool directly with the provided parameters. This is useful for testin
toolbox invoke <tool-name> [params] toolbox invoke <tool-name> [params]
``` ```
- `<tool-name>`: The name of the tool to execute (as defined in your configuration). **Flags:**
- `[params]`: (Optional) A JSON string containing the parameters for the tool.
- `--tool-name`: The name of the tool to execute (as defined in your configuration).
- `params`: (Optional) A JSON string containing the parameters for the tool.
For more detailed instructions, see [Invoke Tools via CLI](../how-to/invoke_tool.md).
</details>
<details>
<summary><code>skills-generate</code></summary>
Generates a skill package from a specified toolset. Each tool in the toolset will have a corresponding Node.js execution script in the generated skill.
**Syntax:**
```bash
toolbox [--tools-file <path> | --prebuilt <name>] skills-generate --name <name> --description <description> --toolset <toolset> --output-dir <output>
```
**Flags:**
- `--name`: Name of the generated skill.
- `--description`: Description of the generated skill.
- `--toolset`: (Optional) Name of the toolset to convert into a skill. If not provided, all tools will be included.
- `--output-dir`: (Optional) Directory to output generated skills (default: "skills").
For more detailed instructions, see [Generate Agent Skills](../how-to/generate_skill.md).
</details>
## Examples ## Examples

View File

@@ -0,0 +1,237 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// 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
//
// 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
// limitations under the License.
package skills
import (
"context"
_ "embed"
"fmt"
"os"
"path/filepath"
"sort"
"github.com/googleapis/genai-toolbox/internal/log"
"github.com/googleapis/genai-toolbox/internal/server"
"github.com/googleapis/genai-toolbox/internal/server/resources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/spf13/cobra"
)
// RootCommand defines the interface for required by skills-generate subcommand.
// This allows subcommands to access shared resources and functionality without
// direct coupling to the root command's implementation.
type RootCommand interface {
// Config returns a copy of the current server configuration.
Config() server.ServerConfig
// LoadConfig loads and merges the configuration from files, folders, and prebuilts.
LoadConfig(ctx context.Context) error
// Setup initializes the runtime environment, including logging and telemetry.
// It returns the updated context and a shutdown function to be called when finished.
Setup(ctx context.Context) (context.Context, func(context.Context) error, error)
// Logger returns the logger instance.
Logger() log.Logger
}
// Command is the command for generating skills.
type Command struct {
*cobra.Command
rootCmd RootCommand
name string
description string
toolset string
outputDir string
}
// NewCommand creates a new Command.
func NewCommand(rootCmd RootCommand) *cobra.Command {
cmd := &Command{
rootCmd: rootCmd,
}
cmd.Command = &cobra.Command{
Use: "skills-generate",
Short: "Generate skills from tool configurations",
RunE: func(c *cobra.Command, args []string) error {
return cmd.run(c)
},
}
cmd.Flags().StringVar(&cmd.name, "name", "", "Name of the generated skill.")
cmd.Flags().StringVar(&cmd.description, "description", "", "Description of the generated skill")
cmd.Flags().StringVar(&cmd.toolset, "toolset", "", "Name of the toolset to convert into a skill. If not provided, all tools will be included.")
cmd.Flags().StringVar(&cmd.outputDir, "output-dir", "skills", "Directory to output generated skills")
_ = cmd.MarkFlagRequired("name")
_ = cmd.MarkFlagRequired("description")
return cmd.Command
}
func (c *Command) run(cmd *cobra.Command) error {
ctx, cancel := context.WithCancel(cmd.Context())
defer cancel()
ctx, shutdown, err := c.rootCmd.Setup(ctx)
if err != nil {
return err
}
defer func() {
_ = shutdown(ctx)
}()
logger := c.rootCmd.Logger()
// Load and merge tool configurations
if err := c.rootCmd.LoadConfig(ctx); err != nil {
return err
}
if err := os.MkdirAll(c.outputDir, 0755); err != nil {
errMsg := fmt.Errorf("error creating output directory: %w", err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
logger.InfoContext(ctx, fmt.Sprintf("Generating skill '%s'...", c.name))
// Initialize toolbox and collect tools
allTools, err := c.collectTools(ctx)
if err != nil {
errMsg := fmt.Errorf("error collecting tools: %w", err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
if len(allTools) == 0 {
logger.InfoContext(ctx, "No tools found to generate.")
return nil
}
// Generate the combined skill directory
skillPath := filepath.Join(c.outputDir, c.name)
if err := os.MkdirAll(skillPath, 0755); err != nil {
errMsg := fmt.Errorf("error creating skill directory: %w", err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
// Generate assets directory
assetsPath := filepath.Join(skillPath, "assets")
if err := os.MkdirAll(assetsPath, 0755); err != nil {
errMsg := fmt.Errorf("error creating assets dir: %w", err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
// Generate scripts directory
scriptsPath := filepath.Join(skillPath, "scripts")
if err := os.MkdirAll(scriptsPath, 0755); err != nil {
errMsg := fmt.Errorf("error creating scripts dir: %w", err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
// Iterate over keys to ensure deterministic order
var toolNames []string
for name := range allTools {
toolNames = append(toolNames, name)
}
sort.Strings(toolNames)
for _, toolName := range toolNames {
// Generate YAML config in asset directory
minimizedContent, err := generateToolConfigYAML(c.rootCmd.Config(), toolName)
if err != nil {
errMsg := fmt.Errorf("error generating filtered config for %s: %w", toolName, err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
specificToolsFileName := fmt.Sprintf("%s.yaml", toolName)
if minimizedContent != nil {
destPath := filepath.Join(assetsPath, specificToolsFileName)
if err := os.WriteFile(destPath, minimizedContent, 0644); err != nil {
errMsg := fmt.Errorf("error writing filtered config for %s: %w", toolName, err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
}
// Generate wrapper script in scripts directory
scriptContent, err := generateScriptContent(toolName, specificToolsFileName)
if err != nil {
errMsg := fmt.Errorf("error generating script content for %s: %w", toolName, err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
scriptFilename := filepath.Join(scriptsPath, fmt.Sprintf("%s.js", toolName))
if err := os.WriteFile(scriptFilename, []byte(scriptContent), 0755); err != nil {
errMsg := fmt.Errorf("error writing script %s: %w", scriptFilename, err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
}
// Generate SKILL.md
skillContent, err := generateSkillMarkdown(c.name, c.description, allTools)
if err != nil {
errMsg := fmt.Errorf("error generating SKILL.md content: %w", err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
skillMdPath := filepath.Join(skillPath, "SKILL.md")
if err := os.WriteFile(skillMdPath, []byte(skillContent), 0644); err != nil {
errMsg := fmt.Errorf("error writing SKILL.md: %w", err)
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
logger.InfoContext(ctx, fmt.Sprintf("Successfully generated skill '%s' with %d tools.", c.name, len(allTools)))
return nil
}
func (c *Command) collectTools(ctx context.Context) (map[string]tools.Tool, error) {
// Initialize Resources
sourcesMap, authServicesMap, embeddingModelsMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap, err := server.InitializeConfigs(ctx, c.rootCmd.Config())
if err != nil {
return nil, fmt.Errorf("failed to initialize resources: %w", err)
}
resourceMgr := resources.NewResourceManager(sourcesMap, authServicesMap, embeddingModelsMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap)
result := make(map[string]tools.Tool)
if c.toolset == "" {
return toolsMap, nil
}
ts, ok := resourceMgr.GetToolset(c.toolset)
if !ok {
return nil, fmt.Errorf("toolset %q not found", c.toolset)
}
for _, t := range ts.Tools {
if t != nil {
tool := *t
result[tool.McpManifest().Name] = tool
}
}
return result, nil
}

View File

@@ -0,0 +1,301 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// 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
//
// 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
// limitations under the License.
package skills
import (
"bytes"
"encoding/json"
"fmt"
"sort"
"strings"
"text/template"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/server"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
const skillTemplate = `---
name: {{.SkillName}}
description: {{.SkillDescription}}
---
## Usage
All scripts can be executed using Node.js. Replace ` + "`" + `<param_name>` + "`" + ` and ` + "`" + `<param_value>` + "`" + ` with actual values.
**Bash:**
` + "`" + `node scripts/<script_name>.js '{"<param_name>": "<param_value>"}'` + "`" + `
**PowerShell:**
` + "`" + `node scripts/<script_name>.js '{\"<param_name>\": \"<param_value>\"}'` + "`" + `
## Scripts
{{range .Tools}}
### {{.Name}}
{{.Description}}
{{.ParametersSchema}}
---
{{end}}
`
type toolTemplateData struct {
Name string
Description string
ParametersSchema string
}
type skillTemplateData struct {
SkillName string
SkillDescription string
Tools []toolTemplateData
}
// generateSkillMarkdown generates the content of the SKILL.md file.
// It includes usage instructions and a reference section for each tool in the skill,
// detailing its description and parameters.
func generateSkillMarkdown(skillName, skillDescription string, toolsMap map[string]tools.Tool) (string, error) {
var toolsData []toolTemplateData
// Order tools based on name
var toolNames []string
for name := range toolsMap {
toolNames = append(toolNames, name)
}
sort.Strings(toolNames)
for _, name := range toolNames {
tool := toolsMap[name]
manifest := tool.Manifest()
parametersSchema, err := formatParameters(manifest.Parameters)
if err != nil {
return "", err
}
toolsData = append(toolsData, toolTemplateData{
Name: name,
Description: manifest.Description,
ParametersSchema: parametersSchema,
})
}
data := skillTemplateData{
SkillName: skillName,
SkillDescription: skillDescription,
Tools: toolsData,
}
tmpl, err := template.New("markdown").Parse(skillTemplate)
if err != nil {
return "", fmt.Errorf("error parsing markdown template: %w", err)
}
var buf strings.Builder
if err := tmpl.Execute(&buf, data); err != nil {
return "", fmt.Errorf("error executing markdown template: %w", err)
}
return buf.String(), nil
}
const nodeScriptTemplate = `#!/usr/bin/env node
const { spawn, execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
const toolName = "{{.Name}}";
const toolsFileName = "{{.ToolsFileName}}";
function getToolboxPath() {
try {
const checkCommand = process.platform === 'win32' ? 'where toolbox' : 'which toolbox';
const globalPath = execSync(checkCommand, { stdio: 'pipe', encoding: 'utf-8' }).trim();
if (globalPath) {
return globalPath.split('\n')[0].trim();
}
} catch (e) {
// Ignore error;
}
const localPath = path.resolve(__dirname, '../../../toolbox');
if (fs.existsSync(localPath)) {
return localPath;
}
throw new Error("Toolbox binary not found");
}
let toolboxBinary;
try {
toolboxBinary = getToolboxPath();
} catch (err) {
console.error("Error:", err.message);
process.exit(1);
}
let configArgs = [];
if (toolsFileName) {
configArgs.push("--tools-file", path.join(__dirname, "..", "assets", toolsFileName));
}
const args = process.argv.slice(2);
const toolboxArgs = [...configArgs, "invoke", toolName, ...args];
const child = spawn(toolboxBinary, toolboxArgs, { stdio: 'inherit' });
child.on('close', (code) => {
process.exit(code);
});
child.on('error', (err) => {
console.error("Error executing toolbox:", err);
process.exit(1);
});
`
type scriptData struct {
Name string
ToolsFileName string
}
// generateScriptContent creates the content for a Node.js wrapper script.
// This script invokes the toolbox CLI with the appropriate configuration
// (using a generated tools file) and arguments to execute the specific tool.
func generateScriptContent(name string, toolsFileName string) (string, error) {
data := scriptData{
Name: name,
ToolsFileName: toolsFileName,
}
tmpl, err := template.New("script").Parse(nodeScriptTemplate)
if err != nil {
return "", fmt.Errorf("error parsing script template: %w", err)
}
var buf strings.Builder
if err := tmpl.Execute(&buf, data); err != nil {
return "", fmt.Errorf("error executing script template: %w", err)
}
return buf.String(), nil
}
// formatParameters converts a list of parameter manifests into a formatted JSON schema string.
// This schema is used in the skill documentation to describe the input parameters for a tool.
func formatParameters(params []parameters.ParameterManifest) (string, error) {
if len(params) == 0 {
return "", nil
}
properties := make(map[string]interface{})
var required []string
for _, p := range params {
paramMap := map[string]interface{}{
"type": p.Type,
"description": p.Description,
}
if p.Default != nil {
paramMap["default"] = p.Default
}
properties[p.Name] = paramMap
if p.Required {
required = append(required, p.Name)
}
}
schema := map[string]interface{}{
"type": "object",
"properties": properties,
}
if len(required) > 0 {
schema["required"] = required
}
schemaJSON, err := json.MarshalIndent(schema, "", " ")
if err != nil {
return "", fmt.Errorf("error generating parameters schema: %w", err)
}
return fmt.Sprintf("#### Parameters\n\n```json\n%s\n```", string(schemaJSON)), nil
}
// generateToolConfigYAML generates the YAML configuration for a single tool and its dependency (source).
// It extracts the relevant tool and source configurations from the server config and formats them
// into a YAML document suitable for inclusion in the skill's assets.
func generateToolConfigYAML(cfg server.ServerConfig, toolName string) ([]byte, error) {
toolCfg, ok := cfg.ToolConfigs[toolName]
if !ok {
return nil, fmt.Errorf("error finding tool config: %s", toolName)
}
var documents [][]byte
// Process Tool Config
var toolMap map[string]interface{}
b, err := yaml.Marshal(toolCfg)
if err != nil {
return nil, fmt.Errorf("error marshaling tool config: %w", err)
}
if err := yaml.Unmarshal(b, &toolMap); err != nil {
return nil, fmt.Errorf("error unmarshaling tool config map: %w", err)
}
toolMap["kind"] = "tools"
toolMap["name"] = toolName
toolMap["type"] = toolCfg.ToolConfigType()
toolDoc, err := yaml.Marshal(toolMap)
if err != nil {
return nil, fmt.Errorf("error marshaling tool doc: %w", err)
}
documents = append(documents, toolDoc)
// Process Source Config
var sourceName string
if s, ok := toolMap["source"].(string); ok {
sourceName = s
}
if sourceName != "" {
if sourceCfg, ok := cfg.SourceConfigs[sourceName]; ok {
var sourceMap map[string]interface{}
b, err := yaml.Marshal(sourceCfg)
if err != nil {
return nil, fmt.Errorf("error marshaling source config: %w", err)
}
if err := yaml.Unmarshal(b, &sourceMap); err != nil {
return nil, fmt.Errorf("error unmarshaling source config map: %w", err)
}
sourceMap["kind"] = "sources"
sourceMap["name"] = sourceName
sourceMap["type"] = sourceCfg.SourceConfigType()
sourceDoc, err := yaml.Marshal(sourceMap)
if err != nil {
return nil, fmt.Errorf("error marshaling source doc: %w", err)
}
documents = append(documents, sourceDoc)
}
}
return bytes.Join(documents, []byte("---\n")), nil
}

View File

@@ -0,0 +1,327 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// 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
//
// 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
// limitations under the License.
package skills
import (
"context"
"strings"
"testing"
"github.com/googleapis/genai-toolbox/internal/server"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.opentelemetry.io/otel/trace"
)
type MockToolConfig struct {
TypeVal string
SourceVal string
OtherVal string
}
func (m MockToolConfig) ToolConfigType() string {
return m.TypeVal
}
func (m MockToolConfig) Initialize(map[string]sources.Source) (tools.Tool, error) {
return nil, nil
}
// Custom marshaling to include fields in output YAML
func (m MockToolConfig) MarshalYAML() (interface{}, error) {
return map[string]interface{}{
"source": m.SourceVal,
"other": m.OtherVal,
"type": m.TypeVal,
}, nil
}
type MockSourceConfig struct {
TypeVal string
ConnVal string
}
func (m MockSourceConfig) SourceConfigType() string {
return m.TypeVal
}
func (m MockSourceConfig) Initialize(context.Context, trace.Tracer) (sources.Source, error) {
return nil, nil
}
func (m MockSourceConfig) MarshalYAML() (interface{}, error) {
return map[string]interface{}{
"connection_string": m.ConnVal,
"type": m.TypeVal,
}, nil
}
func TestFormatParameters(t *testing.T) {
tests := []struct {
name string
params []parameters.ParameterManifest
wantContains []string
wantErr bool
}{
{
name: "empty parameters",
params: []parameters.ParameterManifest{},
wantContains: []string{""},
},
{
name: "single required string parameter",
params: []parameters.ParameterManifest{
{
Name: "param1",
Description: "A test parameter",
Type: "string",
Required: true,
},
},
wantContains: []string{
"## Parameters",
"```json",
`"type": "object"`,
`"properties": {`,
`"param1": {`,
`"type": "string"`,
`"description": "A test parameter"`,
`"required": [`,
`"param1"`,
},
},
{
name: "mixed parameters with defaults",
params: []parameters.ParameterManifest{
{
Name: "param1",
Description: "Param 1",
Type: "string",
Required: true,
},
{
Name: "param2",
Description: "Param 2",
Type: "integer",
Default: 42,
Required: false,
},
},
wantContains: []string{
`"param1": {`,
`"param2": {`,
`"default": 42`,
`"required": [`,
`"param1"`,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := formatParameters(tt.params)
if (err != nil) != tt.wantErr {
t.Errorf("formatParameters() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr {
return
}
if len(tt.params) == 0 {
if got != "" {
t.Errorf("formatParameters() = %v, want empty string", got)
}
return
}
for _, want := range tt.wantContains {
if !strings.Contains(got, want) {
t.Errorf("formatParameters() result missing expected string: %s\nGot:\n%s", want, got)
}
}
})
}
}
func TestGenerateSkillMarkdown(t *testing.T) {
toolsMap := map[string]tools.Tool{
"tool1": server.MockTool{
Description: "First tool",
Params: []parameters.Parameter{
parameters.NewStringParameter("p1", "d1"),
},
},
}
got, err := generateSkillMarkdown("MySkill", "My Description", toolsMap)
if err != nil {
t.Fatalf("generateSkillMarkdown() error = %v", err)
}
expectedSubstrings := []string{
"name: MySkill",
"description: My Description",
"## Usage",
"All scripts can be executed using Node.js",
"**Bash:**",
"`node scripts/<script_name>.js '{\"<param_name>\": \"<param_value>\"}'`",
"**PowerShell:**",
"`node scripts/<script_name>.js '{\"<param_name>\": \"<param_value>\"}'`",
"## Scripts",
"### tool1",
"First tool",
"## Parameters",
}
for _, s := range expectedSubstrings {
if !strings.Contains(got, s) {
t.Errorf("generateSkillMarkdown() missing substring %q", s)
}
}
}
func TestGenerateScriptContent(t *testing.T) {
tests := []struct {
name string
toolName string
toolsFileName string
wantContains []string
}{
{
name: "basic script",
toolName: "test-tool",
toolsFileName: "",
wantContains: []string{
`const toolName = "test-tool";`,
`const toolsFileName = "";`,
`const toolboxArgs = [...configArgs, "invoke", toolName, ...args];`,
},
},
{
name: "script with tools file",
toolName: "complex-tool",
toolsFileName: "tools.yaml",
wantContains: []string{
`const toolName = "complex-tool";`,
`const toolsFileName = "tools.yaml";`,
`configArgs.push("--tools-file", path.join(__dirname, "..", "assets", toolsFileName));`,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := generateScriptContent(tt.toolName, tt.toolsFileName)
if err != nil {
t.Fatalf("generateScriptContent() error = %v", err)
}
for _, s := range tt.wantContains {
if !strings.Contains(got, s) {
t.Errorf("generateScriptContent() missing substring %q\nGot:\n%s", s, got)
}
}
})
}
}
func TestGenerateToolConfigYAML(t *testing.T) {
cfg := server.ServerConfig{
ToolConfigs: server.ToolConfigs{
"tool1": MockToolConfig{
TypeVal: "custom-tool",
SourceVal: "src1",
OtherVal: "foo",
},
"toolNoSource": MockToolConfig{
TypeVal: "http",
},
},
SourceConfigs: server.SourceConfigs{
"src1": MockSourceConfig{
TypeVal: "postgres",
ConnVal: "conn1",
},
},
}
tests := []struct {
name string
toolName string
wantContains []string
wantErr bool
wantNil bool
}{
{
name: "tool with source",
toolName: "tool1",
wantContains: []string{
"kind: tools",
"name: tool1",
"type: custom-tool",
"source: src1",
"other: foo",
"---",
"kind: sources",
"name: src1",
"type: postgres",
"connection_string: conn1",
},
},
{
name: "tool without source",
toolName: "toolNoSource",
wantContains: []string{
"kind: tools",
"name: toolNoSource",
"type: http",
},
},
{
name: "non-existent tool",
toolName: "missing-tool",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotBytes, err := generateToolConfigYAML(cfg, tt.toolName)
if (err != nil) != tt.wantErr {
t.Errorf("generateToolConfigYAML() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr {
return
}
if tt.wantNil {
if gotBytes != nil {
t.Errorf("generateToolConfigYAML() expected nil, got %s", string(gotBytes))
}
return
}
got := string(gotBytes)
for _, want := range tt.wantContains {
if !strings.Contains(got, want) {
t.Errorf("generateToolConfigYAML() result missing expected string: %q\nGot:\n%s", want, got)
}
}
})
}
}

View File

@@ -19,6 +19,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
@@ -234,10 +235,8 @@ func toolInvokeHandler(s *Server, w http.ResponseWriter, r *http.Request) {
params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth) params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth)
if err != nil { if err != nil {
// If auth error, return 401 // If auth error, return 401
errMsg := fmt.Sprintf("error parsing authenticated parameters from ID token: %w", err) if errors.Is(err, util.ErrUnauthorized) {
var clientServerErr *util.ClientServerError s.logger.DebugContext(ctx, fmt.Sprintf("error parsing authenticated parameters from ID token: %s", err))
if errors.As(err, &clientServerErr) && clientServerErr.Code == http.StatusUnauthorized {
s.logger.DebugContext(ctx, errMsg)
_ = render.Render(w, r, newErrResponse(err, http.StatusUnauthorized)) _ = render.Render(w, r, newErrResponse(err, http.StatusUnauthorized))
return return
} }
@@ -260,49 +259,34 @@ func toolInvokeHandler(s *Server, w http.ResponseWriter, r *http.Request) {
// Determine what error to return to the users. // Determine what error to return to the users.
if err != nil { if err != nil {
var tbErr util.ToolboxError errStr := err.Error()
var statusCode int
if errors.As(err, &tbErr) { // Upstream API auth error propagation
switch tbErr.Category() { switch {
case util.CategoryAgent: case strings.Contains(errStr, "Error 401"):
// Agent Errors -> 200 OK statusCode = http.StatusUnauthorized
s.logger.DebugContext(ctx, fmt.Sprintf("Tool invocation agent error: %v", err)) case strings.Contains(errStr, "Error 403"):
_ = render.Render(w, r, newErrResponse(err, http.StatusOK)) statusCode = http.StatusForbidden
return }
case util.CategoryServer: if statusCode == http.StatusUnauthorized || statusCode == http.StatusForbidden {
// Server Errors -> Check the specific code inside if clientAuth {
var clientServerErr *util.ClientServerError // Propagate the original 401/403 error.
statusCode := http.StatusInternalServerError // Default to 500 s.logger.DebugContext(ctx, fmt.Sprintf("error invoking tool. Client credentials lack authorization to the source: %v", err))
if errors.As(err, &clientServerErr) {
if clientServerErr.Code != 0 {
statusCode = clientServerErr.Code
}
}
// Process auth error
if statusCode == http.StatusUnauthorized || statusCode == http.StatusForbidden {
if clientAuth {
// Token error, pass through 401/403
s.logger.DebugContext(ctx, fmt.Sprintf("Client credentials lack authorization: %v", err))
_ = render.Render(w, r, newErrResponse(err, statusCode))
return
}
// ADC/Config error, return 500
statusCode = http.StatusInternalServerError
}
s.logger.ErrorContext(ctx, fmt.Sprintf("Tool invocation server error: %v", err))
_ = render.Render(w, r, newErrResponse(err, statusCode)) _ = render.Render(w, r, newErrResponse(err, statusCode))
return return
} }
} else { // ADC lacking permission or credentials configuration error.
// Unknown error -> 500 internalErr := fmt.Errorf("unexpected auth error occured during Tool invocation: %w", err)
s.logger.ErrorContext(ctx, fmt.Sprintf("Tool invocation unknown error: %v", err)) s.logger.ErrorContext(ctx, internalErr.Error())
_ = render.Render(w, r, newErrResponse(err, http.StatusInternalServerError)) _ = render.Render(w, r, newErrResponse(internalErr, http.StatusInternalServerError))
return return
} }
err = fmt.Errorf("error while invoking tool: %w", err)
s.logger.DebugContext(ctx, err.Error())
_ = render.Render(w, r, newErrResponse(err, http.StatusBadRequest))
return
} }
resMarshal, err := json.Marshal(res) resMarshal, err := json.Marshal(res)

View File

@@ -24,13 +24,11 @@ import (
"testing" "testing"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/log" "github.com/googleapis/genai-toolbox/internal/log"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/resources" "github.com/googleapis/genai-toolbox/internal/server/resources"
"github.com/googleapis/genai-toolbox/internal/telemetry" "github.com/googleapis/genai-toolbox/internal/telemetry"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -42,140 +40,6 @@ var (
_ prompts.Prompt = MockPrompt{} _ prompts.Prompt = MockPrompt{}
) )
// MockTool is used to mock tools in tests
type MockTool struct {
Name string
Description string
Params []parameters.Parameter
manifest tools.Manifest
unauthorized bool
requiresClientAuthrorization bool
}
func (t MockTool) Invoke(context.Context, tools.SourceProvider, parameters.ParamValues, tools.AccessToken) (any, util.ToolboxError) {
mock := []any{t.Name}
return mock, nil
}
func (t MockTool) ToConfig() tools.ToolConfig {
return nil
}
// claims is a map of user info decoded from an auth token
func (t MockTool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.Params, data, claimsMap)
}
func (t MockTool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.Params, paramValues, embeddingModelsMap, nil)
}
func (t MockTool) Manifest() tools.Manifest {
pMs := make([]parameters.ParameterManifest, 0, len(t.Params))
for _, p := range t.Params {
pMs = append(pMs, p.Manifest())
}
return tools.Manifest{Description: t.Description, Parameters: pMs}
}
func (t MockTool) Authorized(verifiedAuthServices []string) bool {
// defaulted to true
return !t.unauthorized
}
func (t MockTool) RequiresClientAuthorization(tools.SourceProvider) (bool, error) {
// defaulted to false
return t.requiresClientAuthrorization, nil
}
func (t MockTool) GetParameters() parameters.Parameters {
return t.Params
}
func (t MockTool) McpManifest() tools.McpManifest {
properties := make(map[string]parameters.ParameterMcpManifest)
required := make([]string, 0)
authParams := make(map[string][]string)
for _, p := range t.Params {
name := p.GetName()
paramManifest, authParamList := p.McpManifest()
properties[name] = paramManifest
required = append(required, name)
if len(authParamList) > 0 {
authParams[name] = authParamList
}
}
toolsSchema := parameters.McpToolsSchema{
Type: "object",
Properties: properties,
Required: required,
}
mcpManifest := tools.McpManifest{
Name: t.Name,
Description: t.Description,
InputSchema: toolsSchema,
}
if len(authParams) > 0 {
mcpManifest.Metadata = map[string]any{
"toolbox/authParams": authParams,
}
}
return mcpManifest
}
func (t MockTool) GetAuthTokenHeaderName(tools.SourceProvider) (string, error) {
return "Authorization", nil
}
// MockPrompt is used to mock prompts in tests
type MockPrompt struct {
Name string
Description string
Args prompts.Arguments
}
func (p MockPrompt) SubstituteParams(vals parameters.ParamValues) (any, error) {
return []prompts.Message{
{
Role: "user",
Content: fmt.Sprintf("substituted %s", p.Name),
},
}, nil
}
func (p MockPrompt) ParseArgs(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) {
var params parameters.Parameters
for _, arg := range p.Args {
params = append(params, arg.Parameter)
}
return parameters.ParseParams(params, data, claimsMap)
}
func (p MockPrompt) Manifest() prompts.Manifest {
var argManifests []parameters.ParameterManifest
for _, arg := range p.Args {
argManifests = append(argManifests, arg.Manifest())
}
return prompts.Manifest{
Description: p.Description,
Arguments: argManifests,
}
}
func (p MockPrompt) McpManifest() prompts.McpManifest {
return prompts.GetMcpManifest(p.Name, p.Description, p.Args)
}
func (p MockPrompt) ToConfig() prompts.PromptConfig {
return nil
}
var tool1 = MockTool{ var tool1 = MockTool{
Name: "no_params", Name: "no_params",
Params: []parameters.Parameter{}, Params: []parameters.Parameter{},

View File

@@ -23,6 +23,7 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strings"
"sync" "sync"
"time" "time"
@@ -443,17 +444,15 @@ func httpHandler(s *Server, w http.ResponseWriter, r *http.Request) {
code := rpcResponse.Error.Code code := rpcResponse.Error.Code
switch code { switch code {
case jsonrpc.INTERNAL_ERROR: case jsonrpc.INTERNAL_ERROR:
// Map Internal RPC Error (-32603) to HTTP 500
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
case jsonrpc.INVALID_REQUEST: case jsonrpc.INVALID_REQUEST:
var clientServerErr *util.ClientServerError errStr := err.Error()
if errors.As(err, &clientServerErr) { if errors.Is(err, util.ErrUnauthorized) {
switch clientServerErr.Code { w.WriteHeader(http.StatusUnauthorized)
case http.StatusUnauthorized: } else if strings.Contains(errStr, "Error 401") {
w.WriteHeader(http.StatusUnauthorized) w.WriteHeader(http.StatusUnauthorized)
case http.StatusForbidden: } else if strings.Contains(errStr, "Error 403") {
w.WriteHeader(http.StatusForbidden) w.WriteHeader(http.StatusForbidden)
}
} }
} }
} }

View File

@@ -21,6 +21,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc" "github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
@@ -123,11 +124,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
} }
if clientAuth { if clientAuth {
if accessToken == "" { if accessToken == "" {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.NewClientServerError( return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.ErrUnauthorized
"missing access token in the 'Authorization' header",
http.StatusUnauthorized,
nil,
)
} }
} }
@@ -175,11 +172,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// Check if any of the specified auth services is verified // Check if any of the specified auth services is verified
isAuthorized := tool.Authorized(verifiedAuthServices) isAuthorized := tool.Authorized(verifiedAuthServices)
if !isAuthorized { if !isAuthorized {
err = util.NewClientServerError( err = fmt.Errorf("unauthorized Tool call: Please make sure your specify correct auth headers: %w", util.ErrUnauthorized)
"unauthorized Tool call: Please make sure you specify correct auth headers",
http.StatusUnauthorized,
nil,
)
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
} }
logger.DebugContext(ctx, "tool invocation authorized") logger.DebugContext(ctx, "tool invocation authorized")
@@ -201,44 +194,30 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// run tool invocation and generate response. // run tool invocation and generate response.
results, err := tool.Invoke(ctx, resourceMgr, params, accessToken) results, err := tool.Invoke(ctx, resourceMgr, params, accessToken)
if err != nil { if err != nil {
var tbErr util.ToolboxError errStr := err.Error()
// Missing authService tokens.
if errors.As(err, &tbErr) { if errors.Is(err, util.ErrUnauthorized) {
switch tbErr.Category() { return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
case util.CategoryAgent: }
// MCP - Tool execution error // Upstream auth error
// Return SUCCESS but with IsError: true if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "Error 403") {
text := TextContent{ if clientAuth {
Type: "text", // Error with client credentials should pass down to the client
Text: err.Error(), return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
case util.CategoryServer:
// MCP Spec - Protocol error
// Return JSON-RPC ERROR
var clientServerErr *util.ClientServerError
rpcCode := jsonrpc.INTERNAL_ERROR // Default to Internal Error (-32603)
if errors.As(err, &clientServerErr) {
if clientServerErr.Code == http.StatusUnauthorized || clientServerErr.Code == http.StatusForbidden {
if clientAuth {
rpcCode = jsonrpc.INVALID_REQUEST
} else {
rpcCode = jsonrpc.INTERNAL_ERROR
}
}
}
return jsonrpc.NewError(id, rpcCode, err.Error(), nil), err
} }
} else { // Auth error with ADC should raise internal 500 error
// Unknown error -> 500
return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err
} }
text := TextContent{
Type: "text",
Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
} }
content := make([]TextContent, 0) content := make([]TextContent, 0)

View File

@@ -21,6 +21,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc" "github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
@@ -123,11 +124,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
} }
if clientAuth { if clientAuth {
if accessToken == "" { if accessToken == "" {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.NewClientServerError( return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.ErrUnauthorized
"missing access token in the 'Authorization' header",
http.StatusUnauthorized,
nil,
)
} }
} }
@@ -175,11 +172,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// Check if any of the specified auth services is verified // Check if any of the specified auth services is verified
isAuthorized := tool.Authorized(verifiedAuthServices) isAuthorized := tool.Authorized(verifiedAuthServices)
if !isAuthorized { if !isAuthorized {
err = util.NewClientServerError( err = fmt.Errorf("unauthorized Tool call: Please make sure your specify correct auth headers: %w", util.ErrUnauthorized)
"unauthorized Tool call: Please make sure you specify correct auth headers",
http.StatusUnauthorized,
nil,
)
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
} }
logger.DebugContext(ctx, "tool invocation authorized") logger.DebugContext(ctx, "tool invocation authorized")
@@ -201,45 +194,31 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// run tool invocation and generate response. // run tool invocation and generate response.
results, err := tool.Invoke(ctx, resourceMgr, params, accessToken) results, err := tool.Invoke(ctx, resourceMgr, params, accessToken)
if err != nil { if err != nil {
var tbErr util.ToolboxError errStr := err.Error()
// Missing authService tokens.
if errors.As(err, &tbErr) { if errors.Is(err, util.ErrUnauthorized) {
switch tbErr.Category() { return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
case util.CategoryAgent: }
// MCP - Tool execution error // Upstream auth error
// Return SUCCESS but with IsError: true if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "Error 403") {
text := TextContent{ if clientAuth {
Type: "text", // Error with client credentials should pass down to the client
Text: err.Error(), return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
case util.CategoryServer:
// MCP Spec - Protocol error
// Return JSON-RPC ERROR
var clientServerErr *util.ClientServerError
rpcCode := jsonrpc.INTERNAL_ERROR // Default to Internal Error (-32603)
if errors.As(err, &clientServerErr) {
if clientServerErr.Code == http.StatusUnauthorized || clientServerErr.Code == http.StatusForbidden {
if clientAuth {
rpcCode = jsonrpc.INVALID_REQUEST
} else {
rpcCode = jsonrpc.INTERNAL_ERROR
}
}
}
return jsonrpc.NewError(id, rpcCode, err.Error(), nil), err
} }
} else { // Auth error with ADC should raise internal 500 error
// Unknown error -> 500
return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err
} }
text := TextContent{
Type: "text",
Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
} }
content := make([]TextContent, 0) content := make([]TextContent, 0)
sliceRes, ok := results.([]any) sliceRes, ok := results.([]any)

View File

@@ -21,6 +21,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc" "github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
@@ -116,12 +117,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
} }
if clientAuth { if clientAuth {
if accessToken == "" { if accessToken == "" {
errMsg := "missing access token in the 'Authorization' header" return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.ErrUnauthorized
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, errMsg, nil), util.NewClientServerError(
errMsg,
http.StatusUnauthorized,
nil,
)
} }
} }
@@ -169,11 +165,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// Check if any of the specified auth services is verified // Check if any of the specified auth services is verified
isAuthorized := tool.Authorized(verifiedAuthServices) isAuthorized := tool.Authorized(verifiedAuthServices)
if !isAuthorized { if !isAuthorized {
err = util.NewClientServerError( err = fmt.Errorf("unauthorized Tool call: Please make sure your specify correct auth headers: %w", util.ErrUnauthorized)
"unauthorized Tool call: Please make sure you specify correct auth headers",
http.StatusUnauthorized,
nil,
)
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
} }
logger.DebugContext(ctx, "tool invocation authorized") logger.DebugContext(ctx, "tool invocation authorized")
@@ -195,44 +187,29 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// run tool invocation and generate response. // run tool invocation and generate response.
results, err := tool.Invoke(ctx, resourceMgr, params, accessToken) results, err := tool.Invoke(ctx, resourceMgr, params, accessToken)
if err != nil { if err != nil {
var tbErr util.ToolboxError errStr := err.Error()
// Missing authService tokens.
if errors.As(err, &tbErr) { if errors.Is(err, util.ErrUnauthorized) {
switch tbErr.Category() { return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
case util.CategoryAgent: }
// MCP - Tool execution error // Upstream auth error
// Return SUCCESS but with IsError: true if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "Error 403") {
text := TextContent{ if clientAuth {
Type: "text", // Error with client credentials should pass down to the client
Text: err.Error(), return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
case util.CategoryServer:
// MCP Spec - Protocol error
// Return JSON-RPC ERROR
var clientServerErr *util.ClientServerError
rpcCode := jsonrpc.INTERNAL_ERROR // Default to Internal Error (-32603)
if errors.As(err, &clientServerErr) {
if clientServerErr.Code == http.StatusUnauthorized || clientServerErr.Code == http.StatusForbidden {
if clientAuth {
rpcCode = jsonrpc.INVALID_REQUEST
} else {
rpcCode = jsonrpc.INTERNAL_ERROR
}
}
}
return jsonrpc.NewError(id, rpcCode, err.Error(), nil), err
} }
} else { // Auth error with ADC should raise internal 500 error
// Unknown error -> 500
return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err
} }
text := TextContent{
Type: "text",
Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
} }
content := make([]TextContent, 0) content := make([]TextContent, 0)

View File

@@ -21,6 +21,7 @@ import (
"errors" "errors"
"fmt" "fmt"
"net/http" "net/http"
"strings"
"github.com/googleapis/genai-toolbox/internal/prompts" "github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc" "github.com/googleapis/genai-toolbox/internal/server/mcp/jsonrpc"
@@ -116,11 +117,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
} }
if clientAuth { if clientAuth {
if accessToken == "" { if accessToken == "" {
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.NewClientServerError( return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, "missing access token in the 'Authorization' header", nil), util.ErrUnauthorized
"missing access token in the 'Authorization' header",
http.StatusUnauthorized,
nil,
)
} }
} }
@@ -168,11 +165,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// Check if any of the specified auth services is verified // Check if any of the specified auth services is verified
isAuthorized := tool.Authorized(verifiedAuthServices) isAuthorized := tool.Authorized(verifiedAuthServices)
if !isAuthorized { if !isAuthorized {
err = util.NewClientServerError( err = fmt.Errorf("unauthorized Tool call: Please make sure your specify correct auth headers: %w", util.ErrUnauthorized)
"unauthorized Tool call: Please make sure you specify correct auth headers",
http.StatusUnauthorized,
nil,
)
return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
} }
logger.DebugContext(ctx, "tool invocation authorized") logger.DebugContext(ctx, "tool invocation authorized")
@@ -194,44 +187,29 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
// run tool invocation and generate response. // run tool invocation and generate response.
results, err := tool.Invoke(ctx, resourceMgr, params, accessToken) results, err := tool.Invoke(ctx, resourceMgr, params, accessToken)
if err != nil { if err != nil {
var tbErr util.ToolboxError errStr := err.Error()
// Missing authService tokens.
if errors.As(err, &tbErr) { if errors.Is(err, util.ErrUnauthorized) {
switch tbErr.Category() { return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
case util.CategoryAgent: }
// MCP - Tool execution error // Upstream auth error
// Return SUCCESS but with IsError: true if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "Error 403") {
text := TextContent{ if clientAuth {
Type: "text", // Error with client credentials should pass down to the client
Text: err.Error(), return jsonrpc.NewError(id, jsonrpc.INVALID_REQUEST, err.Error(), nil), err
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
case util.CategoryServer:
// MCP Spec - Protocol error
// Return JSON-RPC ERROR
var clientServerErr *util.ClientServerError
rpcCode := jsonrpc.INTERNAL_ERROR // Default to Internal Error (-32603)
if errors.As(err, &clientServerErr) {
if clientServerErr.Code == http.StatusUnauthorized || clientServerErr.Code == http.StatusForbidden {
if clientAuth {
rpcCode = jsonrpc.INVALID_REQUEST
} else {
rpcCode = jsonrpc.INTERNAL_ERROR
}
}
}
return jsonrpc.NewError(id, rpcCode, err.Error(), nil), err
} }
} else { // Auth error with ADC should raise internal 500 error
// Unknown error -> 500
return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err return jsonrpc.NewError(id, jsonrpc.INTERNAL_ERROR, err.Error(), nil), err
} }
text := TextContent{
Type: "text",
Text: err.Error(),
}
return jsonrpc.JSONRPCResponse{
Jsonrpc: jsonrpc.JSONRPC_VERSION,
Id: id,
Result: CallToolResult{Content: []TextContent{text}, IsError: true},
}, nil
} }
content := make([]TextContent, 0) content := make([]TextContent, 0)

159
internal/server/mocks.go Normal file
View File

@@ -0,0 +1,159 @@
// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// 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
//
// 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
// limitations under the License.
package server
import (
"context"
"fmt"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/prompts"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
// MockTool is used to mock tools in tests
type MockTool struct {
Name string
Description string
Params []parameters.Parameter
manifest tools.Manifest
unauthorized bool
requiresClientAuthrorization bool
}
func (t MockTool) Invoke(context.Context, tools.SourceProvider, parameters.ParamValues, tools.AccessToken) (any, error) {
mock := []any{t.Name}
return mock, nil
}
func (t MockTool) ToConfig() tools.ToolConfig {
return nil
}
// claims is a map of user info decoded from an auth token
func (t MockTool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.Params, data, claimsMap)
}
func (t MockTool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.Params, paramValues, embeddingModelsMap, nil)
}
func (t MockTool) Manifest() tools.Manifest {
pMs := make([]parameters.ParameterManifest, 0, len(t.Params))
for _, p := range t.Params {
pMs = append(pMs, p.Manifest())
}
return tools.Manifest{Description: t.Description, Parameters: pMs}
}
func (t MockTool) Authorized(verifiedAuthServices []string) bool {
// defaulted to true
return !t.unauthorized
}
func (t MockTool) RequiresClientAuthorization(tools.SourceProvider) (bool, error) {
// defaulted to false
return t.requiresClientAuthrorization, nil
}
func (t MockTool) GetParameters() parameters.Parameters {
return t.Params
}
func (t MockTool) McpManifest() tools.McpManifest {
properties := make(map[string]parameters.ParameterMcpManifest)
required := make([]string, 0)
authParams := make(map[string][]string)
for _, p := range t.Params {
name := p.GetName()
paramManifest, authParamList := p.McpManifest()
properties[name] = paramManifest
required = append(required, name)
if len(authParamList) > 0 {
authParams[name] = authParamList
}
}
toolsSchema := parameters.McpToolsSchema{
Type: "object",
Properties: properties,
Required: required,
}
mcpManifest := tools.McpManifest{
Name: t.Name,
Description: t.Description,
InputSchema: toolsSchema,
}
if len(authParams) > 0 {
mcpManifest.Metadata = map[string]any{
"toolbox/authParams": authParams,
}
}
return mcpManifest
}
func (t MockTool) GetAuthTokenHeaderName(tools.SourceProvider) (string, error) {
return "Authorization", nil
}
// MockPrompt is used to mock prompts in tests
type MockPrompt struct {
Name string
Description string
Args prompts.Arguments
}
func (p MockPrompt) SubstituteParams(vals parameters.ParamValues) (any, error) {
return []prompts.Message{
{
Role: "user",
Content: fmt.Sprintf("substituted %s", p.Name),
},
}, nil
}
func (p MockPrompt) ParseArgs(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) {
var params parameters.Parameters
for _, arg := range p.Args {
params = append(params, arg.Parameter)
}
return parameters.ParseParams(params, data, claimsMap)
}
func (p MockPrompt) Manifest() prompts.Manifest {
var argManifests []parameters.ParameterManifest
for _, arg := range p.Args {
argManifests = append(argManifests, arg.Manifest())
}
return prompts.Manifest{
Description: p.Description,
Arguments: argManifests,
}
}
func (p MockPrompt) McpManifest() prompts.McpManifest {
return prompts.GetMcpManifest(p.Name, p.Description, p.Args)
}
func (p MockPrompt) ToConfig() prompts.PromptConfig {
return nil
}

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -123,49 +122,44 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok || project == "" {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a non-empty string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("invalid 'location' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
} }
clusterID, ok := paramsMap["cluster"].(string) clusterID, ok := paramsMap["cluster"].(string)
if !ok || clusterID == "" { if !ok || clusterID == "" {
return nil, util.NewAgentError("invalid or missing 'cluster' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'cluster' parameter; expected a non-empty string")
} }
password, ok := paramsMap["password"].(string) password, ok := paramsMap["password"].(string)
if !ok || password == "" { if !ok || password == "" {
return nil, util.NewAgentError("invalid or missing 'password' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'password' parameter; expected a non-empty string")
} }
network, ok := paramsMap["network"].(string) network, ok := paramsMap["network"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("invalid 'network' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'network' parameter; expected a string")
} }
user, ok := paramsMap["user"].(string) user, ok := paramsMap["user"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("invalid 'user' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'user' parameter; expected a string")
}
resp, err := source.CreateCluster(ctx, project, location, network, user, password, clusterID, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
} }
return resp, nil return source.CreateCluster(ctx, project, location, network, user, password, clusterID, string(accessToken))
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -124,36 +123,36 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok || project == "" {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a non-empty string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok || location == "" { if !ok || location == "" {
return nil, util.NewAgentError("invalid or missing 'location' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'location' parameter; expected a non-empty string")
} }
cluster, ok := paramsMap["cluster"].(string) cluster, ok := paramsMap["cluster"].(string)
if !ok || cluster == "" { if !ok || cluster == "" {
return nil, util.NewAgentError("invalid or missing 'cluster' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'cluster' parameter; expected a non-empty string")
} }
instanceID, ok := paramsMap["instance"].(string) instanceID, ok := paramsMap["instance"].(string)
if !ok || instanceID == "" { if !ok || instanceID == "" {
return nil, util.NewAgentError("invalid or missing 'instance' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'instance' parameter; expected a non-empty string")
} }
instanceType, ok := paramsMap["instanceType"].(string) instanceType, ok := paramsMap["instanceType"].(string)
if !ok || (instanceType != "READ_POOL" && instanceType != "PRIMARY") { if !ok || (instanceType != "READ_POOL" && instanceType != "PRIMARY") {
return nil, util.NewAgentError("invalid 'instanceType' parameter; expected 'PRIMARY' or 'READ_POOL'", nil) return nil, fmt.Errorf("invalid 'instanceType' parameter; expected 'PRIMARY' or 'READ_POOL'")
} }
displayName, _ := paramsMap["displayName"].(string) displayName, _ := paramsMap["displayName"].(string)
@@ -162,15 +161,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if instanceType == "READ_POOL" { if instanceType == "READ_POOL" {
nodeCount, ok = paramsMap["nodeCount"].(int) nodeCount, ok = paramsMap["nodeCount"].(int)
if !ok { if !ok {
return nil, util.NewAgentError("invalid 'nodeCount' parameter; expected an integer for READ_POOL", nil) return nil, fmt.Errorf("invalid 'nodeCount' parameter; expected an integer for READ_POOL")
} }
} }
resp, err := source.CreateInstance(ctx, project, location, cluster, instanceID, instanceType, displayName, nodeCount, string(accessToken)) return source.CreateInstance(ctx, project, location, cluster, instanceID, instanceType, displayName, nodeCount, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -123,43 +122,43 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok || project == "" {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a non-empty string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok || location == "" { if !ok || location == "" {
return nil, util.NewAgentError("invalid or missing'location' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing'location' parameter; expected a non-empty string")
} }
cluster, ok := paramsMap["cluster"].(string) cluster, ok := paramsMap["cluster"].(string)
if !ok || cluster == "" { if !ok || cluster == "" {
return nil, util.NewAgentError("invalid or missing 'cluster' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'cluster' parameter; expected a non-empty string")
} }
userID, ok := paramsMap["user"].(string) userID, ok := paramsMap["user"].(string)
if !ok || userID == "" { if !ok || userID == "" {
return nil, util.NewAgentError("invalid or missing 'user' parameter; expected a non-empty string", nil) return nil, fmt.Errorf("invalid or missing 'user' parameter; expected a non-empty string")
} }
userType, ok := paramsMap["userType"].(string) userType, ok := paramsMap["userType"].(string)
if !ok || (userType != "ALLOYDB_BUILT_IN" && userType != "ALLOYDB_IAM_USER") { if !ok || (userType != "ALLOYDB_BUILT_IN" && userType != "ALLOYDB_IAM_USER") {
return nil, util.NewAgentError("invalid or missing 'userType' parameter; expected 'ALLOYDB_BUILT_IN' or 'ALLOYDB_IAM_USER'", nil) return nil, fmt.Errorf("invalid or missing 'userType' parameter; expected 'ALLOYDB_BUILT_IN' or 'ALLOYDB_IAM_USER'")
} }
var password string var password string
if userType == "ALLOYDB_BUILT_IN" { if userType == "ALLOYDB_BUILT_IN" {
password, ok = paramsMap["password"].(string) password, ok = paramsMap["password"].(string)
if !ok || password == "" { if !ok || password == "" {
return nil, util.NewAgentError("password is required when userType is ALLOYDB_BUILT_IN", nil) return nil, fmt.Errorf("password is required when userType is ALLOYDB_BUILT_IN")
} }
} }
@@ -171,11 +170,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
} }
} }
resp, err := source.CreateUser(ctx, userType, password, roles, string(accessToken), project, location, cluster, userID) return source.CreateUser(ctx, userType, password, roles, string(accessToken), project, location, cluster, userID)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -121,32 +120,28 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok || location == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'location' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
} }
cluster, ok := paramsMap["cluster"].(string) cluster, ok := paramsMap["cluster"].(string)
if !ok || cluster == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'cluster' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'cluster' parameter; expected a string")
} }
resp, err := source.GetCluster(ctx, project, location, cluster, string(accessToken)) return source.GetCluster(ctx, project, location, cluster, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -121,36 +120,32 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok || location == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'location' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
} }
cluster, ok := paramsMap["cluster"].(string) cluster, ok := paramsMap["cluster"].(string)
if !ok || cluster == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'cluster' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'cluster' parameter; expected a string")
} }
instance, ok := paramsMap["instance"].(string) instance, ok := paramsMap["instance"].(string)
if !ok || instance == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'instance' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'instance' parameter; expected a string")
} }
resp, err := source.GetInstance(ctx, project, location, cluster, instance, string(accessToken)) return source.GetInstance(ctx, project, location, cluster, instance, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -121,36 +120,32 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok || location == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'location' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
} }
cluster, ok := paramsMap["cluster"].(string) cluster, ok := paramsMap["cluster"].(string)
if !ok || cluster == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'cluster' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'cluster' parameter; expected a string")
} }
user, ok := paramsMap["user"].(string) user, ok := paramsMap["user"].(string)
if !ok || user == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'user' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'user' parameter; expected a string")
} }
resp, err := source.GetUsers(ctx, project, location, cluster, user, string(accessToken)) return source.GetUsers(ctx, project, location, cluster, user, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -119,28 +118,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("invalid 'location' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
} }
resp, err := source.ListCluster(ctx, project, location, string(accessToken)) return source.ListCluster(ctx, project, location, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -120,32 +119,28 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("invalid 'location' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
} }
cluster, ok := paramsMap["cluster"].(string) cluster, ok := paramsMap["cluster"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("invalid 'cluster' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'cluster' parameter; expected a string")
} }
resp, err := source.ListInstance(ctx, project, location, cluster, string(accessToken)) return source.ListInstance(ctx, project, location, cluster, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -22,7 +22,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -120,32 +119,28 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'project' parameter; expected a string", nil) return nil, fmt.Errorf("invalid or missing 'project' parameter; expected a string")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok || location == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'location' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'location' parameter; expected a string")
} }
cluster, ok := paramsMap["cluster"].(string) cluster, ok := paramsMap["cluster"].(string)
if !ok || cluster == "" { if !ok {
return nil, util.NewAgentError("invalid or missing 'cluster' parameter; expected a string", nil) return nil, fmt.Errorf("invalid 'cluster' parameter; expected a string")
} }
resp, err := source.ListUsers(ctx, project, location, cluster, string(accessToken)) return source.ListUsers(ctx, project, location, cluster, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -24,7 +24,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -214,25 +213,25 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", 500, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'project' parameter", nil) return nil, fmt.Errorf("missing 'project' parameter")
} }
location, ok := paramsMap["location"].(string) location, ok := paramsMap["location"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'location' parameter", nil) return nil, fmt.Errorf("missing 'location' parameter")
} }
operation, ok := paramsMap["operation"].(string) operation, ok := paramsMap["operation"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'operation' parameter", nil) return nil, fmt.Errorf("missing 'operation' parameter")
} }
ctx, cancel := context.WithTimeout(ctx, 30*time.Minute) ctx, cancel := context.WithTimeout(ctx, 30*time.Minute)
@@ -247,15 +246,14 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
for retries < maxRetries { for retries < maxRetries {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, util.NewAgentError("timed out waiting for operation %s", ctx.Err()) return nil, fmt.Errorf("timed out waiting for operation: %w", ctx.Err())
default: default:
} }
op, err := source.GetOperations(ctx, project, location, operation, alloyDBConnectionMessageTemplate, delay, string(accessToken)) op, err := source.GetOperations(ctx, project, location, operation, alloyDBConnectionMessageTemplate, delay, string(accessToken))
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, err
} } else if op != nil {
if op != nil {
return op, nil return op, nil
} }
@@ -266,7 +264,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
retries++ retries++
} }
return nil, util.NewAgentError("exceeded max retries waiting for operation", nil) return nil, fmt.Errorf("exceeded max retries waiting for operation")
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package alloydbainl
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/pgxpool"
) )
@@ -129,10 +127,10 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
sliceParams := params.AsSlice() sliceParams := params.AsSlice()
@@ -145,7 +143,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
resp, err := source.RunSQL(ctx, t.Statement, allParamValues) resp, err := source.RunSQL(ctx, t.Statement, allParamValues)
if err != nil { if err != nil {
return nil, util.NewClientServerError(fmt.Sprintf("error running SQL query: %v. Query: %v , Values: %v. Toolbox v0.19.0+ is only compatible with AlloyDB AI NL v1.0.3+. Please ensure that you are using the latest AlloyDB AI NL extension", err, t.Statement, allParamValues), http.StatusBadRequest, err) return nil, fmt.Errorf("%w. Query: %v , Values: %v. Toolbox v0.19.0+ is only compatible with AlloyDB AI NL v1.0.3+. Please ensure that you are using the latest AlloyDB AI NL extension", err, t.Statement, allParamValues)
} }
return resp, nil return resp, nil
} }

View File

@@ -17,7 +17,6 @@ package bigqueryanalyzecontribution
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
bigqueryapi "cloud.google.com/go/bigquery" bigqueryapi "cloud.google.com/go/bigquery"
@@ -28,7 +27,6 @@ import (
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery" bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon" bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
bigqueryrestapi "google.golang.org/api/bigquery/v2" bigqueryrestapi "google.golang.org/api/bigquery/v2"
) )
@@ -156,21 +154,21 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke runs the contribution analysis. // Invoke runs the contribution analysis.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
inputData, ok := paramsMap["input_data"].(string) inputData, ok := paramsMap["input_data"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast input_data parameter %s", paramsMap["input_data"]), nil) return nil, fmt.Errorf("unable to cast input_data parameter %s", paramsMap["input_data"])
} }
bqClient, restService, err := source.RetrieveClientAndService(accessToken) bqClient, restService, err := source.RetrieveClientAndService(accessToken)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to retrieve BigQuery client", http.StatusInternalServerError, err) return nil, err
} }
modelID := fmt.Sprintf("contribution_analysis_model_%s", strings.ReplaceAll(uuid.New().String(), "-", "")) modelID := fmt.Sprintf("contribution_analysis_model_%s", strings.ReplaceAll(uuid.New().String(), "-", ""))
@@ -188,7 +186,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
options = append(options, fmt.Sprintf("DIMENSION_ID_COLS = [%s]", strings.Join(strCols, ", "))) options = append(options, fmt.Sprintf("DIMENSION_ID_COLS = [%s]", strings.Join(strCols, ", ")))
} else { } else {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast dimension_id_cols parameter %s", paramsMap["dimension_id_cols"]), nil) return nil, fmt.Errorf("unable to cast dimension_id_cols parameter %s", paramsMap["dimension_id_cols"])
} }
} }
if val, ok := paramsMap["top_k_insights_by_apriori_support"]; ok { if val, ok := paramsMap["top_k_insights_by_apriori_support"]; ok {
@@ -197,7 +195,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if val, ok := paramsMap["pruning_method"].(string); ok { if val, ok := paramsMap["pruning_method"].(string); ok {
upperVal := strings.ToUpper(val) upperVal := strings.ToUpper(val)
if upperVal != "NO_PRUNING" && upperVal != "PRUNE_REDUNDANT_INSIGHTS" { if upperVal != "NO_PRUNING" && upperVal != "PRUNE_REDUNDANT_INSIGHTS" {
return nil, util.NewAgentError(fmt.Sprintf("invalid pruning_method: %s", val), nil) return nil, fmt.Errorf("invalid pruning_method: %s", val)
} }
options = append(options, fmt.Sprintf("PRUNING_METHOD = '%s'", upperVal)) options = append(options, fmt.Sprintf("PRUNING_METHOD = '%s'", upperVal))
} }
@@ -209,7 +207,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var connProps []*bigqueryapi.ConnectionProperty var connProps []*bigqueryapi.ConnectionProperty
session, err := source.BigQuerySession()(ctx) session, err := source.BigQuerySession()(ctx)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to get BigQuery session", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to get BigQuery session: %w", err)
} }
if session != nil { if session != nil {
connProps = []*bigqueryapi.ConnectionProperty{ connProps = []*bigqueryapi.ConnectionProperty{
@@ -218,22 +216,22 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
dryRunJob, err := bqutil.DryRunQuery(ctx, restService, source.BigQueryClient().Project(), source.BigQueryClient().Location, inputData, nil, connProps) dryRunJob, err := bqutil.DryRunQuery(ctx, restService, source.BigQueryClient().Project(), source.BigQueryClient().Location, inputData, nil, connProps)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("query validation failed: %w", err)
} }
statementType := dryRunJob.Statistics.Query.StatementType statementType := dryRunJob.Statistics.Query.StatementType
if statementType != "SELECT" { if statementType != "SELECT" {
return nil, util.NewAgentError(fmt.Sprintf("the 'input_data' parameter only supports a table ID or a SELECT query. The provided query has statement type '%s'", statementType), nil) return nil, fmt.Errorf("the 'input_data' parameter only supports a table ID or a SELECT query. The provided query has statement type '%s'", statementType)
} }
queryStats := dryRunJob.Statistics.Query queryStats := dryRunJob.Statistics.Query
if queryStats != nil { if queryStats != nil {
for _, tableRef := range queryStats.ReferencedTables { for _, tableRef := range queryStats.ReferencedTables {
if !source.IsDatasetAllowed(tableRef.ProjectId, tableRef.DatasetId) { if !source.IsDatasetAllowed(tableRef.ProjectId, tableRef.DatasetId) {
return nil, util.NewAgentError(fmt.Sprintf("query in input_data accesses dataset '%s.%s', which is not in the allowed list", tableRef.ProjectId, tableRef.DatasetId), nil) return nil, fmt.Errorf("query in input_data accesses dataset '%s.%s', which is not in the allowed list", tableRef.ProjectId, tableRef.DatasetId)
} }
} }
} else { } else {
return nil, util.NewAgentError("could not analyze query in input_data to validate against allowed datasets", nil) return nil, fmt.Errorf("could not analyze query in input_data to validate against allowed datasets")
} }
} }
inputDataSource = fmt.Sprintf("(%s)", inputData) inputDataSource = fmt.Sprintf("(%s)", inputData)
@@ -247,10 +245,10 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
case 2: // dataset.table case 2: // dataset.table
projectID, datasetID = source.BigQueryClient().Project(), parts[0] projectID, datasetID = source.BigQueryClient().Project(), parts[0]
default: default:
return nil, util.NewAgentError(fmt.Sprintf("invalid table ID format for 'input_data': %q. Expected 'dataset.table' or 'project.dataset.table'", inputData), nil) return nil, fmt.Errorf("invalid table ID format for 'input_data': %q. Expected 'dataset.table' or 'project.dataset.table'", inputData)
} }
if !source.IsDatasetAllowed(projectID, datasetID) { if !source.IsDatasetAllowed(projectID, datasetID) {
return nil, util.NewAgentError(fmt.Sprintf("access to dataset '%s.%s' (from table '%s') is not allowed", projectID, datasetID, inputData), nil) return nil, fmt.Errorf("access to dataset '%s.%s' (from table '%s') is not allowed", projectID, datasetID, inputData)
} }
} }
inputDataSource = fmt.Sprintf("SELECT * FROM `%s`", inputData) inputDataSource = fmt.Sprintf("SELECT * FROM `%s`", inputData)
@@ -270,7 +268,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Otherwise, a new session will be created by the first query. // Otherwise, a new session will be created by the first query.
session, err := source.BigQuerySession()(ctx) session, err := source.BigQuerySession()(ctx)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to get BigQuery session", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to get BigQuery session: %w", err)
} }
if session != nil { if session != nil {
@@ -283,15 +281,15 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
createModelJob, err := createModelQuery.Run(ctx) createModelJob, err := createModelQuery.Run(ctx)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("failed to start create model job: %w", err)
} }
status, err := createModelJob.Wait(ctx) status, err := createModelJob.Wait(ctx)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("failed to wait for create model job: %w", err)
} }
if err := status.Err(); err != nil { if err := status.Err(); err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("create model job failed: %w", err)
} }
// Determine the session ID to use for subsequent queries. // Determine the session ID to use for subsequent queries.
@@ -302,17 +300,12 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} else if status.Statistics != nil && status.Statistics.SessionInfo != nil { } else if status.Statistics != nil && status.Statistics.SessionInfo != nil {
sessionID = status.Statistics.SessionInfo.SessionID sessionID = status.Statistics.SessionInfo.SessionID
} else { } else {
return nil, util.NewClientServerError("failed to get or create a BigQuery session ID", http.StatusInternalServerError, nil) return nil, fmt.Errorf("failed to get or create a BigQuery session ID")
} }
getInsightsSQL := fmt.Sprintf("SELECT * FROM ML.GET_INSIGHTS(MODEL %s)", modelID) getInsightsSQL := fmt.Sprintf("SELECT * FROM ML.GET_INSIGHTS(MODEL %s)", modelID)
connProps := []*bigqueryapi.ConnectionProperty{{Key: "session_id", Value: sessionID}} connProps := []*bigqueryapi.ConnectionProperty{{Key: "session_id", Value: sessionID}}
return source.RunSQL(ctx, bqClient, getInsightsSQL, "SELECT", nil, connProps)
resp, err := source.RunSQL(ctx, bqClient, getInsightsSQL, "SELECT", nil, connProps)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -172,10 +172,10 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
var tokenStr string var tokenStr string
@@ -184,26 +184,26 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
// Use client-side access token // Use client-side access token
if accessToken == "" { if accessToken == "" {
return nil, util.NewClientServerError("tool is configured for client OAuth but no token was provided in the request header", http.StatusUnauthorized, nil) return nil, fmt.Errorf("tool is configured for client OAuth but no token was provided in the request header: %w", util.ErrUnauthorized)
} }
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} else { } else {
// Get a token source for the Gemini Data Analytics API. // Get a token source for the Gemini Data Analytics API.
tokenSource, err := source.BigQueryTokenSourceWithScope(ctx, nil) tokenSource, err := source.BigQueryTokenSourceWithScope(ctx, nil)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to get token source", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to get token source: %w", err)
} }
// Use cloud-platform token source for Gemini Data Analytics API // Use cloud-platform token source for Gemini Data Analytics API
if tokenSource == nil { if tokenSource == nil {
return nil, util.NewClientServerError("cloud-platform token source is missing", http.StatusInternalServerError, nil) return nil, fmt.Errorf("cloud-platform token source is missing")
} }
token, err := tokenSource.Token() token, err := tokenSource.Token()
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to get token from cloud-platform token source", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to get token from cloud-platform token source: %w", err)
} }
tokenStr = token.AccessToken tokenStr = token.AccessToken
} }
@@ -218,14 +218,14 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var tableRefs []BQTableReference var tableRefs []BQTableReference
if tableRefsJSON != "" { if tableRefsJSON != "" {
if err := json.Unmarshal([]byte(tableRefsJSON), &tableRefs); err != nil { if err := json.Unmarshal([]byte(tableRefsJSON), &tableRefs); err != nil {
return nil, util.NewAgentError("failed to parse 'table_references' JSON string", err) return nil, fmt.Errorf("failed to parse 'table_references' JSON string: %w", err)
} }
} }
if len(source.BigQueryAllowedDatasets()) > 0 { if len(source.BigQueryAllowedDatasets()) > 0 {
for _, tableRef := range tableRefs { for _, tableRef := range tableRefs {
if !source.IsDatasetAllowed(tableRef.ProjectID, tableRef.DatasetID) { if !source.IsDatasetAllowed(tableRef.ProjectID, tableRef.DatasetID) {
return nil, util.NewAgentError(fmt.Sprintf("access to dataset '%s.%s' (from table '%s') is not allowed", tableRef.ProjectID, tableRef.DatasetID, tableRef.TableID), nil) return nil, fmt.Errorf("access to dataset '%s.%s' (from table '%s') is not allowed", tableRef.ProjectID, tableRef.DatasetID, tableRef.TableID)
} }
} }
} }
@@ -258,8 +258,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Call the streaming API // Call the streaming API
response, err := getStream(caURL, payload, headers, source.GetMaxQueryResultRows()) response, err := getStream(caURL, payload, headers, source.GetMaxQueryResultRows())
if err != nil { if err != nil {
// getStream wraps network errors or non-200 responses return nil, fmt.Errorf("failed to get response from conversational analytics API: %w", err)
return nil, util.NewClientServerError("failed to get response from conversational analytics API", http.StatusInternalServerError, err)
} }
return response, nil return response, nil

View File

@@ -18,7 +18,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"strings" "strings"
bigqueryapi "cloud.google.com/go/bigquery" bigqueryapi "cloud.google.com/go/bigquery"
@@ -153,25 +152,25 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
sql, ok := paramsMap["sql"].(string) sql, ok := paramsMap["sql"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast sql parameter %s", paramsMap["sql"]), nil) return nil, fmt.Errorf("unable to cast sql parameter %s", paramsMap["sql"])
} }
dryRun, ok := paramsMap["dry_run"].(bool) dryRun, ok := paramsMap["dry_run"].(bool)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast dry_run parameter %s", paramsMap["dry_run"]), nil) return nil, fmt.Errorf("unable to cast dry_run parameter %s", paramsMap["dry_run"])
} }
bqClient, restService, err := source.RetrieveClientAndService(accessToken) bqClient, restService, err := source.RetrieveClientAndService(accessToken)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to retrieve BigQuery client", http.StatusInternalServerError, err) return nil, err
} }
var connProps []*bigqueryapi.ConnectionProperty var connProps []*bigqueryapi.ConnectionProperty
@@ -179,7 +178,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if source.BigQueryWriteMode() == bigqueryds.WriteModeProtected { if source.BigQueryWriteMode() == bigqueryds.WriteModeProtected {
session, err = source.BigQuerySession()(ctx) session, err = source.BigQuerySession()(ctx)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to get BigQuery session for protected mode", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to get BigQuery session for protected mode: %w", err)
} }
connProps = []*bigqueryapi.ConnectionProperty{ connProps = []*bigqueryapi.ConnectionProperty{
{Key: "session_id", Value: session.ID}, {Key: "session_id", Value: session.ID},
@@ -188,7 +187,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
dryRunJob, err := bqutil.DryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, sql, nil, connProps) dryRunJob, err := bqutil.DryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, sql, nil, connProps)
if err != nil { if err != nil {
return nil, util.NewClientServerError("query validation failed", http.StatusInternalServerError, err) return nil, fmt.Errorf("query validation failed: %w", err)
} }
statementType := dryRunJob.Statistics.Query.StatementType statementType := dryRunJob.Statistics.Query.StatementType
@@ -196,13 +195,13 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
switch source.BigQueryWriteMode() { switch source.BigQueryWriteMode() {
case bigqueryds.WriteModeBlocked: case bigqueryds.WriteModeBlocked:
if statementType != "SELECT" { if statementType != "SELECT" {
return nil, util.NewAgentError("write mode is 'blocked', only SELECT statements are allowed", nil) return nil, fmt.Errorf("write mode is 'blocked', only SELECT statements are allowed")
} }
case bigqueryds.WriteModeProtected: case bigqueryds.WriteModeProtected:
if dryRunJob.Configuration != nil && dryRunJob.Configuration.Query != nil { if dryRunJob.Configuration != nil && dryRunJob.Configuration.Query != nil {
if dest := dryRunJob.Configuration.Query.DestinationTable; dest != nil && dest.DatasetId != session.DatasetID { if dest := dryRunJob.Configuration.Query.DestinationTable; dest != nil && dest.DatasetId != session.DatasetID {
return nil, util.NewAgentError(fmt.Sprintf("protected write mode only supports SELECT statements, or write operations in the anonymous "+ return nil, fmt.Errorf("protected write mode only supports SELECT statements, or write operations in the anonymous "+
"dataset of a BigQuery session, but destination was %q", dest.DatasetId), nil) "dataset of a BigQuery session, but destination was %q", dest.DatasetId)
} }
} }
} }
@@ -210,11 +209,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if len(source.BigQueryAllowedDatasets()) > 0 { if len(source.BigQueryAllowedDatasets()) > 0 {
switch statementType { switch statementType {
case "CREATE_SCHEMA", "DROP_SCHEMA", "ALTER_SCHEMA": case "CREATE_SCHEMA", "DROP_SCHEMA", "ALTER_SCHEMA":
return nil, util.NewAgentError(fmt.Sprintf("dataset-level operations like '%s' are not allowed when dataset restrictions are in place", statementType), nil) return nil, fmt.Errorf("dataset-level operations like '%s' are not allowed when dataset restrictions are in place", statementType)
case "CREATE_FUNCTION", "CREATE_TABLE_FUNCTION", "CREATE_PROCEDURE": case "CREATE_FUNCTION", "CREATE_TABLE_FUNCTION", "CREATE_PROCEDURE":
return nil, util.NewAgentError(fmt.Sprintf("creating stored routines ('%s') is not allowed when dataset restrictions are in place, as their contents cannot be safely analyzed", statementType), nil) return nil, fmt.Errorf("creating stored routines ('%s') is not allowed when dataset restrictions are in place, as their contents cannot be safely analyzed", statementType)
case "CALL": case "CALL":
return nil, util.NewAgentError(fmt.Sprintf("calling stored procedures ('%s') is not allowed when dataset restrictions are in place, as their contents cannot be safely analyzed", statementType), nil) return nil, fmt.Errorf("calling stored procedures ('%s') is not allowed when dataset restrictions are in place, as their contents cannot be safely analyzed", statementType)
} }
// Use a map to avoid duplicate table names. // Use a map to avoid duplicate table names.
@@ -245,7 +244,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
parsedTables, parseErr := bqutil.TableParser(sql, source.BigQueryClient().Project()) parsedTables, parseErr := bqutil.TableParser(sql, source.BigQueryClient().Project())
if parseErr != nil { if parseErr != nil {
// If parsing fails (e.g., EXECUTE IMMEDIATE), we cannot guarantee safety, so we must fail. // If parsing fails (e.g., EXECUTE IMMEDIATE), we cannot guarantee safety, so we must fail.
return nil, util.NewAgentError("could not parse tables from query to validate against allowed datasets", parseErr) return nil, fmt.Errorf("could not parse tables from query to validate against allowed datasets: %w", parseErr)
} }
tableNames = parsedTables tableNames = parsedTables
} }
@@ -255,7 +254,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if len(parts) == 3 { if len(parts) == 3 {
projectID, datasetID := parts[0], parts[1] projectID, datasetID := parts[0], parts[1]
if !source.IsDatasetAllowed(projectID, datasetID) { if !source.IsDatasetAllowed(projectID, datasetID) {
return nil, util.NewAgentError(fmt.Sprintf("query accesses dataset '%s.%s', which is not in the allowed list", projectID, datasetID), nil) return nil, fmt.Errorf("query accesses dataset '%s.%s', which is not in the allowed list", projectID, datasetID)
} }
} }
} }
@@ -265,7 +264,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if dryRunJob != nil { if dryRunJob != nil {
jobJSON, err := json.MarshalIndent(dryRunJob, "", " ") jobJSON, err := json.MarshalIndent(dryRunJob, "", " ")
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to marshal dry run job to JSON", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to marshal dry run job to JSON: %w", err)
} }
return string(jobJSON), nil return string(jobJSON), nil
} }
@@ -276,14 +275,10 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Log the query executed for debugging. // Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx) logger, err := util.LoggerFromContext(ctx)
if err != nil { if err != nil {
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err) return nil, fmt.Errorf("error getting logger: %s", err)
} }
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sql)) logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sql))
resp, err := source.RunSQL(ctx, bqClient, sql, statementType, nil, connProps) return source.RunSQL(ctx, bqClient, sql, statementType, nil, connProps)
if err != nil {
return nil, util.NewClientServerError("error running sql", http.StatusInternalServerError, err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,7 +17,6 @@ package bigqueryforecast
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
bigqueryapi "cloud.google.com/go/bigquery" bigqueryapi "cloud.google.com/go/bigquery"
@@ -134,34 +133,34 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
historyData, ok := paramsMap["history_data"].(string) historyData, ok := paramsMap["history_data"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast history_data parameter %v", paramsMap["history_data"]), nil) return nil, fmt.Errorf("unable to cast history_data parameter %v", paramsMap["history_data"])
} }
timestampCol, ok := paramsMap["timestamp_col"].(string) timestampCol, ok := paramsMap["timestamp_col"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast timestamp_col parameter %v", paramsMap["timestamp_col"]), nil) return nil, fmt.Errorf("unable to cast timestamp_col parameter %v", paramsMap["timestamp_col"])
} }
dataCol, ok := paramsMap["data_col"].(string) dataCol, ok := paramsMap["data_col"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast data_col parameter %v", paramsMap["data_col"]), nil) return nil, fmt.Errorf("unable to cast data_col parameter %v", paramsMap["data_col"])
} }
idColsRaw, ok := paramsMap["id_cols"].([]any) idColsRaw, ok := paramsMap["id_cols"].([]any)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast id_cols parameter %v", paramsMap["id_cols"]), nil) return nil, fmt.Errorf("unable to cast id_cols parameter %v", paramsMap["id_cols"])
} }
var idCols []string var idCols []string
for _, v := range idColsRaw { for _, v := range idColsRaw {
s, ok := v.(string) s, ok := v.(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("id_cols contains non-string value: %v", v), nil) return nil, fmt.Errorf("id_cols contains non-string value: %v", v)
} }
idCols = append(idCols, s) idCols = append(idCols, s)
} }
@@ -170,13 +169,13 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if h, ok := paramsMap["horizon"].(float64); ok { if h, ok := paramsMap["horizon"].(float64); ok {
horizon = int(h) horizon = int(h)
} else { } else {
return nil, util.NewAgentError(fmt.Sprintf("unable to cast horizon parameter %v", paramsMap["horizon"]), nil) return nil, fmt.Errorf("unable to cast horizon parameter %v", paramsMap["horizon"])
} }
} }
bqClient, restService, err := source.RetrieveClientAndService(accessToken) bqClient, restService, err := source.RetrieveClientAndService(accessToken)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to retrieve BigQuery client", http.StatusInternalServerError, err) return nil, err
} }
var historyDataSource string var historyDataSource string
@@ -186,7 +185,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var connProps []*bigqueryapi.ConnectionProperty var connProps []*bigqueryapi.ConnectionProperty
session, err := source.BigQuerySession()(ctx) session, err := source.BigQuerySession()(ctx)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to get BigQuery session", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to get BigQuery session: %w", err)
} }
if session != nil { if session != nil {
connProps = []*bigqueryapi.ConnectionProperty{ connProps = []*bigqueryapi.ConnectionProperty{
@@ -195,22 +194,22 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
dryRunJob, err := bqutil.DryRunQuery(ctx, restService, source.BigQueryClient().Project(), source.BigQueryClient().Location, historyData, nil, connProps) dryRunJob, err := bqutil.DryRunQuery(ctx, restService, source.BigQueryClient().Project(), source.BigQueryClient().Location, historyData, nil, connProps)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("query validation failed: %w", err)
} }
statementType := dryRunJob.Statistics.Query.StatementType statementType := dryRunJob.Statistics.Query.StatementType
if statementType != "SELECT" { if statementType != "SELECT" {
return nil, util.NewAgentError(fmt.Sprintf("the 'history_data' parameter only supports a table ID or a SELECT query. The provided query has statement type '%s'", statementType), nil) return nil, fmt.Errorf("the 'history_data' parameter only supports a table ID or a SELECT query. The provided query has statement type '%s'", statementType)
} }
queryStats := dryRunJob.Statistics.Query queryStats := dryRunJob.Statistics.Query
if queryStats != nil { if queryStats != nil {
for _, tableRef := range queryStats.ReferencedTables { for _, tableRef := range queryStats.ReferencedTables {
if !source.IsDatasetAllowed(tableRef.ProjectId, tableRef.DatasetId) { if !source.IsDatasetAllowed(tableRef.ProjectId, tableRef.DatasetId) {
return nil, util.NewAgentError(fmt.Sprintf("query in history_data accesses dataset '%s.%s', which is not in the allowed list", tableRef.ProjectId, tableRef.DatasetId), nil) return nil, fmt.Errorf("query in history_data accesses dataset '%s.%s', which is not in the allowed list", tableRef.ProjectId, tableRef.DatasetId)
} }
} }
} else { } else {
return nil, util.NewAgentError("could not analyze query in history_data to validate against allowed datasets", nil) return nil, fmt.Errorf("could not analyze query in history_data to validate against allowed datasets")
} }
} }
historyDataSource = fmt.Sprintf("(%s)", historyData) historyDataSource = fmt.Sprintf("(%s)", historyData)
@@ -227,11 +226,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
projectID = source.BigQueryClient().Project() projectID = source.BigQueryClient().Project()
datasetID = parts[0] datasetID = parts[0]
default: default:
return nil, util.NewAgentError(fmt.Sprintf("invalid table ID format for 'history_data': %q. Expected 'dataset.table' or 'project.dataset.table'", historyData), nil) return nil, fmt.Errorf("invalid table ID format for 'history_data': %q. Expected 'dataset.table' or 'project.dataset.table'", historyData)
} }
if !source.IsDatasetAllowed(projectID, datasetID) { if !source.IsDatasetAllowed(projectID, datasetID) {
return nil, util.NewAgentError(fmt.Sprintf("access to dataset '%s.%s' (from table '%s') is not allowed", projectID, datasetID, historyData), nil) return nil, fmt.Errorf("access to dataset '%s.%s' (from table '%s') is not allowed", projectID, datasetID, historyData)
} }
} }
historyDataSource = fmt.Sprintf("TABLE `%s`", historyData) historyDataSource = fmt.Sprintf("TABLE `%s`", historyData)
@@ -244,15 +243,15 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
sql := fmt.Sprintf(`SELECT * sql := fmt.Sprintf(`SELECT *
FROM AI.FORECAST( FROM AI.FORECAST(
%s, %s,
data_col => '%s', data_col => '%s',
timestamp_col => '%s', timestamp_col => '%s',
horizon => %d%s)`, horizon => %d%s)`,
historyDataSource, dataCol, timestampCol, horizon, idColsArg) historyDataSource, dataCol, timestampCol, horizon, idColsArg)
session, err := source.BigQuerySession()(ctx) session, err := source.BigQuerySession()(ctx)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to get BigQuery session", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to get BigQuery session: %w", err)
} }
var connProps []*bigqueryapi.ConnectionProperty var connProps []*bigqueryapi.ConnectionProperty
if session != nil { if session != nil {
@@ -265,15 +264,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Log the query executed for debugging. // Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx) logger, err := util.LoggerFromContext(ctx)
if err != nil { if err != nil {
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err) return nil, fmt.Errorf("error getting logger: %s", err)
} }
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sql)) logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sql))
resp, err := source.RunSQL(ctx, bqClient, sql, "SELECT", nil, connProps) return source.RunSQL(ctx, bqClient, sql, "SELECT", nil, connProps)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,7 +17,6 @@ package bigquerygetdatasetinfo
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
bigqueryapi "cloud.google.com/go/bigquery" bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
@@ -25,7 +24,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon" bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
bigqueryrestapi "google.golang.org/api/bigquery/v2" bigqueryrestapi "google.golang.org/api/bigquery/v2"
) )
@@ -122,38 +120,38 @@ type Tool struct {
func (t Tool) ToConfig() tools.ToolConfig { func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
projectId, ok := mapParams[projectKey].(string) projectId, ok := mapParams[projectKey].(string)
if !ok { if !ok {
// Updated: Use fmt.Sprintf for formatting, pass nil as cause return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", projectKey)
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", projectKey), nil)
} }
datasetId, ok := mapParams[datasetKey].(string) datasetId, ok := mapParams[datasetKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", datasetKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", datasetKey)
} }
bqClient, _, err := source.RetrieveClientAndService(accessToken) bqClient, _, err := source.RetrieveClientAndService(accessToken)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to retrieve BigQuery client", http.StatusInternalServerError, err) return nil, err
} }
if !source.IsDatasetAllowed(projectId, datasetId) { if !source.IsDatasetAllowed(projectId, datasetId) {
return nil, util.NewClientServerError(fmt.Sprintf("access denied to dataset '%s' because it is not in the configured list of allowed datasets for project '%s'", datasetId, projectId), http.StatusInternalServerError, nil) return nil, fmt.Errorf("access denied to dataset '%s' because it is not in the configured list of allowed datasets for project '%s'", datasetId, projectId)
} }
dsHandle := bqClient.DatasetInProject(projectId, datasetId) dsHandle := bqClient.DatasetInProject(projectId, datasetId)
metadata, err := dsHandle.Metadata(ctx) metadata, err := dsHandle.Metadata(ctx)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("failed to get metadata for dataset %s (in project %s): %w", datasetId, projectId, err)
} }
return metadata, nil return metadata, nil

View File

@@ -17,7 +17,6 @@ package bigquerygettableinfo
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
bigqueryapi "cloud.google.com/go/bigquery" bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
@@ -25,7 +24,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon" bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
bigqueryrestapi "google.golang.org/api/bigquery/v2" bigqueryrestapi "google.golang.org/api/bigquery/v2"
) )
@@ -127,35 +125,35 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
projectId, ok := mapParams[projectKey].(string) projectId, ok := mapParams[projectKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", projectKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", projectKey)
} }
datasetId, ok := mapParams[datasetKey].(string) datasetId, ok := mapParams[datasetKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", datasetKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", datasetKey)
} }
tableId, ok := mapParams[tableKey].(string) tableId, ok := mapParams[tableKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", tableKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", tableKey)
} }
if !source.IsDatasetAllowed(projectId, datasetId) { if !source.IsDatasetAllowed(projectId, datasetId) {
return nil, util.NewClientServerError(fmt.Sprintf("access denied to dataset '%s' because it is not in the configured list of allowed datasets for project '%s'", datasetId, projectId), http.StatusInternalServerError, nil) return nil, fmt.Errorf("access denied to dataset '%s' because it is not in the configured list of allowed datasets for project '%s'", datasetId, projectId)
} }
bqClient, _, err := source.RetrieveClientAndService(accessToken) bqClient, _, err := source.RetrieveClientAndService(accessToken)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to retrieve BigQuery client", http.StatusInternalServerError, err) return nil, err
} }
dsHandle := bqClient.DatasetInProject(projectId, datasetId) dsHandle := bqClient.DatasetInProject(projectId, datasetId)
@@ -163,7 +161,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
metadata, err := tableHandle.Metadata(ctx) metadata, err := tableHandle.Metadata(ctx)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("failed to get metadata for table %s.%s.%s: %w", projectId, datasetId, tableId, err)
} }
return metadata, nil return metadata, nil

View File

@@ -17,14 +17,12 @@ package bigquerylistdatasetids
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
bigqueryapi "cloud.google.com/go/bigquery" bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
bigqueryrestapi "google.golang.org/api/bigquery/v2" bigqueryrestapi "google.golang.org/api/bigquery/v2"
"google.golang.org/api/iterator" "google.golang.org/api/iterator"
@@ -122,10 +120,10 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
if len(source.BigQueryAllowedDatasets()) > 0 { if len(source.BigQueryAllowedDatasets()) > 0 {
@@ -134,12 +132,12 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
mapParams := params.AsMap() mapParams := params.AsMap()
projectId, ok := mapParams[projectKey].(string) projectId, ok := mapParams[projectKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", projectKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", projectKey)
} }
bqClient, _, err := source.RetrieveClientAndService(accessToken) bqClient, _, err := source.RetrieveClientAndService(accessToken)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to retrieve BigQuery client", http.StatusInternalServerError, err) return nil, err
} }
datasetIterator := bqClient.Datasets(ctx) datasetIterator := bqClient.Datasets(ctx)
datasetIterator.ProjectID = projectId datasetIterator.ProjectID = projectId
@@ -151,7 +149,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
break break
} }
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("unable to iterate through datasets: %w", err)
} }
// Remove leading and trailing quotes // Remove leading and trailing quotes

View File

@@ -17,7 +17,6 @@ package bigquerylisttableids
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
bigqueryapi "cloud.google.com/go/bigquery" bigqueryapi "cloud.google.com/go/bigquery"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
@@ -25,7 +24,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon" bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
bigqueryrestapi "google.golang.org/api/bigquery/v2" bigqueryrestapi "google.golang.org/api/bigquery/v2"
"google.golang.org/api/iterator" "google.golang.org/api/iterator"
@@ -125,30 +123,31 @@ type Tool struct {
func (t Tool) ToConfig() tools.ToolConfig { func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
projectId, ok := mapParams[projectKey].(string) projectId, ok := mapParams[projectKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", projectKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", projectKey)
} }
datasetId, ok := mapParams[datasetKey].(string) datasetId, ok := mapParams[datasetKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", datasetKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", datasetKey)
} }
if !source.IsDatasetAllowed(projectId, datasetId) { if !source.IsDatasetAllowed(projectId, datasetId) {
return nil, util.NewClientServerError(fmt.Sprintf("access denied to dataset '%s' because it is not in the configured list of allowed datasets for project '%s'", datasetId, projectId), http.StatusInternalServerError, nil) return nil, fmt.Errorf("access denied to dataset '%s' because it is not in the configured list of allowed datasets for project '%s'", datasetId, projectId)
} }
bqClient, _, err := source.RetrieveClientAndService(accessToken) bqClient, _, err := source.RetrieveClientAndService(accessToken)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to retrieve BigQuery client", http.StatusInternalServerError, err) return nil, err
} }
dsHandle := bqClient.DatasetInProject(projectId, datasetId) dsHandle := bqClient.DatasetInProject(projectId, datasetId)
@@ -161,7 +160,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
break break
} }
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("failed to iterate through tables in dataset %s.%s: %w", projectId, datasetId, err)
} }
// Remove leading and trailing quotes // Remove leading and trailing quotes

View File

@@ -17,7 +17,6 @@ package bigquerysearchcatalog
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
dataplexapi "cloud.google.com/go/dataplex/apiv1" dataplexapi "cloud.google.com/go/dataplex/apiv1"
@@ -27,7 +26,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery" bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/iterator" "google.golang.org/api/iterator"
) )
@@ -188,31 +186,28 @@ func ExtractType(resourceString string) string {
return typeMap[resourceString[lastIndex+1:]] return typeMap[resourceString[lastIndex+1:]]
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
pageSize := int32(paramsMap["pageSize"].(int)) pageSize := int32(paramsMap["pageSize"].(int))
prompt, _ := paramsMap["prompt"].(string) prompt, _ := paramsMap["prompt"].(string)
projectIdSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["projectIds"].([]any), "string") projectIdSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["projectIds"].([]any), "string")
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("can't convert projectIds to array of strings: %s", err), err) return nil, fmt.Errorf("can't convert projectIds to array of strings: %s", err)
} }
projectIds := projectIdSlice.([]string) projectIds := projectIdSlice.([]string)
datasetIdSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["datasetIds"].([]any), "string") datasetIdSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["datasetIds"].([]any), "string")
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("can't convert datasetIds to array of strings: %s", err), err) return nil, fmt.Errorf("can't convert datasetIds to array of strings: %s", err)
} }
datasetIds := datasetIdSlice.([]string) datasetIds := datasetIdSlice.([]string)
typesSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["types"].([]any), "string") typesSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["types"].([]any), "string")
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("can't convert types to array of strings: %s", err), err) return nil, fmt.Errorf("can't convert types to array of strings: %s", err)
} }
types := typesSlice.([]string) types := typesSlice.([]string)
@@ -228,17 +223,17 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err := accessToken.ParseBearerToken() tokenStr, err := accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
catalogClient, err = dataplexClientCreator(tokenStr) catalogClient, err = dataplexClientCreator(tokenStr)
if err != nil { if err != nil {
return nil, util.NewClientServerError("error creating client from OAuth access token", http.StatusInternalServerError, err) return nil, fmt.Errorf("error creating client from OAuth access token: %w", err)
} }
} }
it := catalogClient.SearchEntries(ctx, req) it := catalogClient.SearchEntries(ctx, req)
if it == nil { if it == nil {
return nil, util.NewClientServerError(fmt.Sprintf("failed to create search entries iterator for project %q", source.BigQueryProject()), http.StatusInternalServerError, nil) return nil, fmt.Errorf("failed to create search entries iterator for project %q", source.BigQueryProject())
} }
var results []Response var results []Response
@@ -248,7 +243,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
break break
} }
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) break
} }
entrySource := entry.DataplexEntry.GetEntrySource() entrySource := entry.DataplexEntry.GetEntrySource()
resp := Response{ resp := Response{

View File

@@ -17,7 +17,6 @@ package bigquerysql
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"reflect" "reflect"
"strings" "strings"
@@ -28,7 +27,6 @@ import (
bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery" bigqueryds "github.com/googleapis/genai-toolbox/internal/sources/bigquery"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon" bqutil "github.com/googleapis/genai-toolbox/internal/tools/bigquery/bigquerycommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
bigqueryrestapi "google.golang.org/api/bigquery/v2" bigqueryrestapi "google.golang.org/api/bigquery/v2"
) )
@@ -105,10 +103,11 @@ type Tool struct {
func (t Tool) ToConfig() tools.ToolConfig { func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
highLevelParams := make([]bigqueryapi.QueryParameter, 0, len(t.Parameters)) highLevelParams := make([]bigqueryapi.QueryParameter, 0, len(t.Parameters))
@@ -117,7 +116,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
paramsMap := params.AsMap() paramsMap := params.AsMap()
newStatement, err := parameters.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap) newStatement, err := parameters.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil { if err != nil {
return nil, util.NewAgentError("unable to extract template params", err) return nil, fmt.Errorf("unable to extract template params %w", err)
} }
for _, p := range t.Parameters { for _, p := range t.Parameters {
@@ -128,13 +127,13 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if arrayParam, ok := p.(*parameters.ArrayParameter); ok { if arrayParam, ok := p.(*parameters.ArrayParameter); ok {
arrayParamValue, ok := value.([]any) arrayParamValue, ok := value.([]any)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("unable to convert parameter `%s` to []any", name), nil) return nil, fmt.Errorf("unable to convert parameter `%s` to []any", name)
} }
itemType := arrayParam.GetItems().GetType() itemType := arrayParam.GetItems().GetType()
var err error var err error
value, err = parameters.ConvertAnySliceToTyped(arrayParamValue, itemType) value, err = parameters.ConvertAnySliceToTyped(arrayParamValue, itemType)
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("unable to convert parameter `%s` from []any to typed slice", name), err) return nil, fmt.Errorf("unable to convert parameter `%s` from []any to typed slice: %w", name, err)
} }
} }
@@ -162,7 +161,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
lowLevelParam.ParameterType.Type = "ARRAY" lowLevelParam.ParameterType.Type = "ARRAY"
itemType, err := bqutil.BQTypeStringFromToolType(arrayParam.GetItems().GetType()) itemType, err := bqutil.BQTypeStringFromToolType(arrayParam.GetItems().GetType())
if err != nil { if err != nil {
return nil, util.NewAgentError("unable to get BigQuery type from tool parameter type", err) return nil, err
} }
lowLevelParam.ParameterType.ArrayType = &bigqueryrestapi.QueryParameterType{Type: itemType} lowLevelParam.ParameterType.ArrayType = &bigqueryrestapi.QueryParameterType{Type: itemType}
@@ -179,7 +178,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Handle scalar types based on their defined type. // Handle scalar types based on their defined type.
bqType, err := bqutil.BQTypeStringFromToolType(p.GetType()) bqType, err := bqutil.BQTypeStringFromToolType(p.GetType())
if err != nil { if err != nil {
return nil, util.NewAgentError("unable to get BigQuery type from tool parameter type", err) return nil, err
} }
lowLevelParam.ParameterType.Type = bqType lowLevelParam.ParameterType.Type = bqType
lowLevelParam.ParameterValue.Value = fmt.Sprintf("%v", value) lowLevelParam.ParameterValue.Value = fmt.Sprintf("%v", value)
@@ -191,7 +190,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if source.BigQuerySession() != nil { if source.BigQuerySession() != nil {
session, err := source.BigQuerySession()(ctx) session, err := source.BigQuerySession()(ctx)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to get BigQuery session", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to get BigQuery session: %w", err)
} }
if session != nil { if session != nil {
// Add session ID to the connection properties for subsequent calls. // Add session ID to the connection properties for subsequent calls.
@@ -201,20 +200,17 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
bqClient, restService, err := source.RetrieveClientAndService(accessToken) bqClient, restService, err := source.RetrieveClientAndService(accessToken)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to retrieve BigQuery client", http.StatusInternalServerError, err) return nil, err
} }
dryRunJob, err := bqutil.DryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, newStatement, lowLevelParams, connProps) dryRunJob, err := bqutil.DryRunQuery(ctx, restService, bqClient.Project(), bqClient.Location, newStatement, lowLevelParams, connProps)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("query validation failed: %w", err)
} }
statementType := dryRunJob.Statistics.Query.StatementType statementType := dryRunJob.Statistics.Query.StatementType
resp, err := source.RunSQL(ctx, bqClient, newStatement, statementType, highLevelParams, connProps)
if err != nil { return source.RunSQL(ctx, bqClient, newStatement, statementType, highLevelParams, connProps)
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

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

View File

@@ -18,13 +18,11 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -121,16 +119,17 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { // Invoke executes the tool logic
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
query, ok := paramsMap["query"].(string) query, ok := paramsMap["query"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("query parameter not found or not a string", nil) return nil, fmt.Errorf("query parameter not found or not a string")
} }
// Parse the access token if provided // Parse the access token if provided
@@ -139,7 +138,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var err error var err error
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
@@ -155,14 +154,9 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
bodyBytes, err := json.Marshal(payload) bodyBytes, err := json.Marshal(payload)
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to marshal request payload", http.StatusInternalServerError, err) return nil, fmt.Errorf("failed to marshal request payload: %w", err)
} }
return source.RunQuery(ctx, tokenStr, bodyBytes)
resp, err := source.RunQuery(ctx, tokenStr, bodyBytes)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package fhirfetchpage
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -95,31 +93,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
url, ok := params.AsMap()[pageURLKey].(string) url, ok := params.AsMap()[pageURLKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", pageURLKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", pageURLKey)
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
return source.FHIRFetchPage(ctx, url, tokenStr)
resp, err := source.FHIRFetchPage(ctx, url, tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,7 +17,6 @@ package fhirpatienteverything
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
@@ -25,7 +24,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
@@ -118,27 +116,26 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores())
if err != nil { if err != nil {
// ValidateAndFetchStoreID usually returns input validation errors return nil, err
return nil, util.NewAgentError("failed to validate store ID", err)
} }
patientID, ok := params.AsMap()[patientIDKey].(string) patientID, ok := params.AsMap()[patientIDKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", patientIDKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", patientIDKey)
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
@@ -146,11 +143,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if val, ok := params.AsMap()[typeFilterKey]; ok { if val, ok := params.AsMap()[typeFilterKey]; ok {
types, ok := val.([]any) types, ok := val.([]any)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string array", typeFilterKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a string array", typeFilterKey)
} }
typeFilterSlice, err := parameters.ConvertAnySliceToTyped(types, "string") typeFilterSlice, err := parameters.ConvertAnySliceToTyped(types, "string")
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("can't convert '%s' to array of strings: %s", typeFilterKey, err), err) return nil, fmt.Errorf("can't convert '%s' to array of strings: %s", typeFilterKey, err)
} }
if len(typeFilterSlice.([]string)) != 0 { if len(typeFilterSlice.([]string)) != 0 {
opts = append(opts, googleapi.QueryParameter("_type", strings.Join(typeFilterSlice.([]string), ","))) opts = append(opts, googleapi.QueryParameter("_type", strings.Join(typeFilterSlice.([]string), ",")))
@@ -159,18 +156,13 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if since, ok := params.AsMap()[sinceFilterKey]; ok { if since, ok := params.AsMap()[sinceFilterKey]; ok {
sinceStr, ok := since.(string) sinceStr, ok := since.(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string", sinceFilterKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a string", sinceFilterKey)
} }
if sinceStr != "" { if sinceStr != "" {
opts = append(opts, googleapi.QueryParameter("_since", sinceStr)) opts = append(opts, googleapi.QueryParameter("_since", sinceStr))
} }
} }
return source.FHIRPatientEverything(storeID, patientID, tokenStr, opts)
resp, err := source.FHIRPatientEverything(storeID, patientID, tokenStr, opts)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,7 +17,6 @@ package fhirpatientsearch
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
@@ -25,7 +24,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
@@ -152,22 +150,22 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
@@ -181,14 +179,14 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var ok bool var ok bool
summary, ok = v.(bool) summary, ok = v.(bool)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a boolean", summaryKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a boolean", summaryKey)
} }
continue continue
} }
val, ok := v.(string) val, ok := v.(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid parameter '%s'; expected a string", k), nil) return nil, fmt.Errorf("invalid parameter '%s'; expected a string", k)
} }
if val == "" { if val == "" {
continue continue
@@ -207,7 +205,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
parts := strings.Split(val, "/") parts := strings.Split(val, "/")
if len(parts) != 2 { if len(parts) != 2 {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' format; expected YYYY-MM-DD/YYYY-MM-DD", k), nil) return nil, fmt.Errorf("invalid '%s' format; expected YYYY-MM-DD/YYYY-MM-DD", k)
} }
var values []string var values []string
if parts[0] != "" { if parts[0] != "" {
@@ -231,17 +229,13 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
case familyNameKey: case familyNameKey:
opts = append(opts, googleapi.QueryParameter("family", val)) opts = append(opts, googleapi.QueryParameter("family", val))
default: default:
return nil, util.NewAgentError(fmt.Sprintf("unexpected parameter key %q", k), nil) return nil, fmt.Errorf("unexpected parameter key %q", k)
} }
} }
if summary { if summary {
opts = append(opts, googleapi.QueryParameter("_summary", "text")) opts = append(opts, googleapi.QueryParameter("_summary", "text"))
} }
resp, err := source.FHIRPatientSearch(storeID, tokenStr, opts) return source.FHIRPatientSearch(storeID, tokenStr, opts)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package gethealthcaredataset
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/healthcare/v1" "google.golang.org/api/healthcare/v1"
) )
@@ -92,23 +90,19 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
resp, err := source.GetDataset(tokenStr) return source.GetDataset(tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package getdicomstore
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/healthcare/v1" "google.golang.org/api/healthcare/v1"
) )
@@ -109,27 +107,23 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
resp, err := source.GetDICOMStore(storeID, tokenStr) return source.GetDICOMStore(storeID, tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package getdicomstoremetrics
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/healthcare/v1" "google.golang.org/api/healthcare/v1"
) )
@@ -109,27 +107,23 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
resp, err := source.GetDICOMStoreMetrics(storeID, tokenStr) return source.GetDICOMStoreMetrics(storeID, tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package getfhirresource
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -114,36 +112,32 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
resType, ok := params.AsMap()[typeKey].(string) resType, ok := params.AsMap()[typeKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", typeKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", typeKey)
} }
resID, ok := params.AsMap()[idKey].(string) resID, ok := params.AsMap()[idKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected a string", idKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected a string", idKey)
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
resp, err := source.GetFHIRResource(storeID, resType, resID, tokenStr) return source.GetFHIRResource(storeID, resType, resID, tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package getfhirstore
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/healthcare/v1" "google.golang.org/api/healthcare/v1"
) )
@@ -109,27 +107,23 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
resp, err := source.GetFHIRStore(storeID, tokenStr) return source.GetFHIRStore(storeID, tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package getfhirstoremetrics
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/healthcare/v1" "google.golang.org/api/healthcare/v1"
) )
@@ -109,27 +107,23 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedFHIRStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
resp, err := source.GetFHIRStoreMetrics(storeID, tokenStr) return source.GetFHIRStoreMetrics(storeID, tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package listdicomstores
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/healthcare/v1" "google.golang.org/api/healthcare/v1"
) )
@@ -92,23 +90,19 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
resp, err := source.ListDICOMStores(tokenStr) return source.ListDICOMStores(tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package listfhirstores
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/healthcare/v1" "google.golang.org/api/healthcare/v1"
) )
@@ -92,23 +90,19 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
resp, err := source.ListFHIRStores(tokenStr) return source.ListFHIRStores(tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package retrieverendereddicominstance
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -119,44 +117,40 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
study, ok := params.AsMap()[studyInstanceUIDKey].(string) study, ok := params.AsMap()[studyInstanceUIDKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string", studyInstanceUIDKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a string", studyInstanceUIDKey)
} }
series, ok := params.AsMap()[seriesInstanceUIDKey].(string) series, ok := params.AsMap()[seriesInstanceUIDKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string", seriesInstanceUIDKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a string", seriesInstanceUIDKey)
} }
sop, ok := params.AsMap()[sopInstanceUIDKey].(string) sop, ok := params.AsMap()[sopInstanceUIDKey].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string", sopInstanceUIDKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a string", sopInstanceUIDKey)
} }
frame, ok := params.AsMap()[frameNumberKey].(int) frame, ok := params.AsMap()[frameNumberKey].(int)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected an integer", frameNumberKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected an integer", frameNumberKey)
} }
resp, err := source.RetrieveRenderedDICOMInstance(storeID, study, series, sop, frame, tokenStr) return source.RetrieveRenderedDICOMInstance(storeID, study, series, sop, frame, tokenStr)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,7 +17,6 @@ package searchdicominstances
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
@@ -25,7 +24,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
@@ -133,33 +131,33 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
opts, err := common.ParseDICOMSearchParameters(params, []string{sopInstanceUIDKey, patientNameKey, patientIDKey, accessionNumberKey, referringPhysicianNameKey, studyDateKey, modalityKey}) opts, err := common.ParseDICOMSearchParameters(params, []string{sopInstanceUIDKey, patientNameKey, patientIDKey, accessionNumberKey, referringPhysicianNameKey, studyDateKey, modalityKey})
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to parse DICOM search parameters", err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
dicomWebPath := "instances" dicomWebPath := "instances"
if studyInstanceUID, ok := paramsMap[studyInstanceUIDKey]; ok { if studyInstanceUID, ok := paramsMap[studyInstanceUIDKey]; ok {
id, ok := studyInstanceUID.(string) id, ok := studyInstanceUID.(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string", studyInstanceUIDKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a string", studyInstanceUIDKey)
} }
if id != "" { if id != "" {
dicomWebPath = fmt.Sprintf("studies/%s/instances", id) dicomWebPath = fmt.Sprintf("studies/%s/instances", id)
@@ -168,7 +166,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if seriesInstanceUID, ok := paramsMap[seriesInstanceUIDKey]; ok { if seriesInstanceUID, ok := paramsMap[seriesInstanceUIDKey]; ok {
id, ok := seriesInstanceUID.(string) id, ok := seriesInstanceUID.(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string", seriesInstanceUIDKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a string", seriesInstanceUIDKey)
} }
if id != "" { if id != "" {
if dicomWebPath != "instances" { if dicomWebPath != "instances" {
@@ -178,11 +176,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
} }
} }
resp, err := source.SearchDICOM(t.Type, storeID, dicomWebPath, tokenStr, opts) return source.SearchDICOM(t.Type, storeID, dicomWebPath, tokenStr, opts)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package searchdicomseries
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
@@ -130,44 +128,40 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
opts, err := common.ParseDICOMSearchParameters(params, []string{seriesInstanceUIDKey, patientNameKey, patientIDKey, accessionNumberKey, referringPhysicianNameKey, studyDateKey, modalityKey}) opts, err := common.ParseDICOMSearchParameters(params, []string{seriesInstanceUIDKey, patientNameKey, patientIDKey, accessionNumberKey, referringPhysicianNameKey, studyDateKey, modalityKey})
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to parse DICOM search parameters", err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
dicomWebPath := "series" dicomWebPath := "series"
if studyInstanceUID, ok := paramsMap[studyInstanceUIDKey]; ok { if studyInstanceUID, ok := paramsMap[studyInstanceUIDKey]; ok {
id, ok := studyInstanceUID.(string) id, ok := studyInstanceUID.(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string", studyInstanceUIDKey), nil) return nil, fmt.Errorf("invalid '%s' parameter; expected a string", studyInstanceUIDKey)
} }
if id != "" { if id != "" {
dicomWebPath = fmt.Sprintf("studies/%s/series", id) dicomWebPath = fmt.Sprintf("studies/%s/series", id)
} }
} }
resp, err := source.SearchDICOM(t.Type, storeID, dicomWebPath, tokenStr, opts) return source.SearchDICOM(t.Type, storeID, dicomWebPath, tokenStr, opts)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package searchdicomstudies
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common" "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/common"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/googleapi" "google.golang.org/api/googleapi"
) )
@@ -126,32 +124,28 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores()) storeID, err := common.ValidateAndFetchStoreID(params, source.AllowedDICOMStores())
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to validate store ID", err) return nil, err
} }
var tokenStr string var tokenStr string
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenStr, err = accessToken.ParseBearerToken() tokenStr, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("error parsing access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("error parsing access token: %w", err)
} }
} }
opts, err := common.ParseDICOMSearchParameters(params, []string{studyInstanceUIDKey, patientNameKey, patientIDKey, accessionNumberKey, referringPhysicianNameKey, studyDateKey}) opts, err := common.ParseDICOMSearchParameters(params, []string{studyInstanceUIDKey, patientNameKey, patientIDKey, accessionNumberKey, referringPhysicianNameKey, studyDateKey})
if err != nil { if err != nil {
return nil, util.NewAgentError("failed to parse DICOM search parameters", err) return nil, err
} }
dicomWebPath := "studies" dicomWebPath := "studies"
resp, err := source.SearchDICOM(t.Type, storeID, dicomWebPath, tokenStr, opts) return source.SearchDICOM(t.Type, storeID, dicomWebPath, tokenStr, opts)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -16,13 +16,11 @@ package cloudloggingadminlistlognames
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -91,10 +89,10 @@ type Tool struct {
Parameters parameters.Parameters `yaml:"parameters"` Parameters parameters.Parameters `yaml:"parameters"`
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
limit := defaultLimit limit := defaultLimit
@@ -102,22 +100,18 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if val, ok := paramsMap["limit"].(int); ok && val > 0 { if val, ok := paramsMap["limit"].(int); ok && val > 0 {
limit = val limit = val
} else if ok && val < 0 { } else if ok && val < 0 {
return nil, util.NewAgentError("limit must be greater than or equal to 1", nil) return nil, fmt.Errorf("limit must be greater than or equal to 1")
} }
tokenString := "" tokenString := ""
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenString, err = accessToken.ParseBearerToken() tokenString, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to parse access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("failed to parse access token: %w", err)
} }
} }
resp, err := source.ListLogNames(ctx, limit, tokenString) return source.ListLogNames(ctx, limit, tokenString)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) { func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) {

View File

@@ -16,13 +16,11 @@ package cloudloggingadminlistresourcetypes
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -86,25 +84,21 @@ type Tool struct {
mcpManifest tools.McpManifest mcpManifest tools.McpManifest
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
tokenString := "" tokenString := ""
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenString, err = accessToken.ParseBearerToken() tokenString, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to parse access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("failed to parse access token: %w", err)
} }
} }
resp, err := source.ListResourceTypes(ctx, tokenString) return source.ListResourceTypes(ctx, tokenString)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) { func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) {

View File

@@ -16,7 +16,6 @@ package cloudloggingadminquerylogs
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"time" "time"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
@@ -24,7 +23,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
cla "github.com/googleapis/genai-toolbox/internal/sources/cloudloggingadmin" cla "github.com/googleapis/genai-toolbox/internal/sources/cloudloggingadmin"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -106,10 +104,10 @@ type Tool struct {
mcpManifest tools.McpManifest mcpManifest tools.McpManifest
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
// Parse parameters // Parse parameters
@@ -121,7 +119,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if val, ok := paramsMap["limit"].(int); ok && val > 0 { if val, ok := paramsMap["limit"].(int); ok && val > 0 {
limit = val limit = val
} else if ok && val < 0 { } else if ok && val < 0 {
return nil, util.NewAgentError("limit must be greater than or equal to 1", nil) return nil, fmt.Errorf("limit must be greater than or equal to 1")
} }
// Check for verbosity of output // Check for verbosity of output
@@ -131,7 +129,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var filter string var filter string
if f, ok := paramsMap["filter"].(string); ok { if f, ok := paramsMap["filter"].(string); ok {
if len(f) == 0 { if len(f) == 0 {
return nil, util.NewAgentError("filter cannot be empty if provided", nil) return nil, fmt.Errorf("filter cannot be empty if provided")
} }
filter = f filter = f
} }
@@ -140,7 +138,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var startTime string var startTime string
if val, ok := paramsMap["startTime"].(string); ok && val != "" { if val, ok := paramsMap["startTime"].(string); ok && val != "" {
if _, err := time.Parse(time.RFC3339, val); err != nil { if _, err := time.Parse(time.RFC3339, val); err != nil {
return nil, util.NewAgentError(fmt.Sprintf("startTime must be in RFC3339 format (e.g., 2025-12-09T00:00:00Z): %v", err), err) return nil, fmt.Errorf("startTime must be in RFC3339 format (e.g., 2025-12-09T00:00:00Z): %w", err)
} }
startTime = val startTime = val
} else { } else {
@@ -151,7 +149,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var endTime string var endTime string
if val, ok := paramsMap["endTime"].(string); ok && val != "" { if val, ok := paramsMap["endTime"].(string); ok && val != "" {
if _, err := time.Parse(time.RFC3339, val); err != nil { if _, err := time.Parse(time.RFC3339, val); err != nil {
return nil, util.NewAgentError(fmt.Sprintf("endTime must be in RFC3339 format (e.g., 2025-12-09T23:59:59Z): %v", err), err) return nil, fmt.Errorf("endTime must be in RFC3339 format (e.g., 2025-12-09T23:59:59Z): %w", err)
} }
endTime = val endTime = val
} }
@@ -160,7 +158,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if source.UseClientAuthorization() { if source.UseClientAuthorization() {
tokenString, err = accessToken.ParseBearerToken() tokenString, err = accessToken.ParseBearerToken()
if err != nil { if err != nil {
return nil, util.NewClientServerError("failed to parse access token", http.StatusUnauthorized, err) return nil, fmt.Errorf("failed to parse access token: %w", err)
} }
} }
@@ -173,11 +171,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
Limit: limit, Limit: limit,
} }
resp, err := source.QueryLogs(ctx, queryParams, tokenString) return source.QueryLogs(ctx, queryParams, tokenString)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) { func (t Tool) ParseParams(data map[string]any, claimsMap map[string]map[string]any) (parameters.ParamValues, error) {

View File

@@ -23,7 +23,6 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -94,26 +93,22 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
projectID, ok := paramsMap["projectId"].(string) projectID, ok := paramsMap["projectId"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("projectId parameter not found or not a string", nil) return nil, fmt.Errorf("projectId parameter not found or not a string")
} }
query, ok := paramsMap["query"].(string) query, ok := paramsMap["query"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("query parameter not found or not a string", nil) return nil, fmt.Errorf("query parameter not found or not a string")
} }
resp, err := source.RunQuery(projectID, query) return source.RunQuery(projectID, query)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package cloudsqlcloneinstance
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
sqladmin "google.golang.org/api/sqladmin/v1" sqladmin "google.golang.org/api/sqladmin/v1"
) )
@@ -126,35 +124,31 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'project' parameter: %v", paramsMap["project"]), nil) return nil, fmt.Errorf("error casting 'project' parameter: %v", paramsMap["project"])
} }
sourceInstanceName, ok := paramsMap["sourceInstanceName"].(string) sourceInstanceName, ok := paramsMap["sourceInstanceName"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'sourceInstanceName' parameter: %v", paramsMap["sourceInstanceName"]), nil) return nil, fmt.Errorf("error casting 'sourceInstanceName' parameter: %v", paramsMap["sourceInstanceName"])
} }
destinationInstanceName, ok := paramsMap["destinationInstanceName"].(string) destinationInstanceName, ok := paramsMap["destinationInstanceName"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'destinationInstanceName' parameter: %v", paramsMap["destinationInstanceName"]), nil) return nil, fmt.Errorf("error casting 'destinationInstanceName' parameter: %v", paramsMap["destinationInstanceName"])
} }
pointInTime, _ := paramsMap["pointInTime"].(string) pointInTime, _ := paramsMap["pointInTime"].(string)
preferredZone, _ := paramsMap["preferredZone"].(string) preferredZone, _ := paramsMap["preferredZone"].(string)
preferredSecondaryZone, _ := paramsMap["preferredSecondaryZone"].(string) preferredSecondaryZone, _ := paramsMap["preferredSecondaryZone"].(string)
resp, err := source.CloneInstance(ctx, project, sourceInstanceName, destinationInstanceName, pointInTime, preferredZone, preferredSecondaryZone, string(accessToken)) return source.CloneInstance(ctx, project, sourceInstanceName, destinationInstanceName, pointInTime, preferredZone, preferredSecondaryZone, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package cloudsqlcreatebackup
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/sqladmin/v1" "google.golang.org/api/sqladmin/v1"
) )
@@ -122,30 +120,26 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'project' parameter: %v", paramsMap["project"]), nil) return nil, fmt.Errorf("error casting 'project' parameter: %v", paramsMap["project"])
} }
instance, ok := paramsMap["instance"].(string) instance, ok := paramsMap["instance"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'instance' parameter: %v", paramsMap["instance"]), nil) return nil, fmt.Errorf("error casting 'instance' parameter: %v", paramsMap["instance"])
} }
location, _ := paramsMap["location"].(string) location, _ := paramsMap["location"].(string)
description, _ := paramsMap["backup_description"].(string) description, _ := paramsMap["backup_description"].(string)
resp, err := source.InsertBackupRun(ctx, project, instance, location, description, string(accessToken)) return source.InsertBackupRun(ctx, project, instance, location, description, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package cloudsqlcreatedatabase
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -119,31 +117,27 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'project' parameter", nil) return nil, fmt.Errorf("missing 'project' parameter")
} }
instance, ok := paramsMap["instance"].(string) instance, ok := paramsMap["instance"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'instance' parameter", nil) return nil, fmt.Errorf("missing 'instance' parameter")
} }
name, ok := paramsMap["name"].(string) name, ok := paramsMap["name"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'name' parameter", nil) return nil, fmt.Errorf("missing 'name' parameter")
} }
resp, err := source.CreateDatabase(ctx, name, project, instance, string(accessToken)) return source.CreateDatabase(ctx, name, project, instance, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package cloudsqlcreateusers
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -121,34 +119,30 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'project' parameter", nil) return nil, fmt.Errorf("missing 'project' parameter")
} }
instance, ok := paramsMap["instance"].(string) instance, ok := paramsMap["instance"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'instance' parameter", nil) return nil, fmt.Errorf("missing 'instance' parameter")
} }
name, ok := paramsMap["name"].(string) name, ok := paramsMap["name"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'name' parameter", nil) return nil, fmt.Errorf("missing 'name' parameter")
} }
iamUser, _ := paramsMap["iamUser"].(bool) iamUser, _ := paramsMap["iamUser"].(bool)
password, _ := paramsMap["password"].(string) password, _ := paramsMap["password"].(string)
resp, err := source.CreateUsers(ctx, project, instance, name, password, iamUser, string(accessToken)) return source.CreateUsers(ctx, project, instance, name, password, iamUser, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package cloudsqlgetinstances
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -119,27 +117,23 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
projectId, ok := paramsMap["projectId"].(string) projectId, ok := paramsMap["projectId"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'projectId' parameter", nil) return nil, fmt.Errorf("missing 'projectId' parameter")
} }
instanceId, ok := paramsMap["instanceId"].(string) instanceId, ok := paramsMap["instanceId"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'instanceId' parameter", nil) return nil, fmt.Errorf("missing 'instanceId' parameter")
} }
resp, err := source.GetInstance(ctx, projectId, instanceId, string(accessToken)) return source.GetInstance(ctx, projectId, instanceId, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package cloudsqllistdatabases
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -118,27 +116,23 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'project' parameter", nil) return nil, fmt.Errorf("missing 'project' parameter")
} }
instance, ok := paramsMap["instance"].(string) instance, ok := paramsMap["instance"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'instance' parameter", nil) return nil, fmt.Errorf("missing 'instance' parameter")
} }
resp, err := source.ListDatabase(ctx, project, instance, string(accessToken)) return source.ListDatabase(ctx, project, instance, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package cloudsqllistinstances
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -117,23 +115,19 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'project' parameter", nil) return nil, fmt.Errorf("missing 'project' parameter")
} }
resp, err := source.ListInstance(ctx, project, string(accessToken)) return source.ListInstance(ctx, project, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package cloudsqlrestorebackup
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/sqladmin/v1" "google.golang.org/api/sqladmin/v1"
) )
@@ -122,33 +120,29 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
targetProject, ok := paramsMap["target_project"].(string) targetProject, ok := paramsMap["target_project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'target_project' parameter: %v", paramsMap["target_project"]), nil) return nil, fmt.Errorf("error casting 'target_project' parameter: %v", paramsMap["target_project"])
} }
targetInstance, ok := paramsMap["target_instance"].(string) targetInstance, ok := paramsMap["target_instance"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'target_instance' parameter: %v", paramsMap["target_instance"]), nil) return nil, fmt.Errorf("error casting 'target_instance' parameter: %v", paramsMap["target_instance"])
} }
backupID, ok := paramsMap["backup_id"].(string) backupID, ok := paramsMap["backup_id"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'backup_id' parameter: %v", paramsMap["backup_id"]), nil) return nil, fmt.Errorf("error casting 'backup_id' parameter: %v", paramsMap["backup_id"])
} }
sourceProject, _ := paramsMap["source_project"].(string) sourceProject, _ := paramsMap["source_project"].(string)
sourceInstance, _ := paramsMap["source_instance"].(string) sourceInstance, _ := paramsMap["source_instance"].(string)
resp, err := source.RestoreBackup(ctx, targetProject, targetInstance, sourceProject, sourceInstance, backupID, string(accessToken)) return source.RestoreBackup(ctx, targetProject, targetInstance, sourceProject, sourceInstance, backupID, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package cloudsqlwaitforoperation
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"time" "time"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/sqladmin/v1" "google.golang.org/api/sqladmin/v1"
) )
@@ -212,21 +210,21 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'project' parameter", nil) return nil, fmt.Errorf("missing 'project' parameter")
} }
operationID, ok := paramsMap["operation"].(string) operationID, ok := paramsMap["operation"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'operation' parameter", nil) return nil, fmt.Errorf("missing 'operation' parameter")
} }
ctx, cancel := context.WithTimeout(ctx, 30*time.Minute) ctx, cancel := context.WithTimeout(ctx, 30*time.Minute)
@@ -234,7 +232,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
service, err := source.GetService(ctx, string(accessToken)) service, err := source.GetService(ctx, string(accessToken))
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, err
} }
delay := t.Delay delay := t.Delay
@@ -246,13 +244,13 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
for retries < maxRetries { for retries < maxRetries {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, util.NewClientServerError("timed out waiting for operation", http.StatusRequestTimeout, ctx.Err()) return nil, fmt.Errorf("timed out waiting for operation: %w", ctx.Err())
default: default:
} }
op, err := source.GetWaitForOperations(ctx, service, project, operationID, cloudSQLConnectionMessageTemplate, delay) op, err := source.GetWaitForOperations(ctx, service, project, operationID, cloudSQLConnectionMessageTemplate, delay)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, err
} else if op != nil { } else if op != nil {
return op, nil return op, nil
} }
@@ -264,7 +262,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
retries++ retries++
} }
return nil, util.NewClientServerError("exceeded max retries waiting for operation", http.StatusGatewayTimeout, fmt.Errorf("exceeded max retries")) return nil, fmt.Errorf("exceeded max retries waiting for operation")
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package cloudsqlmssqlcreateinstance
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/sqladmin/v1" "google.golang.org/api/sqladmin/v1"
) )
@@ -123,33 +121,33 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'project' parameter: %s", paramsMap["project"]), nil) return nil, fmt.Errorf("error casting 'project' parameter: %s", paramsMap["project"])
} }
name, ok := paramsMap["name"].(string) name, ok := paramsMap["name"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'name' parameter: %s", paramsMap["name"]), nil) return nil, fmt.Errorf("error casting 'name' parameter: %s", paramsMap["name"])
} }
dbVersion, ok := paramsMap["databaseVersion"].(string) dbVersion, ok := paramsMap["databaseVersion"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'databaseVersion' parameter: %s", paramsMap["databaseVersion"]), nil) return nil, fmt.Errorf("error casting 'databaseVersion' parameter: %s", paramsMap["databaseVersion"])
} }
rootPassword, ok := paramsMap["rootPassword"].(string) rootPassword, ok := paramsMap["rootPassword"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'rootPassword' parameter: %s", paramsMap["rootPassword"]), nil) return nil, fmt.Errorf("error casting 'rootPassword' parameter: %s", paramsMap["rootPassword"])
} }
editionPreset, ok := paramsMap["editionPreset"].(string) editionPreset, ok := paramsMap["editionPreset"].(string)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'editionPreset' parameter: %s", paramsMap["editionPreset"]), nil) return nil, fmt.Errorf("error casting 'editionPreset' parameter: %s", paramsMap["editionPreset"])
} }
settings := sqladmin.Settings{} settings := sqladmin.Settings{}
switch strings.ToLower(editionPreset) { switch strings.ToLower(editionPreset) {
@@ -166,13 +164,9 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
settings.DataDiskSizeGb = 100 settings.DataDiskSizeGb = 100
settings.DataDiskType = "PD_SSD" settings.DataDiskType = "PD_SSD"
default: default:
return nil, util.NewAgentError(fmt.Sprintf("invalid 'editionPreset': %q. Must be either 'Production' or 'Development'", editionPreset), nil) return nil, fmt.Errorf("invalid 'editionPreset': %q. Must be either 'Production' or 'Development'", editionPreset)
} }
resp, err := source.CreateInstance(ctx, project, name, dbVersion, rootPassword, settings, string(accessToken)) return source.CreateInstance(ctx, project, name, dbVersion, rootPassword, settings, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package cloudsqlmysqlcreateinstance
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
sqladmin "google.golang.org/api/sqladmin/v1" sqladmin "google.golang.org/api/sqladmin/v1"
) )
@@ -123,33 +121,33 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'project' parameter", nil) return nil, fmt.Errorf("missing 'project' parameter")
} }
name, ok := paramsMap["name"].(string) name, ok := paramsMap["name"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'name' parameter", nil) return nil, fmt.Errorf("missing 'name' parameter")
} }
dbVersion, ok := paramsMap["databaseVersion"].(string) dbVersion, ok := paramsMap["databaseVersion"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'databaseVersion' parameter", nil) return nil, fmt.Errorf("missing 'databaseVersion' parameter")
} }
rootPassword, ok := paramsMap["rootPassword"].(string) rootPassword, ok := paramsMap["rootPassword"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'rootPassword' parameter", nil) return nil, fmt.Errorf("missing 'rootPassword' parameter")
} }
editionPreset, ok := paramsMap["editionPreset"].(string) editionPreset, ok := paramsMap["editionPreset"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'editionPreset' parameter", nil) return nil, fmt.Errorf("missing 'editionPreset' parameter")
} }
settings := sqladmin.Settings{} settings := sqladmin.Settings{}
@@ -167,14 +165,10 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
settings.DataDiskSizeGb = 100 settings.DataDiskSizeGb = 100
settings.DataDiskType = "PD_SSD" settings.DataDiskType = "PD_SSD"
default: default:
return nil, util.NewAgentError(fmt.Sprintf("invalid 'editionPreset': %q. Must be either 'Production' or 'Development'", editionPreset), nil) return nil, fmt.Errorf("invalid 'editionPreset': %q. Must be either 'Production' or 'Development'", editionPreset)
} }
resp, err := source.CreateInstance(ctx, project, name, dbVersion, rootPassword, settings, string(accessToken)) return source.CreateInstance(ctx, project, name, dbVersion, rootPassword, settings, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package cloudsqlpgcreateinstances
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"strings" "strings"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
sqladmin "google.golang.org/api/sqladmin/v1" sqladmin "google.golang.org/api/sqladmin/v1"
) )
@@ -123,33 +121,33 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'project' parameter", nil) return nil, fmt.Errorf("missing 'project' parameter")
} }
name, ok := paramsMap["name"].(string) name, ok := paramsMap["name"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'name' parameter", nil) return nil, fmt.Errorf("missing 'name' parameter")
} }
dbVersion, ok := paramsMap["databaseVersion"].(string) dbVersion, ok := paramsMap["databaseVersion"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'databaseVersion' parameter", nil) return nil, fmt.Errorf("missing 'databaseVersion' parameter")
} }
rootPassword, ok := paramsMap["rootPassword"].(string) rootPassword, ok := paramsMap["rootPassword"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'rootPassword' parameter", nil) return nil, fmt.Errorf("missing 'rootPassword' parameter")
} }
editionPreset, ok := paramsMap["editionPreset"].(string) editionPreset, ok := paramsMap["editionPreset"].(string)
if !ok { if !ok {
return nil, util.NewAgentError("missing 'editionPreset' parameter", nil) return nil, fmt.Errorf("missing 'editionPreset' parameter")
} }
settings := sqladmin.Settings{} settings := sqladmin.Settings{}
@@ -167,13 +165,9 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
settings.DataDiskSizeGb = 100 settings.DataDiskSizeGb = 100
settings.DataDiskType = "PD_SSD" settings.DataDiskType = "PD_SSD"
default: default:
return nil, util.NewAgentError(fmt.Sprintf("invalid 'editionPreset': %q. Must be either 'Production' or 'Development'", editionPreset), nil) return nil, fmt.Errorf("invalid 'editionPreset': %q. Must be either 'Production' or 'Development'", editionPreset)
} }
resp, err := source.CreateInstance(ctx, project, name, dbVersion, rootPassword, settings, string(accessToken)) return source.CreateInstance(ctx, project, name, dbVersion, rootPassword, settings, string(accessToken))
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package cloudsqlpgupgradeprecheck
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"time" "time"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
sqladmin "google.golang.org/api/sqladmin/v1" sqladmin "google.golang.org/api/sqladmin/v1"
) )
@@ -134,31 +132,31 @@ func (t Tool) ToConfig() tools.ToolConfig {
} }
// Invoke executes the tool's logic. // Invoke executes the tool's logic.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
project, ok := paramsMap["project"].(string) project, ok := paramsMap["project"].(string)
if !ok || project == "" { if !ok || project == "" {
return nil, util.NewAgentError("missing or empty 'project' parameter", nil) return nil, fmt.Errorf("missing or empty 'project' parameter")
} }
instanceName, ok := paramsMap["instance"].(string) instanceName, ok := paramsMap["instance"].(string)
if !ok || instanceName == "" { if !ok || instanceName == "" {
return nil, util.NewAgentError("missing or empty 'instance' parameter", nil) return nil, fmt.Errorf("missing or empty 'instance' parameter")
} }
targetVersion, ok := paramsMap["targetDatabaseVersion"].(string) targetVersion, ok := paramsMap["targetDatabaseVersion"].(string)
if !ok || targetVersion == "" { if !ok || targetVersion == "" {
// This should not happen due to the default value // This should not happen due to the default value
return nil, util.NewAgentError("missing or empty 'targetDatabaseVersion' parameter", nil) return nil, fmt.Errorf("missing or empty 'targetDatabaseVersion' parameter")
} }
service, err := source.GetService(ctx, string(accessToken)) service, err := source.GetService(ctx, string(accessToken))
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("failed to get HTTP client from source: %w", err)
} }
reqBody := &sqladmin.InstancesPreCheckMajorVersionUpgradeRequest{ reqBody := &sqladmin.InstancesPreCheckMajorVersionUpgradeRequest{
@@ -170,7 +168,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
call := service.Instances.PreCheckMajorVersionUpgrade(project, instanceName, reqBody).Context(ctx) call := service.Instances.PreCheckMajorVersionUpgrade(project, instanceName, reqBody).Context(ctx)
op, err := call.Do() op, err := call.Do()
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("failed to start pre-check operation: %w", err)
} }
const pollTimeout = 20 * time.Second const pollTimeout = 20 * time.Second
@@ -179,7 +177,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
for time.Now().Before(cutoffTime) { for time.Now().Before(cutoffTime) {
currentOp, err := service.Operations.Get(project, op.Name).Context(ctx).Do() currentOp, err := service.Operations.Get(project, op.Name).Context(ctx).Do()
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, fmt.Errorf("failed to get operation status: %w", err)
} }
if currentOp.Status == "DONE" { if currentOp.Status == "DONE" {
@@ -188,7 +186,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if currentOp.Error.Errors[0].Code != "" { if currentOp.Error.Errors[0].Code != "" {
errMsg = fmt.Sprintf("%s (Code: %s)", errMsg, currentOp.Error.Errors[0].Code) errMsg = fmt.Sprintf("%s (Code: %s)", errMsg, currentOp.Error.Errors[0].Code)
} }
return nil, util.NewClientServerError(errMsg, http.StatusInternalServerError, fmt.Errorf("pre-check operation failed with error: %s", errMsg)) return nil, fmt.Errorf("%s", errMsg)
} }
var preCheckItems []*sqladmin.PreCheckResponse var preCheckItems []*sqladmin.PreCheckResponse
@@ -201,7 +199,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, util.NewClientServerError("timed out waiting for operation", http.StatusRequestTimeout, ctx.Err()) return nil, ctx.Err()
case <-time.After(5 * time.Second): case <-time.After(5 * time.Second):
} }
} }

View File

@@ -17,14 +17,12 @@ package dataplexlookupentry
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
dataplexpb "cloud.google.com/go/dataplex/apiv1/dataplexpb" dataplexpb "cloud.google.com/go/dataplex/apiv1/dataplexpb"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -112,10 +110,10 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
@@ -124,14 +122,10 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
view, _ := paramsMap["view"].(int) view, _ := paramsMap["view"].(int)
aspectTypeSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["aspectTypes"].([]any), "string") aspectTypeSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["aspectTypes"].([]any), "string")
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("can't convert aspectTypes to array of strings: %s", err), err) return nil, fmt.Errorf("can't convert aspectTypes to array of strings: %s", err)
} }
aspectTypes := aspectTypeSlice.([]string) aspectTypes := aspectTypeSlice.([]string)
resp, err := source.LookupEntry(ctx, name, view, aspectTypes, entry) return source.LookupEntry(ctx, name, view, aspectTypes, entry)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package dataplexsearchaspecttypes
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"cloud.google.com/go/dataplex/apiv1/dataplexpb" "cloud.google.com/go/dataplex/apiv1/dataplexpb"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -95,29 +93,16 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
query, ok := paramsMap["query"].(string) query, _ := paramsMap["query"].(string)
if !ok { pageSize, _ := paramsMap["pageSize"].(int)
return nil, util.NewAgentError(fmt.Sprintf("error casting 'query' parameter: %v", paramsMap["query"]), nil) orderBy, _ := paramsMap["orderBy"].(string)
} return source.SearchAspectTypes(ctx, query, pageSize, orderBy)
pageSize, ok := paramsMap["pageSize"].(int)
if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'pageSize' parameter: %v", paramsMap["pageSize"]), nil)
}
orderBy, ok := paramsMap["orderBy"].(string)
if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'orderBy' parameter: %v", paramsMap["orderBy"]), nil)
}
resp, err := source.SearchAspectTypes(ctx, query, pageSize, orderBy)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,14 +17,12 @@ package dataplexsearchentries
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"cloud.google.com/go/dataplex/apiv1/dataplexpb" "cloud.google.com/go/dataplex/apiv1/dataplexpb"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -95,29 +93,16 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
query, ok := paramsMap["query"].(string) query, _ := paramsMap["query"].(string)
if !ok { pageSize, _ := paramsMap["pageSize"].(int)
return nil, util.NewAgentError(fmt.Sprintf("error casting 'query' parameter: %v", paramsMap["query"]), nil) orderBy, _ := paramsMap["orderBy"].(string)
} return source.SearchEntries(ctx, query, pageSize, orderBy)
pageSize, ok := paramsMap["pageSize"].(int)
if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'pageSize' parameter: %v", paramsMap["pageSize"]), nil)
}
orderBy, ok := paramsMap["orderBy"].(string)
if !ok {
return nil, util.NewAgentError(fmt.Sprintf("error casting 'orderBy' parameter: %v", paramsMap["orderBy"]), nil)
}
resp, err := source.SearchEntries(ctx, query, pageSize, orderBy)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,15 +17,13 @@ package firestoreadddocuments
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
firestoreapi "cloud.google.com/go/firestore" firestoreapi "cloud.google.com/go/firestore"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
fsUtil "github.com/googleapis/genai-toolbox/internal/tools/firestore/util" "github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -130,32 +128,32 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
// Get collection path // Get collection path
collectionPath, ok := mapParams[collectionPathKey].(string) collectionPath, ok := mapParams[collectionPathKey].(string)
if !ok || collectionPath == "" { if !ok || collectionPath == "" {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter", collectionPathKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter", collectionPathKey)
} }
// Validate collection path // Validate collection path
if err := fsUtil.ValidateCollectionPath(collectionPath); err != nil { if err := util.ValidateCollectionPath(collectionPath); err != nil {
return nil, util.NewAgentError(fmt.Sprintf("invalid collection path: %v", err), err) return nil, fmt.Errorf("invalid collection path: %w", err)
} }
// Get document data // Get document data
documentDataRaw, ok := mapParams[documentDataKey] documentDataRaw, ok := mapParams[documentDataKey]
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter", documentDataKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter", documentDataKey)
} }
// Convert the document data from JSON format to Firestore format // Convert the document data from JSON format to Firestore format
// The client is passed to handle referenceValue types // The client is passed to handle referenceValue types
documentData, err := fsUtil.JSONToFirestoreValue(documentDataRaw, source.FirestoreClient()) documentData, err := util.JSONToFirestoreValue(documentDataRaw, source.FirestoreClient())
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to convert document data: %v", err), err) return nil, fmt.Errorf("failed to convert document data: %w", err)
} }
// Get return document data flag // Get return document data flag
@@ -163,11 +161,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if val, ok := mapParams[returnDocumentDataKey].(bool); ok { if val, ok := mapParams[returnDocumentDataKey].(bool); ok {
returnData = val returnData = val
} }
resp, err := source.AddDocuments(ctx, collectionPath, documentData, returnData) return source.AddDocuments(ctx, collectionPath, documentData, returnData)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,15 +17,13 @@ package firestoredeletedocuments
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
firestoreapi "cloud.google.com/go/firestore" firestoreapi "cloud.google.com/go/firestore"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
fsUtil "github.com/googleapis/genai-toolbox/internal/tools/firestore/util" "github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -96,43 +94,39 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
documentPathsRaw, ok := mapParams[documentPathsKey].([]any) documentPathsRaw, ok := mapParams[documentPathsKey].([]any)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected an array", documentPathsKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected an array", documentPathsKey)
} }
if len(documentPathsRaw) == 0 { if len(documentPathsRaw) == 0 {
return nil, util.NewAgentError(fmt.Sprintf("'%s' parameter cannot be empty", documentPathsKey), nil) return nil, fmt.Errorf("'%s' parameter cannot be empty", documentPathsKey)
} }
// Use ConvertAnySliceToTyped to convert the slice // Use ConvertAnySliceToTyped to convert the slice
typedSlice, err := parameters.ConvertAnySliceToTyped(documentPathsRaw, "string") typedSlice, err := parameters.ConvertAnySliceToTyped(documentPathsRaw, "string")
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to convert document paths: %v", err), err) return nil, fmt.Errorf("failed to convert document paths: %w", err)
} }
documentPaths, ok := typedSlice.([]string) documentPaths, ok := typedSlice.([]string)
if !ok { if !ok {
return nil, util.NewAgentError("unexpected type conversion error for document paths", nil) return nil, fmt.Errorf("unexpected type conversion error for document paths")
} }
// Validate each document path // Validate each document path
for i, path := range documentPaths { for i, path := range documentPaths {
if err := fsUtil.ValidateDocumentPath(path); err != nil { if err := util.ValidateDocumentPath(path); err != nil {
return nil, util.NewAgentError(fmt.Sprintf("invalid document path at index %d: %v", i, err), err) return nil, fmt.Errorf("invalid document path at index %d: %w", i, err)
} }
} }
resp, err := source.DeleteDocuments(ctx, documentPaths) return source.DeleteDocuments(ctx, documentPaths)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,15 +17,13 @@ package firestoregetdocuments
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
firestoreapi "cloud.google.com/go/firestore" firestoreapi "cloud.google.com/go/firestore"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
fsUtil "github.com/googleapis/genai-toolbox/internal/tools/firestore/util" "github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -96,44 +94,40 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
documentPathsRaw, ok := mapParams[documentPathsKey].([]any) documentPathsRaw, ok := mapParams[documentPathsKey].([]any)
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter; expected an array", documentPathsKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter; expected an array", documentPathsKey)
} }
if len(documentPathsRaw) == 0 { if len(documentPathsRaw) == 0 {
return nil, util.NewAgentError(fmt.Sprintf("'%s' parameter cannot be empty", documentPathsKey), nil) return nil, fmt.Errorf("'%s' parameter cannot be empty", documentPathsKey)
} }
// Use ConvertAnySliceToTyped to convert the slice // Use ConvertAnySliceToTyped to convert the slice
typedSlice, err := parameters.ConvertAnySliceToTyped(documentPathsRaw, "string") typedSlice, err := parameters.ConvertAnySliceToTyped(documentPathsRaw, "string")
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to convert document paths: %v", err), err) return nil, fmt.Errorf("failed to convert document paths: %w", err)
} }
documentPaths, ok := typedSlice.([]string) documentPaths, ok := typedSlice.([]string)
if !ok { if !ok {
return nil, util.NewAgentError("unexpected type conversion error for document paths", nil) return nil, fmt.Errorf("unexpected type conversion error for document paths")
} }
// Validate each document path // Validate each document path
for i, path := range documentPaths { for i, path := range documentPaths {
if err := fsUtil.ValidateDocumentPath(path); err != nil { if err := util.ValidateDocumentPath(path); err != nil {
return nil, util.NewAgentError(fmt.Sprintf("invalid document path at index %d: %v", i, err), err) return nil, fmt.Errorf("invalid document path at index %d: %w", i, err)
} }
} }
resp, err := source.GetDocuments(ctx, documentPaths) return source.GetDocuments(ctx, documentPaths)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,13 +17,11 @@ package firestoregetrules
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/firebaserules/v1" "google.golang.org/api/firebaserules/v1"
) )
@@ -94,16 +92,12 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
resp, err := source.GetRules(ctx) return source.GetRules(ctx)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,15 +17,13 @@ package firestorelistcollections
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
firestoreapi "cloud.google.com/go/firestore" firestoreapi "cloud.google.com/go/firestore"
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
fsUtil "github.com/googleapis/genai-toolbox/internal/tools/firestore/util" "github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -97,10 +95,10 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
@@ -109,15 +107,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
parentPath, _ := mapParams[parentPathKey].(string) parentPath, _ := mapParams[parentPathKey].(string)
if parentPath != "" { if parentPath != "" {
// Validate parent document path // Validate parent document path
if err := fsUtil.ValidateDocumentPath(parentPath); err != nil { if err := util.ValidateDocumentPath(parentPath); err != nil {
return nil, util.NewAgentError(fmt.Sprintf("invalid parent document path: %v", err), err) return nil, fmt.Errorf("invalid parent document path: %w", err)
} }
} }
resp, err := source.ListCollections(ctx, parentPath) return source.ListCollections(ctx, parentPath)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,7 +18,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"strconv" "strconv"
"strings" "strings"
@@ -27,8 +26,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
fsUtil "github.com/googleapis/genai-toolbox/internal/tools/firestore/util" "github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -160,16 +158,16 @@ var validOperators = map[string]bool{
} }
// Invoke executes the Firestore query based on the provided parameters // Invoke executes the Firestore query based on the provided parameters
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
paramsMap := params.AsMap() paramsMap := params.AsMap()
// Process collection path with template substitution // Process collection path with template substitution
collectionPath, err := parameters.PopulateTemplate("collectionPath", t.CollectionPath, paramsMap) collectionPath, err := parameters.PopulateTemplate("collectionPath", t.CollectionPath, paramsMap)
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to process collection path: %v", err), err) return nil, fmt.Errorf("failed to process collection path: %w", err)
} }
var filter firestoreapi.EntityFilter var filter firestoreapi.EntityFilter
@@ -178,13 +176,13 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Apply template substitution to filters // Apply template substitution to filters
filtersJSON, err := parameters.PopulateTemplateWithJSON("filters", t.Filters, paramsMap) filtersJSON, err := parameters.PopulateTemplateWithJSON("filters", t.Filters, paramsMap)
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to process filters template: %v", err), err) return nil, fmt.Errorf("failed to process filters template: %w", err)
} }
// Parse the simplified filter format // Parse the simplified filter format
var simplifiedFilter SimplifiedFilter var simplifiedFilter SimplifiedFilter
if err := json.Unmarshal([]byte(filtersJSON), &simplifiedFilter); err != nil { if err := json.Unmarshal([]byte(filtersJSON), &simplifiedFilter); err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to parse filters: %v", err), err) return nil, fmt.Errorf("failed to parse filters: %w", err)
} }
// Convert simplified filter to Firestore filter // Convert simplified filter to Firestore filter
@@ -193,17 +191,17 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Process and apply ordering // Process and apply ordering
orderBy, err := t.getOrderBy(paramsMap) orderBy, err := t.getOrderBy(paramsMap)
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to process order by: %v", err), err) return nil, err
} }
// Process select fields // Process select fields
selectFields, err := t.processSelectFields(paramsMap) selectFields, err := t.processSelectFields(paramsMap)
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to process select fields: %v", err), err) return nil, err
} }
// Process and apply limit // Process and apply limit
limit, err := t.getLimit(paramsMap) limit, err := t.getLimit(paramsMap)
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to process limit: %v", err), err) return nil, err
} }
// prevent panic when accessing orderBy incase it is nil // prevent panic when accessing orderBy incase it is nil
@@ -217,14 +215,10 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Build the query // Build the query
query, err := source.BuildQuery(collectionPath, filter, selectFields, orderByField, orderByDirection, limit, t.AnalyzeQuery) query, err := source.BuildQuery(collectionPath, filter, selectFields, orderByField, orderByDirection, limit, t.AnalyzeQuery)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, err
} }
// Execute the query and return results // Execute the query and return results
resp, err := source.ExecuteQuery(ctx, query, t.AnalyzeQuery) return source.ExecuteQuery(ctx, query, t.AnalyzeQuery)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
// convertToFirestoreFilter converts simplified filter format to Firestore EntityFilter // convertToFirestoreFilter converts simplified filter format to Firestore EntityFilter
@@ -261,7 +255,7 @@ func (t Tool) convertToFirestoreFilter(source compatibleSource, filter Simplifie
if filter.Field != "" && filter.Op != "" && filter.Value != nil { if filter.Field != "" && filter.Op != "" && filter.Value != nil {
if validOperators[filter.Op] { if validOperators[filter.Op] {
// Convert the value using the Firestore native JSON converter // Convert the value using the Firestore native JSON converter
convertedValue, err := fsUtil.JSONToFirestoreValue(filter.Value, source.FirestoreClient()) convertedValue, err := util.JSONToFirestoreValue(filter.Value, source.FirestoreClient())
if err != nil { if err != nil {
// If conversion fails, use the original value // If conversion fails, use the original value
convertedValue = filter.Value convertedValue = filter.Value
@@ -373,7 +367,7 @@ func (t Tool) getLimit(params map[string]any) (int, error) {
if processedValue != "" { if processedValue != "" {
parsedLimit, err := strconv.Atoi(processedValue) parsedLimit, err := strconv.Atoi(processedValue)
if err != nil { if err != nil {
return 0, err return 0, fmt.Errorf("failed to parse limit value '%s': %w", processedValue, err)
} }
limit = parsedLimit limit = parsedLimit
} }

View File

@@ -18,7 +18,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http" // Added for http.StatusInternalServerError
"strings" "strings"
firestoreapi "cloud.google.com/go/firestore" firestoreapi "cloud.google.com/go/firestore"
@@ -26,8 +25,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
fsUtil "github.com/googleapis/genai-toolbox/internal/tools/firestore/util" "github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
"github.com/googleapis/genai-toolbox/internal/util" // Added for util.ToolboxError
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -232,16 +230,16 @@ func (o *OrderByConfig) GetDirection() firestoreapi.Direction {
} }
// Invoke executes the Firestore query based on the provided parameters // Invoke executes the Firestore query based on the provided parameters
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
// Parse parameters // Parse parameters
queryParams, err := t.parseQueryParameters(params) queryParams, err := t.parseQueryParameters(params)
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to parse query parameters: %v", err), err) return nil, err
} }
var filter firestoreapi.EntityFilter var filter firestoreapi.EntityFilter
@@ -272,13 +270,9 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Build the query // Build the query
query, err := source.BuildQuery(queryParams.CollectionPath, filter, nil, orderByField, orderByDirection, queryParams.Limit, queryParams.AnalyzeQuery) query, err := source.BuildQuery(queryParams.CollectionPath, filter, nil, orderByField, orderByDirection, queryParams.Limit, queryParams.AnalyzeQuery)
if err != nil { if err != nil {
return nil, util.ProecessGcpError(err) return nil, err
} }
resp, err := source.ExecuteQuery(ctx, query, queryParams.AnalyzeQuery) return source.ExecuteQuery(ctx, query, queryParams.AnalyzeQuery)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
// queryParameters holds all parsed query parameters // queryParameters holds all parsed query parameters
@@ -301,7 +295,7 @@ func (t Tool) parseQueryParameters(params parameters.ParamValues) (*queryParamet
} }
// Validate collection path // Validate collection path
if err := fsUtil.ValidateCollectionPath(collectionPath); err != nil { if err := util.ValidateCollectionPath(collectionPath); err != nil {
return nil, fmt.Errorf("invalid collection path: %w", err) return nil, fmt.Errorf("invalid collection path: %w", err)
} }

View File

@@ -17,7 +17,6 @@ package firestoreupdatedocument
import ( import (
"context" "context"
"fmt" "fmt"
"net/http" // Added for http.StatusInternalServerError
"strings" "strings"
firestoreapi "cloud.google.com/go/firestore" firestoreapi "cloud.google.com/go/firestore"
@@ -25,8 +24,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
fsUtil "github.com/googleapis/genai-toolbox/internal/tools/firestore/util" "github.com/googleapis/genai-toolbox/internal/tools/firestore/util"
"github.com/googleapis/genai-toolbox/internal/util" // Added for util.ToolboxError
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
) )
@@ -140,10 +138,10 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
@@ -151,18 +149,18 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Get document path // Get document path
documentPath, ok := mapParams[documentPathKey].(string) documentPath, ok := mapParams[documentPathKey].(string)
if !ok || documentPath == "" { if !ok || documentPath == "" {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter", documentPathKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter", documentPathKey)
} }
// Validate document path // Validate document path
if err := fsUtil.ValidateDocumentPath(documentPath); err != nil { if err := util.ValidateDocumentPath(documentPath); err != nil {
return nil, util.NewAgentError(fmt.Sprintf("invalid document path: %v", err), err) return nil, fmt.Errorf("invalid document path: %w", err)
} }
// Get document data // Get document data
documentDataRaw, ok := mapParams[documentDataKey] documentDataRaw, ok := mapParams[documentDataKey]
if !ok { if !ok {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter", documentDataKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter", documentDataKey)
} }
// Get update mask if provided // Get update mask if provided
@@ -172,11 +170,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Use ConvertAnySliceToTyped to convert the slice // Use ConvertAnySliceToTyped to convert the slice
typedSlice, err := parameters.ConvertAnySliceToTyped(updateMaskArray, "string") typedSlice, err := parameters.ConvertAnySliceToTyped(updateMaskArray, "string")
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to convert update mask: %v", err), err) return nil, fmt.Errorf("failed to convert update mask: %w", err)
} }
updatePaths, ok = typedSlice.([]string) updatePaths, ok = typedSlice.([]string)
if !ok { if !ok {
return nil, util.NewAgentError("unexpected type conversion error for update mask", nil) return nil, fmt.Errorf("unexpected type conversion error for update mask")
} }
} }
} }
@@ -186,15 +184,15 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if len(updatePaths) > 0 { if len(updatePaths) > 0 {
// Convert document data without delete markers // Convert document data without delete markers
dataMap, err := fsUtil.JSONToFirestoreValue(documentDataRaw, source.FirestoreClient()) dataMap, err := util.JSONToFirestoreValue(documentDataRaw, source.FirestoreClient())
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to convert document data: %v", err), err) return nil, fmt.Errorf("failed to convert document data: %w", err)
} }
// Ensure it's a map // Ensure it's a map
dataMapTyped, ok := dataMap.(map[string]interface{}) dataMapTyped, ok := dataMap.(map[string]interface{})
if !ok { if !ok {
return nil, util.NewAgentError("document data must be a map", nil) return nil, fmt.Errorf("document data must be a map")
} }
for _, path := range updatePaths { for _, path := range updatePaths {
@@ -212,9 +210,9 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
} }
} else { } else {
// Update all fields in the document data (merge) // Update all fields in the document data (merge)
documentData, err = fsUtil.JSONToFirestoreValue(documentDataRaw, source.FirestoreClient()) documentData, err = util.JSONToFirestoreValue(documentDataRaw, source.FirestoreClient())
if err != nil { if err != nil {
return nil, util.NewAgentError(fmt.Sprintf("failed to convert document data: %v", err), err) return nil, fmt.Errorf("failed to convert document data: %w", err)
} }
} }
@@ -223,11 +221,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
if val, ok := mapParams[returnDocumentDataKey].(bool); ok { if val, ok := mapParams[returnDocumentDataKey].(bool); ok {
returnData = val returnData = val
} }
resp, err := source.UpdateDocument(ctx, documentPath, updates, documentData, returnData) return source.UpdateDocument(ctx, documentPath, updates, documentData, returnData)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
// getFieldValue retrieves a value from a nested map using a dot-separated path // getFieldValue retrieves a value from a nested map using a dot-separated path

View File

@@ -17,13 +17,11 @@ package firestorevalidaterules
import ( import (
"context" "context"
"fmt" "fmt"
"net/http" // Added for http.StatusInternalServerError
yaml "github.com/goccy/go-yaml" yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels" "github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources" "github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools" "github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util" // Added for util.ToolboxError
"github.com/googleapis/genai-toolbox/internal/util/parameters" "github.com/googleapis/genai-toolbox/internal/util/parameters"
"google.golang.org/api/firebaserules/v1" "google.golang.org/api/firebaserules/v1"
) )
@@ -108,10 +106,10 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config return t.Config
} }
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) { func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type) source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil { if err != nil {
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err) return nil, err
} }
mapParams := params.AsMap() mapParams := params.AsMap()
@@ -119,13 +117,9 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// Get source parameter // Get source parameter
sourceParam, ok := mapParams[sourceKey].(string) sourceParam, ok := mapParams[sourceKey].(string)
if !ok || sourceParam == "" { if !ok || sourceParam == "" {
return nil, util.NewAgentError(fmt.Sprintf("invalid or missing '%s' parameter", sourceKey), nil) return nil, fmt.Errorf("invalid or missing '%s' parameter", sourceKey)
} }
resp, err := source.ValidateRules(ctx, sourceParam) return source.ValidateRules(ctx, sourceParam)
if err != nil {
return nil, util.ProecessGcpError(err)
}
return resp, nil
} }
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) { func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,7 +17,6 @@ package tools
import ( import (
"context" "context"
"fmt" "fmt"
"net/http"
"slices" "slices"
"strings" "strings"
@@ -81,13 +80,13 @@ type AccessToken string
func (token AccessToken) ParseBearerToken() (string, error) { func (token AccessToken) ParseBearerToken() (string, error) {
headerParts := strings.Split(string(token), " ") headerParts := strings.Split(string(token), " ")
if len(headerParts) != 2 || strings.ToLower(headerParts[0]) != "bearer" { if len(headerParts) != 2 || strings.ToLower(headerParts[0]) != "bearer" {
return "", util.NewClientServerError("authorization header must be in the format 'Bearer <token>'", http.StatusUnauthorized, nil) return "", fmt.Errorf("authorization header must be in the format 'Bearer <token>': %w", util.ErrUnauthorized)
} }
return headerParts[1], nil return headerParts[1], nil
} }
type Tool interface { type Tool interface {
Invoke(context.Context, SourceProvider, parameters.ParamValues, AccessToken) (any, util.ToolboxError) Invoke(context.Context, SourceProvider, parameters.ParamValues, AccessToken) (any, error)
EmbedParams(context.Context, parameters.ParamValues, map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) EmbedParams(context.Context, parameters.ParamValues, map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error)
Manifest() Manifest Manifest() Manifest
McpManifest() McpManifest McpManifest() McpManifest

View File

@@ -1,122 +0,0 @@
// Copyright 2026 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// 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
//
// 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
// limitations under the License.
package util
import (
"errors"
"fmt"
"net/http"
"strings"
"google.golang.org/api/googleapi"
)
type ErrorCategory string
const (
CategoryAgent ErrorCategory = "AGENT_ERROR"
CategoryServer ErrorCategory = "SERVER_ERROR"
)
// ToolboxError is the interface all custom errors must satisfy
type ToolboxError interface {
error
Category() ErrorCategory
}
// Agent Errors return 200 to the sender
type AgentError struct {
Msg string
Cause error
}
func (e *AgentError) Error() string { return e.Msg }
func (e *AgentError) Category() ErrorCategory { return CategoryAgent }
func (e *AgentError) Unwrap() error { return e.Cause }
func NewAgentError(msg string, cause error) *AgentError {
return &AgentError{Msg: msg, Cause: cause}
}
// ClientServerError returns 4XX/5XX error code
type ClientServerError struct {
Msg string
Code int
Cause error
}
func (e *ClientServerError) Error() string { return fmt.Sprintf("%s: %v", e.Msg, e.Cause) }
func (e *ClientServerError) Category() ErrorCategory { return CategoryServer }
func (e *ClientServerError) Unwrap() error { return e.Cause }
func NewClientServerError(msg string, code int, cause error) *ClientServerError {
return &ClientServerError{Msg: msg, Code: code, Cause: cause}
}
// ProecessGcpError catches auth related errors and return 401/403 error codes
// Returns AgentError for all other errors
func ProecessGcpError(err error) ToolboxError {
var gErr *googleapi.Error
if errors.As(err, &gErr) {
if gErr.Code == 401 {
return NewClientServerError(
"failed to access GCP resource",
http.StatusUnauthorized,
err,
)
}
if gErr.Code == 403 {
return NewClientServerError(
"failed to access GCP resource",
http.StatusForbidden,
err,
)
}
}
return NewAgentError("error processing GCP request", err)
}
// ProcessGeneralError handles generic errors by inspecting the error string
// for common status code patterns.
func ProcessGeneralError(err error) ToolboxError {
if err == nil {
return nil
}
errStr := err.Error()
// Check for Unauthorized
if strings.Contains(errStr, "Error 401") || strings.Contains(errStr, "status 401") {
return NewClientServerError(
"failed to access resource",
http.StatusUnauthorized,
err,
)
}
// Check for Forbidden
if strings.Contains(errStr, "Error 403") || strings.Contains(errStr, "status 403") {
return NewClientServerError(
"failed to access resource",
http.StatusForbidden,
err,
)
}
// Default to AgentError for logical failures (task execution failed)
return NewAgentError("error processing request", err)
}

View File

@@ -19,7 +19,6 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"net/http"
"reflect" "reflect"
"regexp" "regexp"
"slices" "slices"
@@ -119,7 +118,7 @@ func parseFromAuthService(paramAuthServices []ParamAuthService, claimsMap map[st
} }
return v, nil return v, nil
} }
return nil, util.NewClientServerError("missing or invalid authentication header", http.StatusUnauthorized, nil) return nil, fmt.Errorf("missing or invalid authentication header: %w", util.ErrUnauthorized)
} }
// CheckParamRequired checks if a parameter is required based on the required and default field. // CheckParamRequired checks if a parameter is required based on the required and default field.

View File

@@ -17,6 +17,7 @@ import (
"bytes" "bytes"
"context" "context"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
@@ -187,3 +188,5 @@ func InstrumentationFromContext(ctx context.Context) (*telemetry.Instrumentation
} }
return nil, fmt.Errorf("unable to retrieve instrumentation") return nil, fmt.Errorf("unable to retrieve instrumentation")
} }
var ErrUnauthorized = errors.New("unauthorized")