Skip to content

Commit 409a354

Browse files
authored
feat(dataplex): improve dataplex params (#2855)
## Description feat(dataplex): refine dataplex tools for better agent usability Refines the parameters and descriptions for Dataplex tools to reduce redundancy and improve tool selection accuracy for AI agents. Key changes: - Removed the redundant 'name' parameter from 'lookup_entry' and 'lookup_context' tools. - Implemented automatic derivation of the parent resource path (project and location) from the 'entry' or 'resources' arguments. - Added validation logic to ensure provided resource names follow the correct format and that all resources in a batch request belong to the same project and location. - Enhanced tool and parameter descriptions across 'search_entries', 'lookup_entry', and 'lookup_context' to provide clearer instructions and search syntax examples. - Updated integration tests and YAML configurations to align with the new schema. ## 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>
1 parent f6678f8 commit 409a354

File tree

7 files changed

+86
-104
lines changed

7 files changed

+86
-104
lines changed

internal/prebuiltconfigs/tools/dataplex.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,19 @@ tools:
2121
search_entries:
2222
kind: dataplex-search-entries
2323
source: dataplex-source
24-
description: Use this tool to search for entries in Dataplex Catalog based on the provided search query.
24+
description: Searches for data assets (eg. table/dataset/view) in Catalog based on the provided search query.
2525
lookup_entry:
2626
kind: dataplex-lookup-entry
2727
source: dataplex-source
28-
description: Use this tool to retrieve a specific entry from Dataplex Catalog.
28+
description: Retrieves a specific metadata regarding a data asset (e.g. table/dataset/view) from Catalog
2929
search_aspect_types:
3030
kind: dataplex-search-aspect-types
3131
source: dataplex-source
32-
description: Use this tool to find aspect types relevant to the query.
32+
description: Search aspect types relevant to the query.
3333
lookup_context:
3434
kind: dataplex-lookup-context
3535
source: dataplex-source
36-
description: Use this tool to retrieve rich metadata regarding one or more data assets along with their relationships.
36+
description: Retrieves rich metadata regarding one or more data assets along with their relationships.
3737

3838
toolsets:
3939
discovery:

internal/tools/dataplex/dataplexlookupcontext/dataplexlookupcontext.go

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"fmt"
2020
"net/http"
21+
"strings"
2122

2223
"cloud.google.com/go/dataplex/apiv1/dataplexpb"
2324
"github.com/goccy/go-yaml"
@@ -65,9 +66,13 @@ func (cfg Config) ToolConfigType() string {
6566
}
6667

6768
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
68-
name := parameters.NewStringParameter("name", "The project to which the request should be attributed in the following form: projects/{project}/locations/{location}.")
69-
resources := parameters.NewArrayParameter("resources", "A list of up to 10 resources names for which metadata is needed.", parameters.NewStringParameter("resource", "Name of a resource in the following format: projects/{project}/locations/{location}/entryGroups/{group}/entries/{entry}."))
70-
params := parameters.Parameters{name, resources}
69+
resources := parameters.NewArrayParameter("resources",
70+
"Required. A list of up to 10 resource names from same project and location.",
71+
parameters.NewStringParameter("resource",
72+
"Name of a resource in the following format: projects/{project_id_or_number}/locations/{location}/entryGroups/{group}/entries/{entry}."+
73+
" Example for a BigQuery table: 'projects/{project_id_or_number}/locations/{location}/entryGroups/@bigquery/entries/bigquery.googleapis.com/projects/{project_id}/datasets/{dataset_id}/tables/{table_id}'."+
74+
" This is the same value which is returned by the search_entries tool's response in the dataplexEntry.name field."))
75+
params := parameters.Parameters{resources}
7176

7277
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, nil)
7378

@@ -102,12 +107,33 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
102107
}
103108

