Compare commits

...

5 Commits

Author SHA1 Message Date
dependabot[bot]
3684efd512 chore(deps): bump ajv from 8.17.1 to 8.18.0 in /docs/en/getting-started/quickstart/js/genkit (#2487)
Bumps [ajv](https://github.com/ajv-validator/ajv) from 8.17.1 to 8.18.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/ajv-validator/ajv/releases">ajv's
releases</a>.</em></p>
<blockquote>
<h2>v8.18.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat: allow tree-shaking by adding <code>&quot;sideEffects&quot;:
false</code> to <code>package.json</code> by <a
href="https://github.com/josdejong"><code>@​josdejong</code></a> in <a
href="https://redirect.github.com/ajv-validator/ajv/pull/2480">ajv-validator/ajv#2480</a></li>
<li>fix: <a
href="https://redirect.github.com/ajv-validator/ajv/issues/2482">#2482</a>
Infinity and NaN serialise to null by <a
href="https://github.com/jasoniangreen"><code>@​jasoniangreen</code></a>
in <a
href="https://redirect.github.com/ajv-validator/ajv/pull/2487">ajv-validator/ajv#2487</a></li>
<li>fix: small grammatical error in managing-schemas.md by <a
href="https://github.com/monteiro-renato"><code>@​monteiro-renato</code></a>
in <a
href="https://redirect.github.com/ajv-validator/ajv/pull/2508">ajv-validator/ajv#2508</a></li>
<li>fix: typos in schema-language.md by <a
href="https://github.com/monteiro-renato"><code>@​monteiro-renato</code></a>
in <a
href="https://redirect.github.com/ajv-validator/ajv/pull/2507">ajv-validator/ajv#2507</a></li>
<li>fix(pattern): use configured RegExp engine with $data keyword to
mitigate ReDoS attacks (CVE-2025-69873) by <a
href="https://github.com/epoberezkin"><code>@​epoberezkin</code></a> in
<a
href="https://redirect.github.com/ajv-validator/ajv/pull/2586">ajv-validator/ajv#2586</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/josdejong"><code>@​josdejong</code></a>
made their first contribution in <a
href="https://redirect.github.com/ajv-validator/ajv/pull/2480">ajv-validator/ajv#2480</a></li>
<li><a
href="https://github.com/monteiro-renato"><code>@​monteiro-renato</code></a>
made their first contribution in <a
href="https://redirect.github.com/ajv-validator/ajv/pull/2508">ajv-validator/ajv#2508</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/ajv-validator/ajv/compare/v8.17.1...v8.18.0">https://github.com/ajv-validator/ajv/compare/v8.17.1...v8.18.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="142ce84b80"><code>142ce84</code></a>
8.18.0</li>
<li><a
href="720a23fa45"><code>720a23f</code></a>
fix(pattern): use configured RegExp engine with $data keyword to
mitigate ReD...</li>
<li><a
href="82735a1582"><code>82735a1</code></a>
fix: typos in schema-language.md (<a
href="https://redirect.github.com/ajv-validator/ajv/issues/2507">#2507</a>)</li>
<li><a
href="b17ec32cd9"><code>b17ec32</code></a>
fix: small grammatical error in managing-schemas.md (<a
href="https://redirect.github.com/ajv-validator/ajv/issues/2508">#2508</a>)</li>
<li><a
href="69568d0856"><code>69568d0</code></a>
fix: <a
href="https://redirect.github.com/ajv-validator/ajv/issues/2482">#2482</a>
Infinity and NaN serialise to null (<a
href="https://redirect.github.com/ajv-validator/ajv/issues/2487">#2487</a>)</li>
<li><a
href="f06766f33e"><code>f06766f</code></a>
feat: allow tree-shaking by adding ``&quot;sideEffects&quot;:
false<code>to</code>package.json` ...</li>
<li>See full diff in <a
href="https://github.com/ajv-validator/ajv/compare/v8.17.1...v8.18.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=ajv&package-manager=npm_and_yarn&previous-version=8.17.1&new-version=8.18.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/googleapis/genai-toolbox/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2026-02-18 15:41:29 -08:00
Mend Renovate
7a6d0c12e9 chore(deps): update peter-evans/create-or-update-comment action to v5 (#2500)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[peter-evans/create-or-update-comment](https://redirect.github.com/peter-evans/create-or-update-comment)
| action | major | `v4` → `v5` |

---

### Release Notes

<details>
<summary>peter-evans/create-or-update-comment
(peter-evans/create-or-update-comment)</summary>

###
[`v5`](https://redirect.github.com/peter-evans/create-or-update-comment/compare/v4...v5)

[Compare
Source](https://redirect.github.com/peter-evans/create-or-update-comment/compare/v4...v5)

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined),
Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you
are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/googleapis/genai-toolbox).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMi4wIiwidXBkYXRlZEluVmVyIjoiNDMuMjIuMCIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2026-02-18 21:31:15 +00:00
Mark L
57b77bca09 feat(sources/postgres): add configurable pgx query execution mode (#2477)
Adds optional `queryExecMode` to postgres source config, allowing users
to set pgx `DefaultQueryExecMode` for compatibility with external
connection poolers (e.g. transaction pooling).

Supported values:
- cache_statement (default)
- cache_describe
- describe_exec
- exec
- simple_protocol

Implementation details:
- parse DSN with `pgxpool.ParseConfig`
- map `queryExecMode` to `pgx.QueryExecMode*`
- create pool via `pgxpool.NewWithConfig`
- validate config using `oneof` tag
- document new field in postgres source docs
- add parser/validation tests

Tests run:
`go test -v ./internal/sources/postgres -count=1 -vet=off`

Refs #2385

---------

Co-authored-by: Molt (OpenClaw) <noreply@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
Co-authored-by: Yuan Teoh <yuanteoh@google.com>
2026-02-18 20:35:44 +00:00
Benny Magid
276cf604a2 feat(ui): make tool list panel resizable (#2253)
## Description

Add draggable resize handle to tool list panel with min/max width
constraints, visual feedback, and localStorage persistence.

## 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 #1729

---------

Co-authored-by: Wenxin Du <117315983+duwenxin99@users.noreply.github.com>
2026-02-18 15:07:40 -05:00
dependabot[bot]
e8f3b9c8f2 chore(deps): bump axios from 1.13.4 to 1.13.5 in /docs/en/samples/pre_post_processing/js/langchain (#2510)
Bumps [axios](https://github.com/axios/axios) from 1.13.4 to 1.13.5.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/axios/axios/releases">axios's
releases</a>.</em></p>
<blockquote>
<h2>v1.13.5</h2>
<h2>Release 1.13.5</h2>
<h3>Highlights</h3>
<ul>
<li><strong>Security:</strong> Fixed a potential <strong>Denial of
Service</strong> issue involving the <code>__proto__</code> key in
<code>mergeConfig</code>. (PR <a
href="https://redirect.github.com/axios/axios/pull/7369">#7369</a>)</li>
<li><strong>Bug fix:</strong> Resolved an issue where
<code>AxiosError</code> could be missing the <code>status</code> field
on and after <strong>v1.13.3</strong>. (PR <a
href="https://redirect.github.com/axios/axios/pull/7368">#7368</a>)</li>
</ul>
<h3>Changes</h3>
<h4>Security</h4>
<ul>
<li>Fix Denial of Service via <code>__proto__</code> key in
<code>mergeConfig</code>. (PR <a
href="https://redirect.github.com/axios/axios/pull/7369">#7369</a>)</li>
</ul>
<h4>Fixes</h4>
<ul>
<li>Fix/5657. (PR <a
href="https://redirect.github.com/axios/axios/pull/7313">#7313</a>)</li>
<li>Ensure <code>status</code> is present in <code>AxiosError</code> on
and after v1.13.3. (PR <a
href="https://redirect.github.com/axios/axios/pull/7368">#7368</a>)</li>
</ul>
<h4>Features / Improvements</h4>
<ul>
<li>Add input validation to <code>isAbsoluteURL</code>. (PR <a
href="https://redirect.github.com/axios/axios/pull/7326">#7326</a>)</li>
<li>Refactor: bump minor package versions. (PR <a
href="https://redirect.github.com/axios/axios/pull/7356">#7356</a>)</li>
</ul>
<h4>Documentation</h4>
<ul>
<li>Clarify object-check comment. (PR <a
href="https://redirect.github.com/axios/axios/pull/7323">#7323</a>)</li>
<li>Fix deprecated <code>Buffer</code> constructor usage and README
formatting. (PR <a
href="https://redirect.github.com/axios/axios/pull/7371">#7371</a>)</li>
</ul>
<h4>CI / Maintenance</h4>
<ul>
<li>Chore: fix issues with YAML. (PR <a
href="https://redirect.github.com/axios/axios/pull/7355">#7355</a>)</li>
<li>CI: update workflow YAMLs. (PR <a
href="https://redirect.github.com/axios/axios/pull/7372">#7372</a>)</li>
<li>CI: fix run condition. (PR <a
href="https://redirect.github.com/axios/axios/pull/7373">#7373</a>)</li>
<li>Dev deps: bump <code>karma-sourcemap-loader</code> from 0.3.8 to
0.4.0. (PR <a
href="https://redirect.github.com/axios/axios/pull/7360">#7360</a>)</li>
<li>Chore(release): prepare release 1.13.5. (PR <a
href="https://redirect.github.com/axios/axios/pull/7379">#7379</a>)</li>
</ul>
<h3>New Contributors</h3>
<ul>
<li><a
href="https://github.com/sachin11063"><code>@​sachin11063</code></a>
(first contribution — PR <a
href="https://redirect.github.com/axios/axios/pull/7323">#7323</a>)</li>
<li><a
href="https://github.com/asmitha-16"><code>@​asmitha-16</code></a>
(first contribution — PR <a
href="https://redirect.github.com/axios/axios/pull/7326">#7326</a>)</li>
</ul>
<p><strong>Full Changelog:</strong> <a
href="https://github.com/axios/axios/compare/v1.13.4...v1.13.5">https://github.com/axios/axios/compare/v1.13.4...v1.13.5</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="29f75425f0"><code>29f7542</code></a>
chore(release): prepare release 1.13.5 (<a
href="https://redirect.github.com/axios/axios/issues/7379">#7379</a>)</li>
<li><a
href="431c3a3614"><code>431c3a3</code></a>
ci: fix run condition (<a
href="https://redirect.github.com/axios/axios/issues/7373">#7373</a>)</li>
<li><a
href="9ff3a78ad7"><code>9ff3a78</code></a>
ci: update ymls (<a
href="https://redirect.github.com/axios/axios/issues/7372">#7372</a>)</li>
<li><a
href="265b71234c"><code>265b712</code></a>
docs: fix deprecated Buffer constructor and formatting issues in README
(<a
href="https://redirect.github.com/axios/axios/issues/7371">#7371</a>)</li>
<li><a
href="475e75a260"><code>475e75a</code></a>
feat: add input validation to isAbsoluteURL (<a
href="https://redirect.github.com/axios/axios/issues/7326">#7326</a>)</li>
<li><a
href="28c721588c"><code>28c7215</code></a>
fix: Denial of Service via <strong>proto</strong> Key in mergeConfig (<a
href="https://redirect.github.com/axios/axios/issues/7369">#7369</a>)</li>
<li><a
href="04cf01969e"><code>04cf019</code></a>
docs: clarify object check comment (<a
href="https://redirect.github.com/axios/axios/issues/7323">#7323</a>)</li>
<li><a
href="696fa753c5"><code>696fa75</code></a>
fix: status is missing in AxiosError on and after v1.13.3 (<a
href="https://redirect.github.com/axios/axios/issues/7368">#7368</a>)</li>
<li><a
href="569f028a58"><code>569f028</code></a>
fix: added a option to choose between legacy and the new
request/response int...</li>
<li><a
href="44b7c9f0c4"><code>44b7c9f</code></a>
chore(deps-dev): bump karma-sourcemap-loader (<a
href="https://redirect.github.com/axios/axios/issues/7360">#7360</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/axios/axios/compare/v1.13.4...v1.13.5">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=axios&package-manager=npm_and_yarn&previous-version=1.13.4&new-version=1.13.5)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
You can disable automated security fix PRs for this repo from the
[Security Alerts
page](https://github.com/googleapis/genai-toolbox/network/alerts).

</details>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-18 11:12:10 -08:00
10 changed files with 279 additions and 38 deletions

View File

@@ -114,7 +114,7 @@ jobs:
- name: Create PR Comment
if: env.HAS_CHANGES == 'true' && steps.lychee-check.outcome == 'failure'
uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4
uses: peter-evans/create-or-update-comment@e8674b075228eee787fea43ef493e45ece1004c9 # v5
with:
comment-id: ${{ steps.find-comment.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}

View File

@@ -3231,9 +3231,10 @@
}
},
"node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"fast-uri": "^3.0.1",

View File

@@ -133,3 +133,4 @@ instead of hardcoding your secrets into the configuration file.
| user | string | true | Name of the Postgres user to connect as (e.g. "my-pg-user"). |
| password | string | true | Password of the Postgres user (e.g. "my-password"). |
| queryParams | map[string]string | false | Raw query to be added to the db connection string. |
| queryExecMode | string | false | pgx query execution mode. Valid values: `cache_statement` (default), `cache_describe`, `describe_exec`, `exec`, `simple_protocol`. Useful with connection poolers that don't support prepared statement caching. |

View File

@@ -217,13 +217,13 @@
"license": "MIT"
},
"node_modules/axios": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.4.tgz",
"integrity": "sha512-1wVkUaAO6WyaYtCkcYCOx12ZgpGf9Zif+qXa4n+oYzK558YryKqiL6UWwd5DqiH3VRW0GYhTZQ/vlgJrCoNQlg==",
"version": "1.13.5",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.5.tgz",
"integrity": "sha512-cz4ur7Vb0xS4/KUN0tPWe44eqxrIu31me+fbang3ijiNscE129POzipJJA6zniq2C/Z6sJCjMimjS8Lc/GAs8Q==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
"form-data": "^4.0.4",
"follow-redirects": "^1.15.11",
"form-data": "^4.0.5",
"proxy-from-env": "^1.1.0"
}
},
@@ -1598,17 +1598,6 @@
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}
},
"node_modules/zod-to-json-schema": {
"version": "3.25.1",
"resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz",
"integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==",
"license": "ISC",
"optional": true,
"peer": true,
"peerDependencies": {
"zod": "^3.25 || ^4"
}
}
}
}

View File

@@ -87,8 +87,29 @@ body {
display: flex;
flex-direction: column;
padding: 15px;
align-items: center;
align-items: stretch;
position: relative;
min-width: 200px;
max-width: 50vw;
overflow: visible;
box-sizing: border-box;
}
.resize-handle {
position: absolute;
right: -2px;
top: 0;
bottom: 0;
width: 4px;
cursor: ew-resize;
background-color: transparent;
z-index: 10;
transition: background-color 0.2s ease;
}
.resize-handle:hover,
.resize-handle.active {
background-color: var(--toolbox-blue);
}
.nav-logo {
@@ -626,10 +647,13 @@ body {
.search-container {
display: flex;
width: 100%;
min-width: 0;
margin-bottom: 15px;
box-sizing: border-box;
#toolset-search-input {
flex-grow: 1;
flex: 1;
min-width: 0;
padding: 10px 12px;
border: 1px solid #ccc;
border-radius: 20px 0 0 20px;
@@ -637,6 +661,7 @@ body {
font-family: inherit;
font-size: 0.9em;
color: var(--text-primary-gray);
box-sizing: border-box;
&:focus {
outline: none;

View File

@@ -0,0 +1,116 @@
// Copyright 2025 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.
const STORAGE_KEY = 'toolbox-second-nav-width';
const DEFAULT_WIDTH = 250;
const MIN_WIDTH = 200;
const MAX_WIDTH_PERCENT = 50;
/**
* Creates and attaches a resize handle to the second navigation panel
*/
export function initializeResize() {
const secondNav = document.querySelector('.second-nav');
if (!secondNav) {
return;
}
// Create resize handle
const resizeHandle = document.createElement('div');
resizeHandle.className = 'resize-handle';
resizeHandle.setAttribute('aria-label', 'Resize panel');
secondNav.appendChild(resizeHandle);
// Load saved width or use default
let initialWidth = DEFAULT_WIDTH;
try {
const savedWidth = localStorage.getItem(STORAGE_KEY);
if (savedWidth) {
const parsed = parseInt(savedWidth, 10);
if (!isNaN(parsed) && parsed >= MIN_WIDTH) {
initialWidth = parsed;
}
}
} catch (e) {
// localStorage may be unavailable in private browsing mode
console.warn('Failed to load saved panel width:', e);
}
setPanelWidth(secondNav, initialWidth);
// Setup resize functionality
let startX = 0;
let startWidth = 0;
const onMouseMove = (e) => {
const deltaX = e.clientX - startX;
const newWidth = startWidth + deltaX;
const maxWidth = (window.innerWidth * MAX_WIDTH_PERCENT) / 100;
const clampedWidth = Math.max(MIN_WIDTH, Math.min(newWidth, maxWidth));
setPanelWidth(secondNav, clampedWidth);
};
const onMouseUp = () => {
document.removeEventListener('mousemove', onMouseMove);
document.removeEventListener('mouseup', onMouseUp);
resizeHandle.classList.remove('active');
document.body.style.cursor = '';
document.body.style.userSelect = '';
// Save width to localStorage
try {
localStorage.setItem(STORAGE_KEY, secondNav.offsetWidth.toString());
} catch (e) {
// localStorage may be unavailable in private browsing mode
console.warn('Failed to save panel width:', e);
}
};
resizeHandle.addEventListener('mousedown', (e) => {
startX = e.clientX;
startWidth = secondNav.offsetWidth;
resizeHandle.classList.add('active');
document.body.style.cursor = 'ew-resize';
document.body.style.userSelect = 'none';
e.preventDefault();
document.addEventListener('mousemove', onMouseMove);
document.addEventListener('mouseup', onMouseUp);
});
// Handle window resize to enforce max width
window.addEventListener('resize', () => {
const currentWidth = secondNav.offsetWidth;
const maxWidth = (window.innerWidth * MAX_WIDTH_PERCENT) / 100;
if (currentWidth > maxWidth) {
setPanelWidth(secondNav, maxWidth);
try {
localStorage.setItem(STORAGE_KEY, maxWidth.toString());
} catch (e) {
// localStorage may be unavailable in private browsing mode
console.warn('Failed to save panel width:', e);
}
}
});
}
/**
* Sets the width of the panel and updates flex property
*/
function setPanelWidth(panel, width) {
panel.style.flex = `0 0 ${width}px`;
}

View File

@@ -22,12 +22,16 @@
<script type="module" src="/ui/js/tools.js"></script>
<script src="/ui/js/navbar.js"></script>
<script src="/ui/js/mainContent.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
<script type="module">
document.addEventListener('DOMContentLoaded', async () => {
const navbarContainer = document.getElementById('navbar-container');
const activeNav = navbarContainer.getAttribute('data-active-nav');
renderNavbar('navbar-container', activeNav);
renderMainContent('main-content-container', 'tool-display-area', getToolInstructions())
renderMainContent('main-content-container', 'tool-display-area', getToolInstructions());
// Initialize resize functionality
const { initializeResize } = await import('/ui/js/resize.js');
initializeResize();
});
</script>
</body>

View File

@@ -29,12 +29,16 @@
<script type="module" src="/ui/js/toolsets.js"></script>
<script src="/ui/js/navbar.js"></script>
<script src="/ui/js/mainContent.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
<script type="module">
document.addEventListener('DOMContentLoaded', async () => {
const navbarContainer = document.getElementById('navbar-container');
const activeNav = navbarContainer.getAttribute('data-active-nav');
renderNavbar('navbar-container', activeNav);
renderMainContent('main-content-container', 'tool-display-area', getToolsetInstructions());
// Initialize resize functionality
const { initializeResize } = await import('/ui/js/resize.js');
initializeResize();
});
</script>
</body>

View File

@@ -24,6 +24,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/orderedmap"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"go.opentelemetry.io/otel/trace"
)
@@ -48,14 +49,15 @@ 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"`
Host string `yaml:"host" validate:"required"`
Port string `yaml:"port" validate:"required"`
User string `yaml:"user" validate:"required"`
Password string `yaml:"password" validate:"required"`
Database string `yaml:"database" validate:"required"`
QueryParams map[string]string `yaml:"queryParams"`
Name string `yaml:"name" validate:"required"`
Type string `yaml:"type" validate:"required"`
Host string `yaml:"host" validate:"required"`
Port string `yaml:"port" validate:"required"`
User string `yaml:"user" validate:"required"`
Password string `yaml:"password" validate:"required"`
Database string `yaml:"database" validate:"required"`
QueryParams map[string]string `yaml:"queryParams"`
QueryExecMode string `yaml:"queryExecMode" validate:"omitempty,oneof=cache_statement cache_describe describe_exec exec simple_protocol"`
}
func (r Config) SourceConfigType() string {
@@ -63,7 +65,7 @@ func (r Config) SourceConfigType() string {
}
func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
pool, err := initPostgresConnectionPool(ctx, tracer, r.Name, r.Host, r.Port, r.User, r.Password, r.Database, r.QueryParams)
pool, err := initPostgresConnectionPool(ctx, tracer, r.Name, r.Host, r.Port, r.User, r.Password, r.Database, r.QueryParams, r.QueryExecMode)
if err != nil {
return nil, fmt.Errorf("unable to create pool: %w", err)
}
@@ -126,7 +128,7 @@ func (s *Source) RunSQL(ctx context.Context, statement string, params []any) (an
return out, nil
}
func initPostgresConnectionPool(ctx context.Context, tracer trace.Tracer, name, host, port, user, pass, dbname string, queryParams map[string]string) (*pgxpool.Pool, error) {
func initPostgresConnectionPool(ctx context.Context, tracer trace.Tracer, name, host, port, user, pass, dbname string, queryParams map[string]string, queryExecMode string) (*pgxpool.Pool, error) {
//nolint:all // Reassigned ctx
ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceType, name)
defer span.End()
@@ -150,7 +152,18 @@ func initPostgresConnectionPool(ctx context.Context, tracer trace.Tracer, name,
Path: dbname,
RawQuery: ConvertParamMapToRawQuery(queryParams),
}
pool, err := pgxpool.New(ctx, url.String())
config, err := pgxpool.ParseConfig(url.String())
if err != nil {
return nil, fmt.Errorf("unable to parse connection uri: %w", err)
}
execMode, err := ParseQueryExecMode(queryExecMode)
if err != nil {
return nil, err
}
config.ConnConfig.DefaultQueryExecMode = execMode
pool, err := pgxpool.NewWithConfig(ctx, config)
if err != nil {
return nil, fmt.Errorf("unable to create connection pool: %w", err)
}
@@ -165,3 +178,20 @@ func ConvertParamMapToRawQuery(queryParams map[string]string) string {
}
return strings.Join(queryArray, "&")
}
func ParseQueryExecMode(queryExecMode string) (pgx.QueryExecMode, error) {
switch queryExecMode {
case "", "cache_statement":
return pgx.QueryExecModeCacheStatement, nil
case "cache_describe":
return pgx.QueryExecModeCacheDescribe, nil
case "describe_exec":
return pgx.QueryExecModeDescribeExec, nil
case "exec":
return pgx.QueryExecModeExec, nil
case "simple_protocol":
return pgx.QueryExecModeSimpleProtocol, nil
default:
return 0, fmt.Errorf("invalid queryExecMode %q: must be one of %q, %q, %q, %q, or %q", queryExecMode, "cache_statement", "cache_describe", "describe_exec", "exec", "simple_protocol")
}
}

View File

@@ -25,6 +25,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/sources/postgres"
"github.com/googleapis/genai-toolbox/internal/testutils"
"github.com/jackc/pgx/v5"
)
func TestParseFromYamlPostgres(t *testing.T) {
@@ -88,6 +89,32 @@ func TestParseFromYamlPostgres(t *testing.T) {
},
},
},
{
desc: "example with query exec mode",
in: `
kind: sources
name: my-pg-instance
type: postgres
host: my-host
port: my-port
database: my_db
user: my_user
password: my_pass
queryExecMode: simple_protocol
`,
want: map[string]sources.SourceConfig{
"my-pg-instance": postgres.Config{
Name: "my-pg-instance",
Type: postgres.SourceType,
Host: "my-host",
Port: "my-port",
Database: "my_db",
User: "my_user",
Password: "my_pass",
QueryExecMode: "simple_protocol",
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
@@ -137,6 +164,21 @@ func TestFailParseFromYaml(t *testing.T) {
`,
err: "error unmarshaling sources: unable to parse source \"my-pg-instance\" as \"postgres\": Key: 'Config.Password' Error:Field validation for 'Password' failed on the 'required' tag",
},
{
desc: "invalid query exec mode",
in: `
kind: sources
name: my-pg-instance
type: postgres
host: my-host
port: my-port
database: my_db
user: my_user
password: my_pass
queryExecMode: invalid_mode
`,
err: "error unmarshaling sources: unable to parse source \"my-pg-instance\" as \"postgres\": [6:16] Key: 'Config.QueryExecMode' Error:Field validation for 'QueryExecMode' failed on the 'oneof' tag\n 3 | name: my-pg-instance\n 4 | password: my_pass\n 5 | port: my-port\n> 6 | queryExecMode: invalid_mode\n ^\n 7 | type: postgres\n 8 | user: my_user",
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
@@ -193,3 +235,32 @@ func TestConvertParamMapToRawQuery(t *testing.T) {
})
}
}
func TestParseQueryExecMode(t *testing.T) {
tcs := []struct {
desc string
in string
want pgx.QueryExecMode
wantErr bool
}{
{desc: "empty (default)", in: "", want: pgx.QueryExecModeCacheStatement},
{desc: "cache_statement", in: "cache_statement", want: pgx.QueryExecModeCacheStatement},
{desc: "cache_describe", in: "cache_describe", want: pgx.QueryExecModeCacheDescribe},
{desc: "describe_exec", in: "describe_exec", want: pgx.QueryExecModeDescribeExec},
{desc: "exec", in: "exec", want: pgx.QueryExecModeExec},
{desc: "simple_protocol", in: "simple_protocol", want: pgx.QueryExecModeSimpleProtocol},
{desc: "invalid mode", in: "invalid_mode", wantErr: true},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got, err := postgres.ParseQueryExecMode(tc.in)
if (err != nil) != tc.wantErr {
t.Fatalf("parseQueryExecMode() error = %v, wantErr %v", err, tc.wantErr)
}
if !tc.wantErr && got != tc.want {
t.Errorf("parseQueryExecMode() = %v, want %v", got, tc.want)
}
})
}
}