Skip to content

Commit 7397643

Browse files
authored
Merge branch 'main' into cross-comp
2 parents 50a3a66 + cfd4b18 commit 7397643

File tree

92 files changed

+3182
-236
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

92 files changed

+3182
-236
lines changed

.ci/integration.cloudbuild.yaml

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,47 @@ steps:
3434
path: "/gopath"
3535
script: |
3636
go test -c -race -cover \
37-
-coverpkg=./internal/sources/...,./internal/tools/... ./tests/...
37+
-coverpkg=./internal/sources/...,./internal/tools/... \
38+
$(go list ./tests/... | grep -v '/tests/prompts')
3839
chmod +x .ci/test_with_coverage.sh
3940
41+
- id: "compile-prompt-test-binary"
42+
name: golang:1
43+
waitFor: ["install-dependencies"]
44+
env:
45+
- "GOPATH=/gopath"
46+
volumes:
47+
- name: "go"
48+
path: "/gopath"
49+
script: |
50+
for dir in ./tests/prompts/*; do
51+
if [ -d "$dir" ]; then
52+
PROMPT_TYPE=$(basename "$dir")
53+
echo "--- Compiling prompt test for ${PROMPT_TYPE} with targeted coverage ---"
54+
55+
go test -c -race -cover \
56+
-coverpkg=./internal/prompts/... \
57+
-o "prompt.${PROMPT_TYPE}.test" \
58+
"${dir}"
59+
fi
60+
done
61+
62+
chmod +x .ci/test_prompts_with_coverage.sh
63+
64+
- id: "prompts-custom"
65+
name: golang:1
66+
waitFor: ["compile-prompt-test-binary"]
67+
entrypoint: /bin/bash
68+
env:
69+
- "GOPATH=/gopath"
70+
volumes:
71+
- name: "go"
72+
path: "/gopath"
73+
args:
74+
- -c
75+
- |
76+
.ci/test_prompts_with_coverage.sh "custom"
77+
4078
- id: "cloud-sql-pg"
4179
name: golang:1
4280
waitFor: ["compile-test-binary"]

.ci/test_prompts_with_coverage.sh

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/bin/bash
2+
# .ci/test_prompts_with_coverage.sh
3+
#
4+
# This script runs a specific prompt integration test, calculates its
5+
# code coverage, and checks if it meets a minimum threshold.
6+
#
7+
# It is called with one argument: the type of the prompt.
8+
# Example usage: .ci/test_prompts_with_coverage.sh "custom"
9+
10+
# Exit immediately if a command fails.
11+
set -e
12+
13+
# --- 1. Define Variables ---
14+
15+
# The first argument is the prompt type (e.g., "custom").
16+
PROMPT_TYPE=$1
17+
COVERAGE_THRESHOLD=80 # Minimum coverage percentage required.
18+
19+
if [ -z "$PROMPT_TYPE" ]; then
20+
echo "Error: No prompt type provided. Please call this script with an argument."
21+
echo "Usage: .ci/test_prompts_with_coverage.sh <prompt_type>"
22+
exit 1
23+
fi
24+
25+
# Construct names based on the prompt type.
26+
TEST_BINARY="./prompt.${PROMPT_TYPE}.test"
27+
TEST_NAME="$(tr '[:lower:]' '[:upper:]' <<< ${PROMPT_TYPE:0:1})${PROMPT_TYPE:1} Prompts"
28+
COVERAGE_FILE="coverage.prompts-${PROMPT_TYPE}.out"
29+
30+
31+
# --- 2. Run Integration Tests ---
32+
33+
echo "--- Running integration tests for ${TEST_NAME} ---"
34+
35+
# Safety check for the binary's existence.
36+
if [ ! -f "$TEST_BINARY" ]; then
37+
echo "Error: Test binary not found at ${TEST_BINARY}. Aborting."
38+
exit 1
39+
fi
40+
41+
# Execute the test binary and generate the coverage file.
42+
# If the tests fail, the 'set -e' command will cause the script to exit here.
43+
if ! ./"${TEST_BINARY}" -test.v -test.coverprofile="${COVERAGE_FILE}"; then
44+
echo "Error: Tests for ${TEST_NAME} failed. Exiting."
45+
exit 1
46+
fi
47+
48+
echo "--- Tests for ${TEST_NAME} passed successfully ---"
49+
50+
51+
# --- 3. Calculate and Check Coverage ---
52+
53+
echo "Calculating coverage for ${TEST_NAME}..."
54+
55+
# Calculate the total coverage percentage from the generated file.
56+
# The '2>/dev/null' suppresses warnings if the coverage file is empty.
57+
total_coverage=$(go tool cover -func="${COVERAGE_FILE}" 2>/dev/null | grep "total:" | awk '{print $3}')
58+
59+
if [ -z "$total_coverage" ]; then
60+
echo "Warning: Could not calculate coverage for ${TEST_NAME}. The coverage report might be empty."
61+
total_coverage="0%"
62+
fi
63+
64+
echo "${TEST_NAME} total coverage: $total_coverage"
65+
66+
# Remove the '%' sign for numerical comparison.
67+
coverage_numeric=$(echo "$total_coverage" | sed 's/%//')
68+
69+
# Check if the coverage is below the defined threshold.
70+
if awk -v coverage="$coverage_numeric" -v threshold="$COVERAGE_THRESHOLD" 'BEGIN {exit !(coverage < threshold)}'; then
71+
echo "Coverage failure: ${TEST_NAME} total coverage (${total_coverage}) is below the ${COVERAGE_THRESHOLD}% threshold."
72+
exit 1
73+
else
74+
echo "Coverage for ${TEST_NAME} is sufficient."
75+
fi

.github/workflows/tests.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,12 @@ jobs:
8181
run: |
8282
source_dir="./internal/sources/*"
8383
tool_dir="./internal/tools/*"
84+
prompt_dir="./internal/prompts/*"
8485
auth_dir="./internal/auth/*"
8586
int_test_dir="./tests/*"
86-
included_packages=$(go list ./... | grep -v -e "$source_dir" -e "$tool_dir" -e "$auth_dir" -e "$int_test_dir")
87+
included_packages=$(go list ./... | grep -v -e "$source_dir" -e "$tool_dir" -e "$prompt_dir" -e "$auth_dir" -e "$int_test_dir")
8788
go test -race -cover -coverprofile=coverage.out -v $included_packages
88-
go test -race -v ./internal/sources/... ./internal/tools/... ./internal/auth/...
89+
go test -race -v ./internal/sources/... ./internal/tools/... ./internal/prompts/... ./internal/auth/...
8990
9091
- name: Run tests without coverage
9192
if: ${{ runner.os != 'Linux' }}

CHANGELOG.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
# Changelog
22

3-
## [0.19.0](https://github.com/googleapis/genai-toolbox/compare/v0.18.0...v0.19.0) (2025-11-07)
3+
## [0.19.1](https://github.com/googleapis/genai-toolbox/compare/v0.18.0...v0.19.1) (2025-11-07)
44

55

66
### ⚠ BREAKING CHANGES
77

88
* **tools/alloydbainl:** update AlloyDB AI NL statement order ([#1753](https://github.com/googleapis/genai-toolbox/issues/1753))
9+
* **tools/bigquery-analyze-contribution:** Add allowed dataset support ([#1675](https://github.com/googleapis/genai-toolbox/issues/1675)) ([ef28e39](https://github.com/googleapis/genai-toolbox/commit/ef28e39e90b21287ca8e69b99f4e792c78e9d31f))
910
* **tools/bigquery-get-dataset-info:** add allowed dataset support ([#1654](https://github.com/googleapis/genai-toolbox/issues/1654))
1011

1112
### Features
@@ -26,9 +27,9 @@
2627
* **tools/neo4j-execute-cypher:** Add dry_run parameter to validate Cypher queries ([#1769](https://github.com/googleapis/genai-toolbox/issues/1769)) ([f475da6](https://github.com/googleapis/genai-toolbox/commit/f475da63ce1b65387b503ac497eca47635452723))
2728
* **tools/postgres-list-schemas:** Add new postgres-list-schemas tool ([#1741](https://github.com/googleapis/genai-toolbox/issues/1741)) ([1a19cac](https://github.com/googleapis/genai-toolbox/commit/1a19cac7cd89ed70291eb55e190370fe7b2c1aba))
2829
* **tools/postgres-list-views:** Add new postgres-list-views tool ([#1709](https://github.com/googleapis/genai-toolbox/issues/1709)) ([e8c7fe0](https://github.com/googleapis/genai-toolbox/commit/e8c7fe0994fedcb9be78d461fab3c98cc6bd86b2))
29-
* **tools/serverless-spark:** Add cancel-batch tool ([2881683](https://github.com/googleapis/genai-toolbox/commit/28816832265250de97d84e6ba38bf6c35e040796))
30-
* **tools/serverless-spark:** Add get_batch tool ([7ad1072](https://github.com/googleapis/genai-toolbox/commit/7ad10720b4638324cd77d8aa410cbd1ccf0cc93f))
31-
* **tools/serverless-spark:** Add serverless-spark source with list_batches tool ([816dbce](https://github.com/googleapis/genai-toolbox/commit/816dbce268392046e54767732bd31488c6e89bdb))
30+
* **tools/serverless-spark:** Add cancel-batch tool ([#1827](https://github.com/googleapis/genai-toolbox/pull/1827))([2881683](https://github.com/googleapis/genai-toolbox/commit/28816832265250de97d84e6ba38bf6c35e040796))
31+
* **tools/serverless-spark:** Add get_batch tool ([#1783](https://github.com/googleapis/genai-toolbox/pull/1783))([7ad1072](https://github.com/googleapis/genai-toolbox/commit/7ad10720b4638324cd77d8aa410cbd1ccf0cc93f))
32+
* **tools/serverless-spark:** Add serverless-spark source with list_batches tool ([#1690](https://github.com/googleapis/genai-toolbox/pull/1690))([816dbce](https://github.com/googleapis/genai-toolbox/commit/816dbce268392046e54767732bd31488c6e89bdb))
3233

3334

3435
### Bug Fixes

README.md

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ documentation](https://googleapis.github.io/genai-toolbox/).
3939
- [Sources](#sources)
4040
- [Tools](#tools)
4141
- [Toolsets](#toolsets)
42+
- [Prompts](#prompts)
4243
- [Versioning](#versioning)
4344
- [Pre-1.0.0 Versioning](#pre-100-versioning)
4445
- [Post-1.0.0 Versioning](#post-100-versioning)
@@ -124,7 +125,7 @@ To install Toolbox as a binary:
124125
>
125126
> ```sh
126127
> # see releases page for other versions
127-
> export VERSION=0.18.0
128+
> export VERSION=0.19.1
128129
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/linux/amd64/toolbox
129130
> chmod +x toolbox
130131
> ```
@@ -137,7 +138,7 @@ To install Toolbox as a binary:
137138
>
138139
> ```sh
139140
> # see releases page for other versions
140-
> export VERSION=0.18.0
141+
> export VERSION=0.19.1
141142
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/arm64/toolbox
142143
> chmod +x toolbox
143144
> ```
@@ -150,7 +151,7 @@ To install Toolbox as a binary:
150151
>
151152
> ```sh
152153
> # see releases page for other versions
153-
> export VERSION=0.18.0
154+
> export VERSION=0.19.1
154155
> curl -L -o toolbox https://storage.googleapis.com/genai-toolbox/v$VERSION/darwin/amd64/toolbox
155156
> chmod +x toolbox
156157
> ```
@@ -163,7 +164,7 @@ To install Toolbox as a binary:
163164
>
164165
> ```powershell
165166
> # see releases page for other versions
166-
> $VERSION = "0.18.0"
167+
> $VERSION = "0.19.1"
167168
> Invoke-WebRequest -Uri "https://storage.googleapis.com/genai-toolbox/v$VERSION/windows/amd64/toolbox.exe" -OutFile "toolbox.exe"
168169
> ```
169170
>
@@ -176,7 +177,7 @@ You can also install Toolbox as a container:
176177
177178
```sh
178179
# see releases page for other versions
179-
export VERSION=0.18.0
180+
export VERSION=0.19.1
180181
docker pull us-central1-docker.pkg.dev/database-toolbox/toolbox/toolbox:$VERSION
181182
```
182183
@@ -200,7 +201,7 @@ To install from source, ensure you have the latest version of
200201
[Go installed](https://go.dev/doc/install), and then run the following command:
201202

202203
```sh
203-
go install github.com/googleapis/genai-toolbox@v0.18.0
204+
go install github.com/googleapis/genai-toolbox@v0.19.1
204205
```
205206
<!-- {x-release-please-end} -->
206207

@@ -932,6 +933,25 @@ all_tools = client.load_toolset()
932933
my_second_toolset = client.load_toolset("my_second_toolset")
933934
```
934935
936+
### Prompts
937+
938+
The `prompts` section of a `tools.yaml` defines prompts that can be used for
939+
interactions with LLMs.
940+
941+
```yaml
942+
prompts:
943+
code_review:
944+
description: "Asks the LLM to analyze code quality and suggest improvements."
945+
messages:
946+
- content: "Please review the following code for quality, correctness, and potential improvements: \n\n{{.code}}"
947+
arguments:
948+
- name: "code"
949+
description: "The code to review"
950+
```
951+
952+
For more details on configuring prompts, see the
953+
[Prompts](https://googleapis.github.io/genai-toolbox/resources/prompts).
954+
935955
## Versioning
936956
937957
This project uses [semantic versioning](https://semver.org/) (`MAJOR.MINOR.PATCH`).

cmd/root.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,16 @@ import (
3535
"github.com/googleapis/genai-toolbox/internal/auth"
3636
"github.com/googleapis/genai-toolbox/internal/log"
3737
"github.com/googleapis/genai-toolbox/internal/prebuiltconfigs"
38+
"github.com/googleapis/genai-toolbox/internal/prompts"
3839
"github.com/googleapis/genai-toolbox/internal/server"
3940
"github.com/googleapis/genai-toolbox/internal/sources"
4041
"github.com/googleapis/genai-toolbox/internal/telemetry"
4142
"github.com/googleapis/genai-toolbox/internal/tools"
4243
"github.com/googleapis/genai-toolbox/internal/util"
4344

45+
// Import prompt packages for side effect of registration
46+
_ "github.com/googleapis/genai-toolbox/internal/prompts/custom"
47+
4448
// Import tool packages for side effect of registration
4549
_ "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreatecluster"
4650
_ "github.com/googleapis/genai-toolbox/internal/tools/alloydb/alloydbcreateinstance"
@@ -360,12 +364,13 @@ type ToolsFile struct {
360364
AuthServices server.AuthServiceConfigs `yaml:"authServices"`
361365
Tools server.ToolConfigs `yaml:"tools"`
362366
Toolsets server.ToolsetConfigs `yaml:"toolsets"`
367+
Prompts server.PromptConfigs `yaml:"prompts"`
363368
}
364369

365370
// parseEnv replaces environment variables ${ENV_NAME} with their values.
366371
// also support ${ENV_NAME:default_value}.
367372
func parseEnv(input string) (string, error) {
368-
re := regexp.MustCompile(`\$\{(\w+)(:(\w*))?\}`)
373+
re := regexp.MustCompile(`\$\{(\w+)(:([^}]*))?\}`)
369374

370375
var err error
371376
output := re.ReplaceAllStringFunc(input, func(match string) string {
@@ -376,7 +381,7 @@ func parseEnv(input string) (string, error) {
376381
if value, found := os.LookupEnv(variableName); found {
377382
return value
378383
}
379-
if parts[2] != "" {
384+
if len(parts) >= 4 && parts[2] != "" {
380385
return parts[3]
381386
}
382387
err = fmt.Errorf("environment variable not found: %q", variableName)
@@ -412,6 +417,7 @@ func mergeToolsFiles(files ...ToolsFile) (ToolsFile, error) {
412417
AuthServices: make(server.AuthServiceConfigs),
413418
Tools: make(server.ToolConfigs),
414419
Toolsets: make(server.ToolsetConfigs),
420+
Prompts: make(server.PromptConfigs),
415421
}
416422

417423
var conflicts []string
@@ -461,11 +467,20 @@ func mergeToolsFiles(files ...ToolsFile) (ToolsFile, error) {
461467
merged.Toolsets[name] = toolset
462468
}
463469
}
470+
471+
// Check for conflicts and merge prompts
472+
for name, prompt := range file.Prompts {
473+
if _, exists := merged.Prompts[name]; exists {
474+
conflicts = append(conflicts, fmt.Sprintf("prompt '%s' (file #%d)", name, fileIndex+1))
475+
} else {
476+
merged.Prompts[name] = prompt
477+
}
478+
}
464479
}
465480

466481
// If conflicts were detected, return an error
467482
if len(conflicts) > 0 {
468-
return ToolsFile{}, fmt.Errorf("resource conflicts detected:\n - %s\n\nPlease ensure each source, authService, tool, and toolset has a unique name across all files", strings.Join(conflicts, "\n - "))
483+
return ToolsFile{}, fmt.Errorf("resource conflicts detected:\n - %s\n\nPlease ensure each source, authService, tool, toolset and prompt has a unique name across all files", strings.Join(conflicts, "\n - "))
469484
}
470485

471486
return merged, nil
@@ -539,22 +554,22 @@ func handleDynamicReload(ctx context.Context, toolsFile ToolsFile, s *server.Ser
539554
panic(err)
540555
}
541556

542-
sourcesMap, authServicesMap, toolsMap, toolsetsMap, err := validateReloadEdits(ctx, toolsFile)
557+
sourcesMap, authServicesMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap, err := validateReloadEdits(ctx, toolsFile)
543558
if err != nil {
544559
errMsg := fmt.Errorf("unable to validate reloaded edits: %w", err)
545560
logger.WarnContext(ctx, errMsg.Error())
546561
return err
547562
}
548563

549-
s.ResourceMgr.SetResources(sourcesMap, authServicesMap, toolsMap, toolsetsMap)
564+
s.ResourceMgr.SetResources(sourcesMap, authServicesMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap)
550565

551566
return nil
552567
}
553568

554569
// validateReloadEdits checks that the reloaded tools file configs can initialized without failing
555570
func validateReloadEdits(
556571
ctx context.Context, toolsFile ToolsFile,
557-
) (map[string]sources.Source, map[string]auth.AuthService, map[string]tools.Tool, map[string]tools.Toolset, error,
572+
) (map[string]sources.Source, map[string]auth.AuthService, map[string]tools.Tool, map[string]tools.Toolset, map[string]prompts.Prompt, map[string]prompts.Promptset, error,
558573
) {
559574
logger, err := util.LoggerFromContext(ctx)
560575
if err != nil {
@@ -577,16 +592,17 @@ func validateReloadEdits(
577592
AuthServiceConfigs: toolsFile.AuthServices,
578593
ToolConfigs: toolsFile.Tools,
579594
ToolsetConfigs: toolsFile.Toolsets,
595+
PromptConfigs: toolsFile.Prompts,
580596
}
581597

582-
sourcesMap, authServicesMap, toolsMap, toolsetsMap, err := server.InitializeConfigs(ctx, reloadedConfig)
598+
sourcesMap, authServicesMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap, err := server.InitializeConfigs(ctx, reloadedConfig)
583599
if err != nil {
584600
errMsg := fmt.Errorf("unable to initialize reloaded configs: %w", err)
585601
logger.WarnContext(ctx, errMsg.Error())
586-
return nil, nil, nil, nil, err
602+
return nil, nil, nil, nil, nil, nil, err
587603
}
588604

589-
return sourcesMap, authServicesMap, toolsMap, toolsetsMap, nil
605+
return sourcesMap, authServicesMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap, nil
590606
}
591607

592608
// watchChanges checks for changes in the provided yaml tools file(s) or folder.
@@ -877,7 +893,8 @@ func run(cmd *Command) error {
877893
}
878894
}
879895

880-
cmd.cfg.SourceConfigs, cmd.cfg.AuthServiceConfigs, cmd.cfg.ToolConfigs, cmd.cfg.ToolsetConfigs = toolsFile.Sources, toolsFile.AuthServices, toolsFile.Tools, toolsFile.Toolsets
896+
cmd.cfg.SourceConfigs, cmd.cfg.AuthServiceConfigs, cmd.cfg.ToolConfigs, cmd.cfg.ToolsetConfigs, cmd.cfg.PromptConfigs = toolsFile.Sources, toolsFile.AuthServices, toolsFile.Tools, toolsFile.Toolsets, toolsFile.Prompts
897+
881898
authSourceConfigs := toolsFile.AuthSources
882899
if authSourceConfigs != nil {
883900
cmd.logger.WarnContext(ctx, "`authSources` is deprecated, use `authServices` instead")

0 commit comments

Comments
 (0)