mirror of
https://github.com/googleapis/genai-toolbox.git
synced 2026-02-19 03:14:29 -05:00
Compare commits
44 Commits
dependabot
...
adk-js-pro
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9d1f8efbbd | ||
|
|
d6af2907fd | ||
|
|
ed1d448897 | ||
|
|
d68ebe1ed2 | ||
|
|
0cd854db74 | ||
|
|
f74bae8ff6 | ||
|
|
a0df7dfa59 | ||
|
|
9554ee0277 | ||
|
|
ff1ff4a534 | ||
|
|
49b53c83ce | ||
|
|
c7e41262a0 | ||
|
|
5e3b8d3672 | ||
|
|
4ff4f258aa | ||
|
|
5fe9984661 | ||
|
|
a212aedd19 | ||
|
|
9210e5555c | ||
|
|
b43af71793 | ||
|
|
da1f463dd1 | ||
|
|
3265f7e3a6 | ||
|
|
336743f747 | ||
|
|
911069ae8d | ||
|
|
cee59d52c3 | ||
|
|
9517daba09 | ||
|
|
3c61ee0597 | ||
|
|
19271eb9ee | ||
|
|
3a150c77ca | ||
|
|
ca6f31a192 | ||
|
|
d7faf7700f | ||
|
|
37a60ea2a6 | ||
|
|
8de16976ae | ||
|
|
49cb2f39f7 | ||
|
|
f169874e53 | ||
|
|
db8c3a3c77 | ||
|
|
8b33b0c67f | ||
|
|
35fa73516b | ||
|
|
66df3bfd21 | ||
|
|
73e0edc3cd | ||
|
|
3f32a9aab6 | ||
|
|
28006fc9b2 | ||
|
|
56c69131b4 | ||
|
|
ad4a509340 | ||
|
|
d39acac96c | ||
|
|
6df2ad28a9 | ||
|
|
8416378613 |
@@ -4,8 +4,8 @@ linkTitle: "Redis"
|
||||
type: docs
|
||||
weight: 1
|
||||
description: >
|
||||
Redis is a in-memory data structure store.
|
||||
|
||||
Redis is a in-memory data structure store.
|
||||
|
||||
---
|
||||
|
||||
## About
|
||||
@@ -44,6 +44,9 @@ password: ${MY_AUTH_STRING} # Omit this field if you don't have a password.
|
||||
# database: 0
|
||||
# clusterEnabled: false
|
||||
# useGCPIAM: false
|
||||
# tls:
|
||||
# enabled: false
|
||||
# insecureSkipVerify: false
|
||||
```
|
||||
|
||||
{{< notice tip >}}
|
||||
@@ -61,7 +64,7 @@ Here is an example tools.yaml config with [AUTH][auth] enabled:
|
||||
```yaml
|
||||
kind: sources
|
||||
name: my-redis-cluster-instance
|
||||
type: memorystore-redis
|
||||
type: redis
|
||||
address:
|
||||
- 127.0.0.1:6379
|
||||
password: ${MY_AUTH_STRING}
|
||||
@@ -78,7 +81,7 @@ using IAM authentication:
|
||||
```yaml
|
||||
kind: sources
|
||||
name: my-redis-cluster-instance
|
||||
type: memorystore-redis
|
||||
type: redis
|
||||
address:
|
||||
- 127.0.0.1:6379
|
||||
useGCPIAM: true
|
||||
@@ -89,14 +92,16 @@ clusterEnabled: true
|
||||
|
||||
## Reference
|
||||
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|----------------|:--------:|:------------:|---------------------------------------------------------------------------------------------------------------------------------|
|
||||
| type | string | true | Must be "memorystore-redis". |
|
||||
| address | string | true | Primary endpoint for the Memorystore Redis instance to connect to. |
|
||||
| username | string | false | If you are using a non-default user, specify the user name here. If you are using Memorystore for Redis, leave this field blank |
|
||||
| password | string | false | If you have [Redis AUTH][auth] enabled, specify the AUTH string here |
|
||||
| database | int | false | The Redis database to connect to. Not applicable for cluster enabled instances. The default database is `0`. |
|
||||
| clusterEnabled | bool | false | Set it to `true` if using a Redis Cluster instance. Defaults to `false`. |
|
||||
| useGCPIAM | string | false | Set it to `true` if you are using GCP's IAM authentication. Defaults to `false`. |
|
||||
| **field** | **type** | **required** | **description** |
|
||||
|------------------------|:--------:|:------------:|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| type | string | true | Must be "redis". |
|
||||
| address | string | true | Primary endpoint for the Memorystore Redis instance to connect to. |
|
||||
| username | string | false | If you are using a non-default user, specify the user name here. If you are using Memorystore for Redis, leave this field blank |
|
||||
| password | string | false | If you have [Redis AUTH][auth] enabled, specify the AUTH string here |
|
||||
| database | int | false | The Redis database to connect to. Not applicable for cluster enabled instances. The default database is `0`. |
|
||||
| tls.enabled | bool | false | Set it to `true` to enable TLS for the Redis connection. Defaults to `false`. |
|
||||
| tls.insecureSkipVerify | bool | false | Set it to `true` to skip TLS certificate verification. **Warning:** This is insecure and not recommended for production. Defaults to `false`. |
|
||||
| clusterEnabled | bool | false | Set it to `true` if using a Redis Cluster instance. Defaults to `false`. |
|
||||
| useGCPIAM | bool | false | Set it to `true` if you are using GCP's IAM authentication. Defaults to `false`. |
|
||||
|
||||
[auth]: https://cloud.google.com/memorystore/docs/redis/about-redis-auth
|
||||
|
||||
4
docs/en/samples/pre_post_processing/golden.txt
Normal file
4
docs/en/samples/pre_post_processing/golden.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
Final Client Response:
|
||||
AI:
|
||||
Loyalty Points
|
||||
POLICY CHECK: Intercepting 'update-hotel'
|
||||
@@ -16,10 +16,18 @@ This guide demonstrates how to implement these patterns in your Toolbox applicat
|
||||
|
||||
{{< tabpane persist=header >}}
|
||||
{{% tab header="ADK" text=true %}}
|
||||
Coming soon.
|
||||
The following example demonstrates how to use the `beforeToolCallback` and `afterToolCallback` hooks in the ADK `LlmAgent` to implement pre and post processing logic.
|
||||
|
||||
```js
|
||||
{{< include "js/adk/agent.js" >}}
|
||||
```
|
||||
|
||||
You can also add model-level (`beforeModelCallback`, `afterModelCallback`) and agent-level (`beforeAgentCallback`, `afterAgentCallback`) hooks to intercept messages at different stages of the execution loop.
|
||||
|
||||
For more information, see the [ADK Callbacks documentation](https://google.github.io/adk-docs/callbacks/types-of-callbacks/).
|
||||
{{% /tab %}}
|
||||
{{% tab header="Langchain" text=true %}}
|
||||
The following example demonstrates how to use `ToolboxClient` with LangChain's middleware to implement pre- and post- processing for tool calls.
|
||||
The following example demonstrates how to use `ToolboxClient` with LangChain's middleware to implement pre and post processing for tool calls.
|
||||
|
||||
```js
|
||||
{{< include "js/langchain/agent.js" >}}
|
||||
|
||||
103
docs/en/samples/pre_post_processing/js/adk/agent.js
Normal file
103
docs/en/samples/pre_post_processing/js/adk/agent.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import { InMemoryRunner, LlmAgent, LogLevel } from '@google/adk';
|
||||
import { ToolboxClient } from '@toolbox-sdk/adk';
|
||||
|
||||
process.env.GOOGLE_GENAI_API_KEY = process.env.GOOGLE_API_KEY || 'your-api-key'; // Replace it with your API key
|
||||
|
||||
const systemPrompt = `
|
||||
You're a helpful hotel assistant. You handle hotel searching, booking and
|
||||
cancellations. When the user searches for a hotel, mention it's name, id,
|
||||
location and price tier. Always mention hotel ids while performing any
|
||||
searches. This is very important for any operations. For any bookings or
|
||||
cancellations, please provide the appropriate confirmation. Be sure to
|
||||
update checkin or checkout dates if mentioned by the user.
|
||||
Don't ask for confirmations from the user.
|
||||
`;
|
||||
|
||||
// Pre-Processing
|
||||
function enforeBusinessRules({tool, args}) {
|
||||
const name = tool.name;
|
||||
console.log(`POLICY CHECK: Intercepting '${name}'`);
|
||||
|
||||
if (name === "update-hotel" && args.checkin_date && args.checkout_date) {
|
||||
try {
|
||||
const start = new Date(args.checkin_date);
|
||||
const end = new Date(args.checkout_date);
|
||||
const duration = (end - start) / (1000 * 60 * 60 * 24); // days
|
||||
|
||||
if (duration > 14) {
|
||||
console.log("BLOCKED: Stay too long");
|
||||
return "Error: Maximum stay duration is 14 days.";
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore invalid dates
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Post-Processing
|
||||
function enrichResponse({tool, response}) {
|
||||
const name = tool.name;
|
||||
console.log(`ENRICHING RESPONSE: Intercepting '${name}'`);
|
||||
if (name === "book-hotel") {
|
||||
let content = response;
|
||||
if (response && typeof response === "object") {
|
||||
content = response.content;
|
||||
}
|
||||
if (typeof content === "string" && !content.includes("Error")) {
|
||||
const loyaltyBonus = 500;
|
||||
const enrichedContent = `Booking Confirmed!\n You earned ${loyaltyBonus} Loyalty Points with this stay.\n\nSystem Details: ${content}`;
|
||||
|
||||
if (response && typeof response === "object") {
|
||||
return { ...response, content: enrichedContent };
|
||||
}
|
||||
return enrichedContent;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
async function runTurn(runner, userId, sessionId, prompt) {
|
||||
console.log(`\nUSER: '${prompt}'`);
|
||||
const content = { role: 'user', parts: [{ text: prompt }] };
|
||||
const stream = runner.runAsync({ userId, sessionId, newMessage: content });
|
||||
|
||||
let fullText = "";
|
||||
for await (const chunk of stream) {
|
||||
if (chunk.content && chunk.content.parts) {
|
||||
fullText += chunk.content.parts.map(p => p.text || "").join("");
|
||||
}
|
||||
}
|
||||
|
||||
console.log("-".repeat(50));
|
||||
console.log(`AI: ${fullText}`);
|
||||
}
|
||||
|
||||
export async function main() {
|
||||
const userId = 'test_user';
|
||||
const client = new ToolboxClient('http://127.0.0.1:5000');
|
||||
const tools = await client.loadToolset("my-toolset");
|
||||
|
||||
const rootAgent = new LlmAgent({
|
||||
name: 'hotel_agent',
|
||||
model: 'gemini-2.5-flash',
|
||||
description: 'Agent for hotel bookings and administration.',
|
||||
instruction: systemPrompt,
|
||||
tools: tools,
|
||||
// Add any pre- and post- processing callbacks
|
||||
beforeToolCallback: enforeBusinessRules,
|
||||
afterToolCallback: enrichResponse
|
||||
});
|
||||
|
||||
const appName = rootAgent.name;
|
||||
const runner = new InMemoryRunner({ agent: rootAgent, appName, logLevel: LogLevel.ERROR });
|
||||
const session = await runner.sessionService.createSession({ appName, userId });
|
||||
|
||||
// Turn 1: Booking
|
||||
await runTurn(runner, userId, session.id, "Book hotel with id 3.");
|
||||
|
||||
// Turn 2: Policy Violation
|
||||
await runTurn(runner, userId, session.id, "Update my hotel with id 3 with checkin date 2025-01-18 and checkout date 2025-02-10");
|
||||
}
|
||||
|
||||
main();
|
||||
3728
docs/en/samples/pre_post_processing/js/adk/package-lock.json
generated
Normal file
3728
docs/en/samples/pre_post_processing/js/adk/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
16
docs/en/samples/pre_post_processing/js/adk/package.json
Normal file
16
docs/en/samples/pre_post_processing/js/adk/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "adk",
|
||||
"version": "1.0.0",
|
||||
"description": "ADK.js sample for pre/post processing",
|
||||
"type": "module",
|
||||
"main": "agent.js",
|
||||
"scripts": {
|
||||
"start": "node agent.js"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@google/adk": "^0.2.0",
|
||||
"@toolbox-sdk/adk": "^0.2.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
# Empty init for package resolution
|
||||
2
go.mod
2
go.mod
@@ -94,7 +94,7 @@ require (
|
||||
cloud.google.com/go/monitoring v1.24.3 // indirect
|
||||
cloud.google.com/go/trace v1.11.7 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.1 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
|
||||
github.com/99designs/keyring v1.2.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
||||
|
||||
3
go.sum
3
go.sum
@@ -639,9 +639,8 @@ cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcP
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
filippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=
|
||||
filippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
|
||||
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
|
||||
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
|
||||
|
||||
@@ -15,6 +15,7 @@ package redis
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
@@ -44,14 +45,20 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Type string `yaml:"type" validate:"required"`
|
||||
Address []string `yaml:"address" validate:"required"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
Database int `yaml:"database"`
|
||||
UseGCPIAM bool `yaml:"useGCPIAM"`
|
||||
ClusterEnabled bool `yaml:"clusterEnabled"`
|
||||
Name string `yaml:"name" validate:"required"`
|
||||
Type string `yaml:"type" validate:"required"`
|
||||
Address []string `yaml:"address" validate:"required"`
|
||||
Username string `yaml:"username"`
|
||||
Password string `yaml:"password"`
|
||||
Database int `yaml:"database"`
|
||||
UseGCPIAM bool `yaml:"useGCPIAM"`
|
||||
ClusterEnabled bool `yaml:"clusterEnabled"`
|
||||
TLS TLSConfig `yaml:"tls"`
|
||||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
InsecureSkipVerify bool `yaml:"insecureSkipVerify"`
|
||||
}
|
||||
|
||||
func (r Config) SourceConfigType() string {
|
||||
@@ -91,6 +98,13 @@ func initRedisClient(ctx context.Context, r Config) (RedisClient, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var tlsConfig *tls.Config
|
||||
if r.TLS.Enabled {
|
||||
tlsConfig = &tls.Config{
|
||||
InsecureSkipVerify: r.TLS.InsecureSkipVerify,
|
||||
}
|
||||
}
|
||||
|
||||
var client RedisClient
|
||||
var err error
|
||||
if r.ClusterEnabled {
|
||||
@@ -104,6 +118,7 @@ func initRedisClient(ctx context.Context, r Config) (RedisClient, error) {
|
||||
CredentialsProviderContext: authFn,
|
||||
Username: r.Username,
|
||||
Password: r.Password,
|
||||
TLSConfig: tlsConfig,
|
||||
})
|
||||
err = clusterClient.ForEachShard(ctx, func(ctx context.Context, shard *redis.Client) error {
|
||||
return shard.Ping(ctx).Err()
|
||||
@@ -125,6 +140,7 @@ func initRedisClient(ctx context.Context, r Config) (RedisClient, error) {
|
||||
CredentialsProviderContext: authFn,
|
||||
Username: r.Username,
|
||||
Password: r.Password,
|
||||
TLSConfig: tlsConfig,
|
||||
})
|
||||
_, err = standaloneClient.Ping(ctx).Result()
|
||||
if err != nil {
|
||||
|
||||
@@ -63,6 +63,9 @@ func TestParseFromYamlRedis(t *testing.T) {
|
||||
database: 1
|
||||
useGCPIAM: true
|
||||
clusterEnabled: true
|
||||
tls:
|
||||
enabled: true
|
||||
insecureSkipVerify: true
|
||||
`,
|
||||
want: map[string]sources.SourceConfig{
|
||||
"my-redis-instance": redis.Config{
|
||||
@@ -73,6 +76,10 @@ func TestParseFromYamlRedis(t *testing.T) {
|
||||
Database: 1,
|
||||
ClusterEnabled: true,
|
||||
UseGCPIAM: true,
|
||||
TLS: redis.TLSConfig{
|
||||
Enabled: true,
|
||||
InsecureSkipVerify: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user