104109
paramsMap := params.AsMap()
105-
name, _ := paramsMap["name"].(string)
106110
resourcesSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["resources"].([]any), "string")
107111
if err != nil {
108112
return nil, util.NewAgentError(fmt.Sprintf("can't convert resources to array of strings: %s", err), err)
109113
}
110114
resources := resourcesSlice.([]string)
115+
116+
if len(resources) == 0 {
117+
err := fmt.Errorf("resources cannot be empty")
118+
return nil, util.NewAgentError(err.Error(), err)
119+
}
120+
var name string
121+
for i, resource := range resources {
122+
parts := strings.Split(resource, "/")
123+
if len(parts) < 4 || parts[0] != "projects" || parts[2] != "locations" {
124+
err := fmt.Errorf("invalid resource format at index %d, must be in the format of projects/{project_id_or_number}/locations/{location}/entryGroups/{group}/entries/{entry}", i)
125+
return nil, util.NewAgentError(err.Error(), err)
126+
}
127+
128+
currentName := strings.Join(parts[:4], "/")
129+
if i == 0 {
130+
name = currentName
131+
} else if name != currentName {
132+
err := fmt.Errorf("all resources must belong to the same project and location. Please make separate calls for each distinct project and location combination")
133+
return nil, util.NewAgentError(err.Error(), err)
134+
}
135+
}
136+
111137
resp, err := source.LookupContext(ctx, name, resources)
112138
if err != nil {
113139
return nil, util.ProcessGcpError(err)

internal/tools/dataplex/dataplexlookupcontext/dataplexlookupcontext_test.go

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"github.com/googleapis/genai-toolbox/internal/server"
2222
"github.com/googleapis/genai-toolbox/internal/testutils"
2323
"github.com/googleapis/genai-toolbox/internal/tools/dataplex/dataplexlookupcontext"
24-
"github.com/googleapis/genai-toolbox/internal/util/parameters"
2524
)
2625

2726
func TestParseFromYamlDataplexLookupContext(t *testing.T) {
@@ -53,40 +52,6 @@ func TestParseFromYamlDataplexLookupContext(t *testing.T) {
5352
},
5453
},
5554
},
56-
{
57-
desc: "advanced example",
58-
in: `
59-
kind: tool
60-
name: example_tool
61-
type: dataplex-lookup-context
62-
source: my-instance
63-
description: some description
64-
parameters:
65-
- name: name
66-
type: string
67-
description: some name description
68-
- name: resources
69-
type: array
70-
description: some resources description
71-
items:
72-
name: resource
73-
type: string
74-
description: some resource description
75-
`,
76-
want: server.ToolConfigs{
77-
"example_tool": dataplexlookupcontext.Config{
78-
Name: "example_tool",
79-
Type: "dataplex-lookup-context",
80-
Source: "my-instance",
81-
Description: "some description",
82-
AuthRequired: []string{},
83-
Parameters: []parameters.Parameter{
84-
parameters.NewStringParameter("name", "some name description"),
85-
parameters.NewArrayParameter("resources", "some resources description", parameters.NewStringParameter("resource", "some resource description")),
86-
},
87-
},
88-
},
89-
},
9055
}
9156
for _, tc := range tcs {
9257
t.Run(tc.desc, func(t *testing.T) {

internal/tools/dataplex/dataplexlookupentry/dataplexlookupentry.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"fmt"
2020
"net/http"
21+
"strings"
2122

2223
dataplexpb "cloud.google.com/go/dataplex/apiv1/dataplexpb"
2324
"github.com/goccy/go-yaml"
@@ -70,7 +71,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
7071
7172
**Type:** Integer
7273
73-
**Description:** Specifies the parts of the entry and its aspects to return.
74+
**Description:** Optional. Specifies the parts of the entry and its aspects to return.
7475
7576
**Possible Values:**
7677
@@ -80,11 +81,10 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
8081
* 4 (ALL): Return the entry and both required and optional aspects (at most 100 aspects)
8182
`
8283

83-
name := parameters.NewStringParameter("name", "The project to which the request should be attributed in the following form: projects/{project}/locations/{location}.")
8484
view := parameters.NewIntParameterWithDefault("view", 2, viewDesc)
85-
aspectTypes := parameters.NewArrayParameterWithDefault("aspectTypes", []any{}, "Limits the aspects returned to the provided aspect types. It only works when used together with CUSTOM view.", parameters.NewStringParameter("aspectType", "The types of aspects to be included in the response in the format `projects/{project}/locations/{location}/aspectTypes/{aspectType}`."))
86-
entry := parameters.NewStringParameter("entry", "The resource name of the Entry in the following form: projects/{project}/locations/{location}/entryGroups/{entryGroup}/entries/{entry}.")
87-
params := parameters.Parameters{name, view, aspectTypes, entry}
85+
aspectTypes := parameters.NewArrayParameterWithDefault("aspectTypes", []any{}, "Optional. Limits the aspects returned to the provided aspect types. It only works when used together with CUSTOM view.", parameters.NewStringParameter("aspectType", "The types of aspects to be included in the response in the format `projects/{project}/locations/{location}/aspectTypes/{aspectType}`."))
86+
entry := parameters.NewStringParameter("entry", "Required. The resource name of the Entry in the following form: projects/{project}/locations/{location}/entryGroups/{entryGroup}/entries/{entry}.")
87+
params := parameters.Parameters{entry, view, aspectTypes}
8888

8989
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, nil)
9090

@@ -119,13 +119,18 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
119119
}
120120

121121
paramsMap := params.AsMap()
122-
name, _ := paramsMap["name"].(string)
123122
entry, _ := paramsMap["entry"].(string)
124123
view, _ := paramsMap["view"].(int)
125124
aspectTypeSlice, err := parameters.ConvertAnySliceToTyped(paramsMap["aspectTypes"].([]any), "string")
126125
if err != nil {
127126
return nil, util.NewAgentError(fmt.Sprintf("can't convert aspectTypes to array of strings: %s", err), err)
128127
}
128+
parts := strings.Split(entry, "/")
129+
if len(parts) < 4 || parts[0] != "projects" || parts[2] != "locations" {
130+
err = fmt.Errorf("invalid entry format: must be in the form projects/{project}/locations/{location}/entryGroups/{entryGroup}/entries/{entry}")
131+
return nil, util.NewAgentError(err.Error(), err)
132+
}
133+
name := strings.Join(parts[:4], "/")
129134
aspectTypes := aspectTypeSlice.([]string)
130135
resp, err := source.LookupEntry(ctx, name, view, aspectTypes, entry)
131136
if err != nil {

internal/tools/dataplex/dataplexlookupentry/dataplexlookupentry_test.go

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ import (
2121
"github.com/googleapis/genai-toolbox/internal/server"
2222
"github.com/googleapis/genai-toolbox/internal/testutils"
2323
"github.com/googleapis/genai-toolbox/internal/tools/dataplex/dataplexlookupentry"
24-
"github.com/googleapis/genai-toolbox/internal/util/parameters"
2524
)
2625

2726
func TestParseFromYamlDataplexLookupEntry(t *testing.T) {
@@ -53,49 +52,6 @@ func TestParseFromYamlDataplexLookupEntry(t *testing.T) {
5352
},
5453
},
5554
},
56-
{
57-
desc: "advanced example",
58-
in: `
59-
kind: tool
60-
name: example_tool
61-
type: dataplex-lookup-entry
62-
source: my-instance
63-
description: some description
64-
parameters:
65-
- name: name
66-
type: string
67-
description: some name description
68-
- name: view
69-
type: string
70-
description: some view description
71-
- name: aspectTypes
72-
type: array
73-
description: some aspect types description
74-
default: []
75-
items:
76-
name: aspectType
77-
type: string
78-
description: some aspect type description
79-
- name: entry
80-
type: string
81-
description: some entry description
82-
`,
83-
want: server.ToolConfigs{
84-
"example_tool": dataplexlookupentry.Config{
85-
Name: "example_tool",
86-
Type: "dataplex-lookup-entry",
87-
Source: "my-instance",
88-
Description: "some description",
89-
AuthRequired: []string{},
90-
Parameters: []parameters.Parameter{
91-
parameters.NewStringParameter("name", "some name description"),
92-
parameters.NewStringParameter("view", "some view description"),
93-
parameters.NewArrayParameterWithDefault("aspectTypes", []any{}, "some aspect types description", parameters.NewStringParameter("aspectType", "some aspect type description")),
94-
parameters.NewStringParameter("entry", "some entry description"),
95-
},
96-
},
97-
},
98-
},
9955
}
10056
for _, tc := range tcs {
10157
t.Run(tc.desc, func(t *testing.T) {

internal/tools/dataplex/dataplexsearchentries/dataplexsearchentries.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@ func (cfg Config) ToolConfigType() string {
6464
}
6565

6666
func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error) {
67-
query := parameters.NewStringParameter("query", "The query against which entries in scope should be matched.")
67+
query := parameters.NewStringParameter("query",
68+
"A query string for searching entries, following Dataplex search syntax. "+
69+
"Supports logical operators (AND, OR, NOT) and grouping. "+
70+
"For example, to find a table that might have been renamed, you could use 'type:table (name:books OR fiction)'. "+
71+
"This can be more efficient than multiple separate calls."+
72+
"Warning: Performing broad searches without specific filters (e.g., type:table) can be slow and consume significant resources. When performing exploratory searches, always use the pageSize parameter to limit the number of results returned.")
6873
scope := parameters.NewStringParameterWithDefault("scope", "", "A scope limits the search space to a particular project or organization. It must be in the format: organizations/<org_id> or projects/<project_id> or projects/<project_number>.")
6974
pageSize := parameters.NewIntParameterWithDefault("pageSize", 5, "Number of results in the search page.")
7075
orderBy := parameters.NewStringParameterWithDefault("orderBy", "relevance", "Specifies the ordering of results. Supported values are: relevance, last_modified_timestamp, last_modified_timestamp asc")

0 commit comments

Comments
 (0)