Compare commits

..

31 Commits

Author SHA1 Message Date
Ben Knutson
b1dfff13fa Refactor Github Action per b/485167538 2026-02-17 23:30:05 +00:00
rahulpinto19
64b52de996 add licence 2026-02-11 10:34:20 +00:00
rahulpinto19
a44b7ca8ef add weekly report 2026-02-11 10:15:38 +00:00
rahulpinto19
08036ef663 docs: link-check-report 2026-02-11 10:12:51 +00:00
Anubhav Dhawan
1f8019c50a docs(adk): align quickstart script with other orchestrations (#2423)
This brings the ADK Python quickstart sample up to par with the other
orchestrations in the guidance, removing the need for special test
handling.
2026-02-11 00:15:00 +05:30
dependabot[bot]
6e8255476a chore(deps): bump langsmith from 0.4.3 to 0.5.0 in /docs/en/getting-started/quickstart/js/langchain (#2438)
Bumps [langsmith](https://github.com/langchain-ai/langsmith-sdk) from
0.4.3 to 0.5.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/langchain-ai/langsmith-sdk/releases">langsmith's
releases</a>.</em></p>
<blockquote>
<h2>v0.5.0</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py,js): Python 0.5, JS 0.4 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2182">langchain-ai/langsmith-sdk#2182</a></li>
<li>js: Ensures parent traceables always finish after children in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2094">langchain-ai/langsmith-sdk#2094</a></li>
<li>py: Make evaluator target function traces only trace inputs instead
of nested inputs outputs reference_outputs (BREAKING) in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2074">langchain-ai/langsmith-sdk#2074</a></li>
<li>py,js: Change default ingest to multipart instead of single run
(BREAKING) in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2206">langchain-ai/langsmith-sdk#2206</a></li>
<li>js: Omit raw HTTP info when tracing AI SDK runs (BREAKING) in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2207">langchain-ai/langsmith-sdk#2207</a></li>
<li>py,js: Remove legacy evaluators (BREAKING) in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2144">langchain-ai/langsmith-sdk#2144</a></li>
<li>js: Remove legacy AI SDK exporter and wrapAIModel (BREAKING) in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2210">langchain-ai/langsmith-sdk#2210</a></li>
<li>js: Make traceables that receive iterables as inputs use a single
POST instead of the typical POST + PATCH flow in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2209">langchain-ai/langsmith-sdk#2209</a></li>
<li>release(js): 0.4.0 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2216">langchain-ai/langsmith-sdk#2216</a></li>
<li>release(py): 0.5.0 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2217">langchain-ai/langsmith-sdk#2217</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.60...v0.5.0">https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.60...v0.5.0</a></p>
<h2>v0.4.60</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(js): Wrap additional OpenAI methods in wrapOpenAI by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2198">langchain-ai/langsmith-sdk#2198</a></li>
<li>fix(js): Fix traceable typing for async iterables and runnable
config by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2199">langchain-ai/langsmith-sdk#2199</a></li>
<li>release(js): 0.3.86 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2202">langchain-ai/langsmith-sdk#2202</a></li>
<li>fix(py): add missing imports during TYPE_CHECKING by <a
href="https://github.com/mkaoudis"><code>@​mkaoudis</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2201">langchain-ai/langsmith-sdk#2201</a></li>
<li>fix(js): Use one generic param for async iterable in traceable types
by <a href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2203">langchain-ai/langsmith-sdk#2203</a></li>
<li>release(js): 0.3.87 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2204">langchain-ai/langsmith-sdk#2204</a></li>
<li>feat: automatically infer attachments by <a
href="https://github.com/hinthornw"><code>@​hinthornw</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2200">langchain-ai/langsmith-sdk#2200</a></li>
<li>fix(openai agents sdk): Merge invocation params into run extra by <a
href="https://github.com/angus-langchain"><code>@​angus-langchain</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2214">langchain-ai/langsmith-sdk#2214</a></li>
<li>bump(py): 0.4.60 by <a
href="https://github.com/angus-langchain"><code>@​angus-langchain</code></a>
in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2215">langchain-ai/langsmith-sdk#2215</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/mkaoudis"><code>@​mkaoudis</code></a>
made their first contribution in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2201">langchain-ai/langsmith-sdk#2201</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.59...v0.4.60">https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.59...v0.4.60</a></p>
<h2>v0.4.59</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py,js): Add prop to omit traced runtime info by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2195">langchain-ai/langsmith-sdk#2195</a></li>
<li>release(js): 0.3.85 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2196">langchain-ai/langsmith-sdk#2196</a></li>
<li>release(py): 0.4.59 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2197">langchain-ai/langsmith-sdk#2197</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.58...v0.4.59">https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.58...v0.4.59</a></p>
<h2>v0.4.58</h2>
<h2>What's Changed</h2>
<ul>
<li>feat(py,js): Add batch example deletion method, hard delete for
Python by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2190">langchain-ai/langsmith-sdk#2190</a></li>
<li>release(js): 0.3.84 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2191">langchain-ai/langsmith-sdk#2191</a></li>
<li>release(py): 0.4.58 by <a
href="https://github.com/jacoblee93"><code>@​jacoblee93</code></a> in <a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/pull/2192">langchain-ai/langsmith-sdk#2192</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.57...v0.4.58">https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.57...v0.4.58</a></p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="a3edbcd681"><code>a3edbcd</code></a>
release(py): 0.5.0 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2217">#2217</a>)</li>
<li><a
href="750c47117b"><code>750c471</code></a>
release(js): 0.4.0 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2216">#2216</a>)</li>
<li><a
href="9edd9e503c"><code>9edd9e5</code></a>
feat(py,js): Python 0.5, JS 0.4 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2182">#2182</a>)</li>
<li><a
href="55f64b012a"><code>55f64b0</code></a>
bump(py): 0.4.60 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2215">#2215</a>)</li>
<li><a
href="bcc7457107"><code>bcc7457</code></a>
fix(openai agents sdk): Merge invocation params into run extra (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2214">#2214</a>)</li>
<li><a
href="c45bf95e53"><code>c45bf95</code></a>
feat: automatically infer attachments (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2200">#2200</a>)</li>
<li><a
href="5932480852"><code>5932480</code></a>
release(js): 0.3.87 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2204">#2204</a>)</li>
<li><a
href="d7f7866677"><code>d7f7866</code></a>
fix(js): Use one generic param for async iterable in traceable types (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2203">#2203</a>)</li>
<li><a
href="28d89bc5bf"><code>28d89bc</code></a>
fix(py): add missing imports during TYPE_CHECKING (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2201">#2201</a>)</li>
<li><a
href="6545031be5"><code>6545031</code></a>
release(js): 0.3.86 (<a
href="https://redirect.github.com/langchain-ai/langsmith-sdk/issues/2202">#2202</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/langchain-ai/langsmith-sdk/compare/v0.4.3...v0.5.0">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by [GitHub Actions](<a
href="https://www.npmjs.com/~GitHub">https://www.npmjs.com/~GitHub</a>
Actions), a new releaser for langsmith since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=langsmith&package-manager=npm_and_yarn&previous-version=0.4.3&new-version=0.5.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>
2026-02-10 09:43:28 -08:00
dependabot[bot]
4fb5b34a5a chore(deps): bump axios from 1.12.2 to 1.13.5 in /docs/en/getting-started/quickstart/js/genkit (#2442)
Bumps [axios](https://github.com/axios/axios) from 1.12.2 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>
<h2>v1.13.4</h2>
<h2>Overview</h2>
<p>The release addresses issues discovered in v1.13.3 and includes
significant CI/CD improvements.</p>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/axios/axios/compare/v1.13.3...v1.13.4">v1.13.3...v1.13.4</a></p>
<h2>What's New in v1.13.4</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>fix: issues with version 1.13.3</strong> (<a
href="https://redirect.github.com/axios/axios/issues/7352">#7352</a>)
(<a
href="ee90dfc28a">ee90dfc</a>)
<ul>
<li>Fixed issues discovered in v1.13.3 release</li>
</ul>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/axios/axios/blob/v1.x/CHANGELOG.md">axios's
changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2><a
href="https://github.com/axios/axios/compare/v1.13.2...v1.13.3">1.13.3</a>
(2026-01-20)</h2>
<h3>Bug Fixes</h3>
<ul>
<li><strong>http2:</strong> Use port 443 for HTTPS connections by
default. (<a
href="https://redirect.github.com/axios/axios/issues/7256">#7256</a>)
(<a
href="d7e6065346">d7e6065</a>)</li>
<li><strong>interceptor:</strong> handle the error in the same
interceptor (<a
href="https://redirect.github.com/axios/axios/issues/6269">#6269</a>)
(<a
href="5945e40bb1">5945e40</a>)</li>
<li>main field in package.json should correspond to cjs artifacts (<a
href="https://redirect.github.com/axios/axios/issues/5756">#5756</a>)
(<a
href="7373fbff24">7373fbf</a>)</li>
<li><strong>package.json:</strong> add 'bun' package.json 'exports'
condition. Load the Node.js build in Bun instead of the browser build
(<a
href="https://redirect.github.com/axios/axios/issues/5754">#5754</a>)
(<a
href="b89217e3e9">b89217e</a>)</li>
<li>silentJSONParsing=false should throw on invalid JSON (<a
href="https://redirect.github.com/axios/axios/issues/7253">#7253</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/7257">#7257</a>)
(<a
href="7d19335e43">7d19335</a>)</li>
<li>turn AxiosError into a native error (<a
href="https://redirect.github.com/axios/axios/issues/5394">#5394</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/5558">#5558</a>)
(<a
href="1c6a86dd2c">1c6a86d</a>)</li>
<li><strong>types:</strong> add handlers to AxiosInterceptorManager
interface (<a
href="https://redirect.github.com/axios/axios/issues/5551">#5551</a>)
(<a
href="8d1271b49f">8d1271b</a>)</li>
<li><strong>types:</strong> restore AxiosError.cause type from unknown
to Error (<a
href="https://redirect.github.com/axios/axios/issues/7327">#7327</a>)
(<a
href="d8233d9e8e">d8233d9</a>)</li>
<li>unclear error message is thrown when specifying an empty proxy
authorization (<a
href="https://redirect.github.com/axios/axios/issues/6314">#6314</a>)
(<a
href="6ef867e684">6ef867e</a>)</li>
</ul>
<h3>Features</h3>
<ul>
<li>add <code>undefined</code> as a value in AxiosRequestConfig (<a
href="https://redirect.github.com/axios/axios/issues/5560">#5560</a>)
(<a
href="095033c626">095033c</a>)</li>
<li>add automatic minor and patch upgrades to dependabot (<a
href="https://redirect.github.com/axios/axios/issues/6053">#6053</a>)
(<a
href="65a7584eda">65a7584</a>)</li>
<li>add Node.js coverage script using c8 (closes <a
href="https://redirect.github.com/axios/axios/issues/7289">#7289</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/7294">#7294</a>)
(<a
href="ec9d94e9f8">ec9d94e</a>)</li>
<li>added copilot instructions (<a
href="3f83143bfe">3f83143</a>)</li>
<li>compatibility with frozen prototypes (<a
href="https://redirect.github.com/axios/axios/issues/6265">#6265</a>)
(<a
href="860e03396a">860e033</a>)</li>
<li>enhance pipeFileToResponse with error handling (<a
href="https://redirect.github.com/axios/axios/issues/7169">#7169</a>)
(<a
href="88d7884254">88d7884</a>)</li>
<li><strong>types:</strong> Intellisense for string literals in a
widened union (<a
href="https://redirect.github.com/axios/axios/issues/6134">#6134</a>)
(<a
href="f73474d02c">f73474d</a>),
closes <a
href="https://redirect.github.com//redirect.github.com/microsoft/TypeScript/issues/33471/issues/issuecomment-1376364329">microsoft/TypeScript#33471</a></li>
</ul>
<h3>Reverts</h3>
<ul>
<li>Revert &quot;fix: silentJSONParsing=false should throw on invalid
JSON (<a
href="https://redirect.github.com/axios/axios/issues/7253">#7253</a>)
(<a
href="https://redirect.github.com/axios/axios/issues/7">#7</a>…&quot;
(<a
href="https://redirect.github.com/axios/axios/issues/7298">#7298</a>)
(<a
href="a4230f5581">a4230f5</a>),
closes <a
href="https://redirect.github.com/axios/axios/issues/7253">#7253</a> <a
href="https://redirect.github.com/axios/axios/issues/7">#7</a> <a
href="https://redirect.github.com/axios/axios/issues/7298">#7298</a></li>
<li><strong>deps:</strong> bump peter-evans/create-pull-request from 7
to 8 in the github-actions group (<a
href="https://redirect.github.com/axios/axios/issues/7334">#7334</a>)
(<a
href="2d6ad5e48b">2d6ad5e</a>)</li>
</ul>
<h3>Contributors to this release</h3>
<ul>
<li><!-- raw HTML omitted --> <a href="https://github.com/ashvin2005"
title="+1752/-4 ([#7218](https://github.com/axios/axios/issues/7218)
[#7218](https://github.com/axios/axios/issues/7218) )">Ashvin
Tiwari</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/mochinikunj"
title="+940/-12 ([#7294](https://github.com/axios/axios/issues/7294)
[#7294](https://github.com/axios/axios/issues/7294) )">Nikunj
Mochi</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/imanchalsingh"
title="+544/-102 ([#7169](https://github.com/axios/axios/issues/7169)
[#7185](https://github.com/axios/axios/issues/7185) )">Anchal
Singh</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/jasonsaayman"
title="+317/-73 ([#7334](https://github.com/axios/axios/issues/7334)
[#7298](https://github.com/axios/axios/issues/7298)
)">jasonsaayman</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/brodo"
title="+99/-120 ([#5558](https://github.com/axios/axios/issues/5558)
)">Julian Dax</a></li>
<li><!-- raw HTML omitted --> <a
href="https://github.com/AKASHDHARDUBEY" title="+167/-0
([#7287](https://github.com/axios/axios/issues/7287)
[#7288](https://github.com/axios/axios/issues/7288) )">Akash Dhar
Dubey</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/madhumitaaa"
title="+20/-68 ([#7198](https://github.com/axios/axios/issues/7198)
)">Madhumita</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/Tackoil"
title="+80/-2 ([#6269](https://github.com/axios/axios/issues/6269)
)">Tackoil</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/justindhillon"
title="+41/-41 ([#6324](https://github.com/axios/axios/issues/6324)
[#6315](https://github.com/axios/axios/issues/6315) )">Justin
Dhillon</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/Rudrxxx"
title="+71/-2 ([#7257](https://github.com/axios/axios/issues/7257)
)">Rudransh</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/WuMingDao"
title="+36/-36 ([#7215](https://github.com/axios/axios/issues/7215)
)">WuMingDao</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/codenomnom"
title="+70/-0 ([#7201](https://github.com/axios/axios/issues/7201)
[#7201](https://github.com/axios/axios/issues/7201)
)">codenomnom</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/Nandann018-ux"
title="+60/-10 ([#7272](https://github.com/axios/axios/issues/7272)
)">Nandan Acharya</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/KernelDeimos"
title="+22/-40 ([#7042](https://github.com/axios/axios/issues/7042)
)">Eric Dubé</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/tiborpilz"
title="+40/-4 ([#5551](https://github.com/axios/axios/issues/5551)
)">Tibor Pilz</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/joaoGabriel55"
title="+31/-4 ([#6314](https://github.com/axios/axios/issues/6314)
)">Gabriel Quaresma</a></li>
<li><!-- raw HTML omitted --> <a href="https://github.com/turadg"
title="+23/-6 ([#6265](https://github.com/axios/axios/issues/6265)
)">Turadg Aleahmad</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</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.12.2...v1.13.5">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by [GitHub Actions](<a
href="https://www.npmjs.com/~GitHub">https://www.npmjs.com/~GitHub</a>
Actions), a new releaser for axios since your current version.</p>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=axios&package-manager=npm_and_yarn&previous-version=1.12.2&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-10 17:04:04 +00:00
Twisha Bansal
1664a69dfd docs: add pre/post processing docs for langchain python (#2378)
## Description

Trigger has been tested corresponding to local changes. Latest
successful run:
https://pantheon.corp.google.com/cloud-build/builds;region=global/1c37031f-95f1-4c6c-9ef8-0452277599d5?e=13802955&mods=-autopush_coliseum&project=toolbox-testing-438616

Note: After merging, update python pre and post processing sample
testing trigger.

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

- [x] 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>

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
Co-authored-by: Averi Kitsch <akitsch@google.com>
2026-02-10 16:41:02 +00:00
manuka rahul
7f88caa985 docs: fix broken links (#2440)
Fix broken links

---------

Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2026-02-10 19:04:22 +05:30
dependabot[bot]
3c9365dfd9 chore(deps): bump fast-xml-parser and @google-cloud/storage in /docs/en/getting-started/quickstart/js/adk (#2434)
Bumps
[fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser)
and
[@google-cloud/storage](https://github.com/googleapis/nodejs-storage).
These dependencies needed to be updated together.
Updates `fast-xml-parser` from 4.5.3 to 5.3.5
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/releases">fast-xml-parser's
releases</a>.</em></p>
<blockquote>
<h2>v5.3.5</h2>
<h2>What's Changed</h2>
<ul>
<li>Add missing exports to fxp commonjs types by <a
href="https://github.com/jeremymeng"><code>@​jeremymeng</code></a> in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/782">NaturalIntelligence/fast-xml-parser#782</a></li>
<li>fix: Escape regex char in entity name</li>
<li>update strnum to 2.1.2</li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a
href="https://github.com/jeremymeng"><code>@​jeremymeng</code></a> made
their first contribution in <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/pull/782">NaturalIntelligence/fast-xml-parser#782</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.5">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.3.4...v5.3.5</a></p>
<h2>fix: handle HTML numeric and hex entities when out of range</h2>
<p>No release notes provided.</p>
<h2>bug fix and performance improvements</h2>
<ul>
<li>fix <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/775">#775</a>:
transformTagName with allowBooleanAttributes adds an unnecessary
attribute</li>
<li>Performance improvement for stopNodes (By <a
href="https://github.com/macieklamberski">Maciek Lamberski</a>)</li>
</ul>
<h2>Replace <code>Buffer</code> with <code>Uint8Array</code></h2>
<ul>
<li>Launched Separate CLI module</li>
<li>Replace <code>Buffer</code> with <code>Uint8Array</code></li>
</ul>
<h2>Support EMPTY and ANY with ELEMENT in DOCTYPE</h2>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.2.4...v5.2.4">https://github.com/NaturalIntelligence/fast-xml-parser/compare/v5.2.4...v5.2.4</a></p>
<h2>upgrade to ESM module and fixing value parsing issues</h2>
<ul>
<li>Support ESM modules</li>
<li>fix value parsing issues</li>
<li>a feature to access tag location is added (metadata)</li>
<li>fix to read DOCTYPE correctly</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md">https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md</a></p>
<h2>Summary update on all the previous releases from v4.2.4</h2>
<ul>
<li>Multiple minor fixes provided in the validator and parser</li>
<li>v6 is added for experimental use.</li>
<li>ignoreAttributes support function, and array of string or regex</li>
<li>Add support for parsing HTML numeric entities</li>
<li>v5 of the application is ESM module now. However, JS is also
supported</li>
</ul>
<p><strong>Note</strong>: Release section in not updated frequently.
Please check <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md">CHANGELOG</a>
or <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/tags">Tags</a>
for latest release information.</p>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/blob/master/CHANGELOG.md">fast-xml-parser's
changelog</a>.</em></p>
<blockquote>
<p><!-- raw HTML omitted -->Note: If you find missing information about
particular minor version, that version must have been changed without
any functional change in this library.<!-- raw HTML omitted --></p>
<p><strong>5.3.5 / 2026-02-08</strong></p>
<ul>
<li>fix: Escape regex char in entity name</li>
<li>update strnum to 2.1.2</li>
<li>add missing exports in CJS typings</li>
</ul>
<p><strong>5.3.4 / 2026-01-30</strong></p>
<ul>
<li>fix: handle HTML numeric and hex entities when out of range</li>
</ul>
<p><strong>5.3.3 / 2025-12-12</strong></p>
<ul>
<li>fix <a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/775">#775</a>:
transformTagName with allowBooleanAttributes adds an unnecessary
attribute</li>
</ul>
<p><strong>5.3.2 / 2025-11-14</strong></p>
<ul>
<li>fix for import statement for v6</li>
</ul>
<p><strong>5.3.1 / 2025-11-03</strong></p>
<ul>
<li>Performance improvement for stopNodes (By <a
href="https://github.com/macieklamberski">Maciek Lamberski</a>)</li>
</ul>
<p><strong>5.3.0 / 2025-10-03</strong></p>
<ul>
<li>Use <code>Uint8Array</code> in place of <code>Buffer</code> in
Parser</li>
</ul>
<p><strong>5.2.5 / 2025-06-08</strong></p>
<ul>
<li>Inform user to use <a
href="https://github.com/NaturalIntelligence/fxp-cli">fxp-cli</a>
instead of in-built CLI feature</li>
<li>Export typings  for direct use</li>
</ul>
<p><strong>5.2.4 / 2025-06-06</strong></p>
<ul>
<li>fix (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/747">#747</a>):
fix EMPTY and ANY with ELEMENT in DOCTYPE</li>
</ul>
<p><strong>5.2.3 / 2025-05-11</strong></p>
<ul>
<li>fix (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/747">#747</a>):
support EMPTY and ANY with ELEMENT in DOCTYPE</li>
</ul>
<p><strong>5.2.2 / 2025-05-05</strong></p>
<ul>
<li>fix (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/746">#746</a>):
update strnum to fix parsing issues related to enotations</li>
</ul>
<p><strong>5.2.1 / 2025-04-22</strong></p>
<ul>
<li>fix: read DOCTYPE entity value correctly</li>
<li>read DOCTYPE NOTATION, ELEMENT exp but not using read values</li>
</ul>
<p><strong>5.2.0 / 2025-04-03</strong></p>
<ul>
<li>feat: support metadata on nodes (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/593">#593</a>)
(By <a href="https://github.com/srl295">Steven R. Loomis</a>)</li>
</ul>
<p><strong>5.1.0 / 2025-04-02</strong></p>
<ul>
<li>feat: declare package as side-effect free (<a
href="https://redirect.github.com/NaturalIntelligence/fast-xml-parser/issues/738">#738</a>)
(By <a href="https://github.com/tbouffard">Thomas Bouffard</a>)</li>
<li>fix cjs build mode</li>
<li>fix builder return type to string</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li>See full diff in <a
href="https://github.com/NaturalIntelligence/fast-xml-parser/commits/v5.3.5">compare
view</a></li>
</ul>
</details>
<br />

Updates `@google-cloud/storage` from 7.18.0 to 7.19.0
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/nodejs-storage/releases"><code>@​google-cloud/storage</code>'s
releases</a>.</em></p>
<blockquote>
<h2>v7.19.0</h2>
<h2><a
href="https://github.com/googleapis/nodejs-storage/compare/v7.18.0...v7.19.0">7.19.0</a>
(2026-02-05)</h2>
<h3>Features</h3>
<ul>
<li>Enable full object checksum validation on JSON path (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2687">#2687</a>)
(<a
href="08a896240e">08a8962</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> Update dependency fast-xml-parser to v5
[security] (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2713">#2713</a>)
(<a
href="420935a033">420935a</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/googleapis/nodejs-storage/blob/main/CHANGELOG.md"><code>@​google-cloud/storage</code>'s
changelog</a>.</em></p>
<blockquote>
<h2><a
href="https://github.com/googleapis/nodejs-storage/compare/v7.18.0...v7.19.0">7.19.0</a>
(2026-02-05)</h2>
<h3>Features</h3>
<ul>
<li>Enable full object checksum validation on JSON path (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2687">#2687</a>)
(<a
href="08a896240e">08a8962</a>)</li>
</ul>
<h3>Bug Fixes</h3>
<ul>
<li><strong>deps:</strong> Update dependency fast-xml-parser to v5
[security] (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2713">#2713</a>)
(<a
href="420935a033">420935a</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="95a2af4cc8"><code>95a2af4</code></a>
chore(main): release 7.19.0 (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2694">#2694</a>)</li>
<li><a
href="420935a033"><code>420935a</code></a>
fix(deps): update dependency fast-xml-parser to v5 [security] (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2713">#2713</a>)</li>
<li><a
href="4e3c328ccf"><code>4e3c328</code></a>
test: skip system tests requiring public access (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2717">#2717</a>)</li>
<li><a
href="30522654c5"><code>3052265</code></a>
chore: fix lint failures (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2685">#2685</a>)</li>
<li><a
href="08a896240e"><code>08a8962</code></a>
feat: Enable full object checksum validation on JSON path (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2687">#2687</a>)</li>
<li><a
href="3dcda1b715"><code>3dcda1b</code></a>
chore: lint failures (<a
href="https://redirect.github.com/googleapis/nodejs-storage/issues/2681">#2681</a>)</li>
<li>See full diff in <a
href="https://github.com/googleapis/nodejs-storage/compare/v7.18.0...v7.19.0">compare
view</a></li>
</ul>
</details>
<br />


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-09 06:23:39 +00:00
manuka rahul
7767e1e019 ci: optimize link checker workflow and failure reporting (#2411)
The current system streamlines troubleshooting by omitting passing links
from the output. Reporting now focuses solely on:

- Broken or failing links
- Redirecting links
- File paths (now prioritized at the top of the log for rapid
identification)

The link resolution notice is now presented first, followed by specific
errors to prevent developer distraction.

The .lycheeignore file has been updated to use regular expressions for
broader coverage of npm and SQL links, replacing the previous method of
listing individual URLs.

---------

Co-authored-by: Twisha Bansal <58483338+twishabansal@users.noreply.github.com>
2026-02-09 11:29:31 +05:30
Twisha Bansal
bb40cdf78c refactor: consolidate quickstart tests into universal runner (#2413)
## Description

Tested locally.
Last successful runs:
- Python:
https://pantheon.corp.google.com/cloud-build/builds;region=global/e86608d7-b9df-46b3-b8e6-60f0d6144b59;step=0?e=13802955&mods=-autopush_coliseum&project=toolbox-testing-438616
- JS:
https://pantheon.corp.google.com/cloud-build/builds;region=global/7d8803fb-3e4e-4d0b-8b60-44f0c7962132?e=13802955&mods=-autopush_coliseum&project=toolbox-testing-438616
- Go:
https://pantheon.corp.google.com/cloud-build/builds;region=global/a0d0d693-ec14-422f-aa62-b0ae664005ff?e=13802955&mods=-autopush_coliseum&project=toolbox-testing-438616

Note: After merging change path to cloud build yaml files for all these
triggers: quickstart-python, quickstart-python-test-on-merge and same
for JS and Go.

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

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2026-02-09 11:05:29 +05:30
Dr. Strangelove
a15a12873f feat(tools/looker): Validate project tool (#2430)
## Description

Validate changes to a LookML project and report LookML errors.

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

- [x] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [x] 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
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)
- [x] Make sure to add `!` if this involve a breaking change
2026-02-06 14:17:45 -08:00
Averi Kitsch
5e4f2a131f docs: add managed connection pooling to docs (#2425)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

## 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-06 10:01:58 -08:00
Haoyu Wang
80ef346214 feat(cli/skills): add support for generating agent skills from toolset (#2392)
## Description

This PR introduces a new skills-generate command that enables users to
generate standardized agent skills from their existing Toolbox tool
configurations. This facilitates the integration of Toolbox tools into
agentic workflows by automatically creating skill descriptions
(SKILL.md) and executable wrappers.
- New Subcommand: Implemented skills-generate, which automates the
creation of agent skill packages including metadata and executable
scripts.
- Skill Generation: Added logic to generate SKILL.md files with
parameter schemas and Node.js wrappers for cross-platform tool
execution.
- Toolset Integration: Supports selective generation of skills based on
defined toolsets, including support for both local files and prebuilt
configurations.
- Testing: Added unit tests for the generation logic and
integration tests for the CLI command.
- Documentation: Created a new "how-to" guide for generating skills and
updated the CLI reference documentation.
2026-02-04 15:51:14 -05:00
Mend Renovate
732eaed41d chore(deps): update github actions (#2386)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/cache](https://redirect.github.com/actions/cache)
([changelog](8b402f58fb..cdf6c1fa76))
| action | digest | `8b402f5` → `cdf6c1f` |
| [actions/checkout](https://redirect.github.com/actions/checkout)
([changelog](8e8c483db8..de0fac2e45))
| action | digest | `8e8c483` → `de0fac2` |

---

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

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- 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:eyJjcmVhdGVkSW5WZXIiOiI0Mi45Mi4xIiwidXBkYXRlZEluVmVyIjoiNDIuOTUuMiIsInRhcmdldEJyYW5jaCI6Im1haW4iLCJsYWJlbHMiOltdfQ==-->

Co-authored-by: Averi Kitsch <akitsch@google.com>
2026-02-04 02:40:22 +00:00
Yuan Teoh
a2097ba8eb docs: add index page for cloud logging admin tools (#2414)
Add _index page for cloud logging admin tools for drop down.

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-02-03 12:22:03 -08: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
Averi Kitsch
4c96bb5c81 docs: fix dataplex tool names in reference (#2366)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

## 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-02 19:13:55 +00:00
Twisha Bansal
e0245946ea docs: standardize installation note for antigravity plugins (#2373)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

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

- [x] 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-01-30 10:25:29 +05:30
Haoyu Wang
6e49ba436e feat(cli/invoke): add support for direct tool invocation from CLI (#2353)
## Description

This PR introduces a new subcommand, invoke, to the toolbox CLI. This
feature allows developers to execute tools defined in their
configuration directly from the command line.

- New Subcommand: Implemented invoke as subcommand, which handles tool
lookup, parameter unmarshaling from JSON, and invocation.
- Persistent Configuration Flags: Updated cmd/root.go to make flags like
--tools-file, --tools-folder, and --prebuilt persistent, allowing them
to be used with subcommands.
- Testing: Added unit tests for various scenarios
- Documentation: Created a new "how-to" guide for CLI tool testing and
updated the CLI reference documentation.
2026-01-29 19:46:21 -05:00
Dennis Geurts
4cff979491 chore(deps): mongodb driver version bump to v2 (#2349)
## Description

Update mongo-driver to v2, as requested by @duwenxin99  

🛠️ Fixes #2335

Co-authored-by: Yuan Teoh <yuanteoh@google.com>
2026-01-29 23:54:21 +00:00
Binh Tran
e995349ea0 feat(prebuiltconfigs/alloydb-omni): Implement Alloydb omni dataplane tools (#2340)
## Description

Implement Dataplane tools for alloydb omni using Postgres source and
postgres tools. I also add more omni-specific tool (columnar engine).

---------

Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2026-01-29 22:47:49 +00:00
Wenxin Du
4c9765f1fb ci(oracle): fix Oracle integration test (#2388) 2026-01-29 20:14:55 +00:00
Mohammed Tanveer
d1358916d8 chore(ui): prevent script execution in Toolbox UI rendering (#2331)
# Defensive Security Hardening: Prevent Script Execution in Toolbox UI
Rendering

> **Note:** This issue was identified during security research and
reviewed previously.
> While typical deployments operate within a trusted configuration
model, addressing this behavior was recommended as a defense-in-depth
improvement. This PR describes the implemented fix.

## Overview

This change improves the safety of the GenAI Toolbox UI by preventing
unintended JavaScript execution when rendering values derived from tool
configuration files.

Previously, certain fields from tool definitions were rendered directly
into HTML contexts without escaping. As a result, tool definitions
containing embedded HTML or script payloads could trigger JavaScript
execution when viewed in the dashboard. While this occurs within the
same trust boundary as the configuration owner, escaping these values by
default avoids unexpected execution and improves robustness.

## Changes Implemented

### 1. New Utility
- Added `sanitize.js` which exports a strict `escapeHtml()` function.
- Escapes dangerous characters: `&`, `<`, `>`, `"`, `'`, `/`, `` ` ``.
- Performs strict type checking, rendering `null` and `undefined` values
as empty strings.

### 2. Input Handling
- Updated `internal/server/static/js/toolDisplay.js` to wrap `tool.name`
and `tool.description` with `escapeHtml()` prior to rendering them into
the DOM.

### 3. Error Handling
- Updated `internal/server/static/js/loadTools.js` to sanitize error
messages that may reflect user-controlled or derived input before
rendering.

## Validation

- Verified behavior using tool definition files containing common script
execution vectors.
- Confirmed that embedded HTML and script payloads are rendered as
literal text.
- Verified that standard and existing tool definitions continue to
render correctly without functional regression.

## Notes

This change is a defense-in-depth hardening measure.  
It does not modify the existing trust model or intended usage patterns,
but ensures safer default rendering behavior and avoids unintended
script execution in the UI.

## Attribution

**Contributor:** Mohammed Tanveer (threatpointer)

---------

Co-authored-by: threatpointer <mohammed.tanveer1@gmail.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2026-01-29 00:22:24 +00:00
Wenxin Du
2d5d33388c refactor: remove ParseParams() from Tool interface (#2375)
The `ParseParams` Tool interface is only passing the tool's parameter
into a generic `parameters.ParseParams()` helper. Instead of keeping it
as a tool interface, we add a `GetParameters()` method
(https://github.com/googleapis/genai-toolbox/pull/2374) to the tool
interface and call it directly from the API handlers. This way we keep
the parameter parsing logic independent from the tools.
2026-01-28 23:01:13 +00:00
Pranjul Kalsi
252fc3091a feat(sources/cloud-logging-admin): add source, tools, integration test and docs (#2137)
## Description

This PR adds cloud logging admin source, tools, integration test and
docs.

1. Source is implemented in a manner consistent with the BigQuery
source. Supports ADC, OAuth and impersonate Service Account.
2. Total of 3 tools have been implemented 
- `cloud-logging-admin-list-log-names` 
- `cloud-logging-admin-list-resource-types` 
- `cloud-logging-admin-query-logs` 
3. docs added for resource and tools.
4. Supporting integration test is added with updated ci

Note for reviewers:
1. Integration test runs on cloud, will require `LOGADMIN_PROJECT` env
variable, the test creates logs in the project using the `logging`
client and then verifies working of the tools using the `logadmin`
client.
2. Moved `cache.go` from the BigQuery source to `sources/cache.go` due
to shared utility.

Regarding Tools:

1. `cloud-logging-admin-list-log-names` uses `client.Logs()` instead of
`client.Entries()`, as the latter is resource heavy and the tradeoff was
not being able to apply any filters, tool has an optional parameter
`limit` which defaults to 200.
2. `cloud-logging-admin-list-resource-types` uses
`client.ResourceDescriptors(ctx)`, aim of the tool is to enable the
agent become aware of the the resources present and utilise this
information in writing filters.
3. `cloud-logging-admin-query-logs` tool enables search and read logs
from Google Cloud.
Parameters: 
 `filter` (optional): A text string to search for specific logs.
 `newestFirst` (optional): A simple true/false switch for ordering.
`startTime ` (optional): The start date and time to search from (e.g.,
2025-12-09T00:00:00Z). Defaults to 30 days ago if not set.
`endTime` (optional): The end date and time to search up to. Defaults to
"now".
`verbose` (optional): If set to true, Shows all available details for
each log entry else shows only the main info (timestamp, message,
severity).
`limit` (optional): The maximum number of log entries to return (default
is 200).

Looking forward to the feedback here, as `verbose` is simply implemented
to save context tokens, any alternative suggestion here is also
welcomed.

Simple tools.yaml
```
sources:
  my-logging-admin:
    kind: cloud-logging-admin
    project: <Add project>
    useClientOAuth: false

tools:
  list_resource_types:
    kind: cloud-logging-admin-list-resource-types
    source: my-logging-admin
    description: List the types of resource that are indexed by Cloud Logging.
  list_log_names:
    kind: cloud-logging-admin-list-log-names
    source: my-logging-admin
    description: List log names matching a filter criteria.
  query_logs:
    kind: cloud-logging-admin-query-logs
    source: my-logging-admin
    description: query logs

``` 

## PR Checklist
- [x] Make sure you reviewed

[CONTRIBUTING.md](https://github.com/googleapis/genai-toolbox/blob/main/CONTRIBUTING.md)
- [x] 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
- [x] Ensure the tests and linter pass
- [x] Code coverage does not decrease (if any source code was changed)
- [x] Appropriate docs were updated (if necessary)
- [ ] Make sure to add `!` if this involve a breaking change

🛠️ Fixes #1772
@anubhav756 @averikitsch Thanks for the guidance and feedback on the
implementation plan.

---------

Co-authored-by: Yuan Teoh <yuanteoh@google.com>
Co-authored-by: Yuan Teoh <45984206+Yuan325@users.noreply.github.com>
2026-01-28 14:31:25 -08:00
Yuan Teoh
10c445b05c docs: update cloudsqlrestorebackup docs with new config format (#2381)
Update docs example for `cloudsqlrestorebackup`.
2026-01-28 13:58:52 -08:00
dishaprakash
341316bb63 chore: Update Version String (#2380)
## Description

This PR modifies the User Agent (version string) generation to better
accommodate the recent support for combining prebuilt and custom tools.

The design for this update is as so:
Single Prebuilt: <version+metadata>+prebuilt.bigquery
Multiple prebuilt: <version+metadata>+prebuilt.bigquery+prebuilt.alloydb
Prebuilt + Custom:  <version+metadata>+custom.bigquery
Multiple prebuilt + custom:
<version+metadata>+custom.bigquery+custom.alloydb

Versioning Logic:
- Single Prebuilt: Appends +prebuilt.<name>
  Example: ```<version>+prebuilt.bigquery```

- Multiple Prebuilt: Appends a tag for each prebuilt config.
  Example: ```<version>+prebuilt.bigquery+prebuilt.alloydb```

- Prebuilt + Custom: If custom tools are detected, the tag for all
prebuilt tools switches to custom.
  Example: ```<version>+custom.bigquery```

- Multiple Prebuilt + Custom: All prebuilt tools receive the custom tag.
  Example: ```<version>+custom.bigquery+custom.alloydb```

## 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-01-28 18:00:39 +00:00
Averi Kitsch
44da09300c docs: remove non-prod header (#2376)
## Description

> Should include a concise description of the changes (bug or feature),
it's
> impact, along with a summary of the solution

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

---------

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-01-28 09:37:32 -08:00
295 changed files with 5984 additions and 1662 deletions

View File

@@ -171,6 +171,23 @@ steps:
alloydbainl \
alloydbainl
- id: "alloydb-omni"
name: golang:1
waitFor: ["compile-test-binary"]
entrypoint: /bin/bash
env:
- "GOPATH=/gopath"
volumes:
- name: "go"
path: "/gopath"
args:
- -c
- |
.ci/test_with_coverage.sh \
"AlloyDB Omni" \
alloydbomni \
postgres
- id: "bigtable"
name: golang:1
waitFor: ["compile-test-binary"]
@@ -295,6 +312,25 @@ steps:
cloudhealthcare \
cloudhealthcare
- id: "cloud-logging-admin"
name: golang:1
waitFor: ["compile-test-binary"]
entrypoint: /bin/bash
env:
- "GOPATH=/gopath"
- "LOGADMIN_PROJECT=$PROJECT_ID"
secretEnv: ["CLIENT_ID"]
volumes:
- name: "go"
path: "/gopath"
args:
- -c
- |
.ci/test_with_coverage.sh \
"Cloud Logging Admin" \
cloudloggingadmin \
cloudloggingadmin
- id: "postgres"
name: golang:1
waitFor: ["compile-test-binary"]
@@ -657,7 +693,7 @@ steps:
"Looker" \
looker \
looker
- id: "mindsdb"
name: golang:1
waitFor: ["compile-test-binary"]
@@ -845,7 +881,7 @@ steps:
"Snowflake" \
snowflake \
snowflake
- id: "cassandra"
name: golang:1
waitFor: ["compile-test-binary"]
@@ -887,16 +923,16 @@ steps:
tar -C /usr/local -xzf go.tar.gz
export PATH="/usr/local/go/bin:$$PATH"
go test -v ./internal/sources/oracle/... \
go test -v ./tests/oracle/... \
-coverprofile=oracle_coverage.out \
-coverpkg=./internal/sources/oracle/...,./internal/tools/oracle/...
# Coverage check
total_coverage=$(go tool cover -func=oracle_coverage.out | grep "total:" | awk '{print $3}')
echo "Oracle total coverage: $total_coverage"
coverage_numeric=$(echo "$total_coverage" | sed 's/%//')
if awk -v cov="$coverage_numeric" 'BEGIN {exit !(cov < 20)}'; then
echo "Coverage failure: $total_coverage is below 20%."
if awk -v cov="$coverage_numeric" 'BEGIN {exit !(cov < 60)}'; then
echo "Coverage failure: $total_coverage is below 60%."
exit 1
fi

View File

@@ -1,125 +0,0 @@
# 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.
#!/bin/bash
set -e
TABLE_NAME="hotels_go"
QUICKSTART_GO_DIR="docs/en/getting-started/quickstart/go"
SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql"
PROXY_PID=""
TOOLBOX_PID=""
install_system_packages() {
apt-get update && apt-get install -y \
postgresql-client \
wget \
gettext-base \
netcat-openbsd
}
start_cloud_sql_proxy() {
wget "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy
chmod +x /usr/local/bin/cloud-sql-proxy
cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" &
PROXY_PID=$!
for i in {1..30}; do
if nc -z 127.0.0.1 5432; then
echo "Cloud SQL Proxy is up and running."
return
fi
sleep 1
done
echo "Cloud SQL Proxy failed to start within the timeout period."
exit 1
}
setup_toolbox() {
TOOLBOX_YAML="/tools.yaml"
echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML"
if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi
wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox"
chmod +x "/toolbox"
/toolbox --tools-file "$TOOLBOX_YAML" &
TOOLBOX_PID=$!
sleep 2
}
setup_orch_table() {
export TABLE_NAME
envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME"
}
run_orch_test() {
local orch_dir="$1"
local orch_name
orch_name=$(basename "$orch_dir")
if [ "$orch_name" == "openAI" ]; then
echo -e "\nSkipping framework '${orch_name}': Temporarily excluded."
return
fi
(
set -e
setup_orch_table
echo "--- Preparing module for $orch_name ---"
cd "$orch_dir"
if [ -f "go.mod" ]; then
go mod tidy
fi
cd ..
export ORCH_NAME="$orch_name"
echo "--- Running tests for $orch_name ---"
go test -v ./...
)
}
cleanup_all() {
echo "--- Final cleanup: Shutting down processes and dropping table ---"
if [ -n "$TOOLBOX_PID" ]; then
kill $TOOLBOX_PID || true
fi
if [ -n "$PROXY_PID" ]; then
kill $PROXY_PID || true
fi
}
trap cleanup_all EXIT
# Main script execution
install_system_packages
start_cloud_sql_proxy
export PGHOST=127.0.0.1
export PGPORT=5432
export PGPASSWORD="$DB_PASSWORD"
export GOOGLE_API_KEY="$GOOGLE_API_KEY"
setup_toolbox
for ORCH_DIR in "$QUICKSTART_GO_DIR"/*/; do
if [ ! -d "$ORCH_DIR" ]; then
continue
fi
run_orch_test "$ORCH_DIR"
done

View File

@@ -1,125 +0,0 @@
# 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.
#!/bin/bash
set -e
TABLE_NAME="hotels_js"
QUICKSTART_JS_DIR="docs/en/getting-started/quickstart/js"
SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql"
# Initialize process IDs to empty at the top of the script
PROXY_PID=""
TOOLBOX_PID=""
install_system_packages() {
apt-get update && apt-get install -y \
postgresql-client \
wget \
gettext-base \
netcat-openbsd
}
start_cloud_sql_proxy() {
wget "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy
chmod +x /usr/local/bin/cloud-sql-proxy
cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" &
PROXY_PID=$!
for i in {1..30}; do
if nc -z 127.0.0.1 5432; then
echo "Cloud SQL Proxy is up and running."
return
fi
sleep 1
done
echo "Cloud SQL Proxy failed to start within the timeout period."
exit 1
}
setup_toolbox() {
TOOLBOX_YAML="/tools.yaml"
echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML"
if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi
wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox"
chmod +x "/toolbox"
/toolbox --tools-file "$TOOLBOX_YAML" &
TOOLBOX_PID=$!
sleep 2
}
setup_orch_table() {
export TABLE_NAME
envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME"
}
run_orch_test() {
local orch_dir="$1"
local orch_name
orch_name=$(basename "$orch_dir")
(
set -e
echo "--- Preparing environment for $orch_name ---"
setup_orch_table
cd "$orch_dir"
echo "Installing dependencies for $orch_name..."
if [ -f "package-lock.json" ]; then
npm ci
else
npm install
fi
cd ..
echo "--- Running tests for $orch_name ---"
export ORCH_NAME="$orch_name"
node --test quickstart.test.js
echo "--- Cleaning environment for $orch_name ---"
rm -rf "${orch_name}/node_modules"
)
}
cleanup_all() {
echo "--- Final cleanup: Shutting down processes and dropping table ---"
if [ -n "$TOOLBOX_PID" ]; then
kill $TOOLBOX_PID || true
fi
if [ -n "$PROXY_PID" ]; then
kill $PROXY_PID || true
fi
}
trap cleanup_all EXIT
# Main script execution
install_system_packages
start_cloud_sql_proxy
export PGHOST=127.0.0.1
export PGPORT=5432
export PGPASSWORD="$DB_PASSWORD"
export GOOGLE_API_KEY="$GOOGLE_API_KEY"
setup_toolbox
for ORCH_DIR in "$QUICKSTART_JS_DIR"/*/; do
if [ ! -d "$ORCH_DIR" ]; then
continue
fi
run_orch_test "$ORCH_DIR"
done

View File

@@ -1,115 +0,0 @@
# 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.
#!/bin/bash
set -e
TABLE_NAME="hotels_python"
QUICKSTART_PYTHON_DIR="docs/en/getting-started/quickstart/python"
SQL_FILE=".ci/quickstart_test/setup_hotels_sample.sql"
PROXY_PID=""
TOOLBOX_PID=""
install_system_packages() {
apt-get update && apt-get install -y \
postgresql-client \
python3-venv \
wget \
gettext-base \
netcat-openbsd
}
start_cloud_sql_proxy() {
wget "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy
chmod +x /usr/local/bin/cloud-sql-proxy
cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" &
PROXY_PID=$!
for i in {1..30}; do
if nc -z 127.0.0.1 5432; then
echo "Cloud SQL Proxy is up and running."
return
fi
sleep 1
done
echo "Cloud SQL Proxy failed to start within the timeout period."
exit 1
}
setup_toolbox() {
TOOLBOX_YAML="/tools.yaml"
echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML"
if [ ! -f "$TOOLBOX_YAML" ]; then echo "Failed to create tools.yaml"; exit 1; fi
wget "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox"
chmod +x "/toolbox"
/toolbox --tools-file "$TOOLBOX_YAML" &
TOOLBOX_PID=$!
sleep 2
}
setup_orch_table() {
export TABLE_NAME
envsubst < "$SQL_FILE" | psql -h "$PGHOST" -p "$PGPORT" -U "$DB_USER" -d "$DATABASE_NAME"
}
run_orch_test() {
local orch_dir="$1"
local orch_name
orch_name=$(basename "$orch_dir")
(
set -e
setup_orch_table
cd "$orch_dir"
local VENV_DIR=".venv"
python3 -m venv "$VENV_DIR"
source "$VENV_DIR/bin/activate"
pip install -r requirements.txt
echo "--- Running tests for $orch_name ---"
cd ..
ORCH_NAME="$orch_name" pytest
rm -rf "$VENV_DIR"
)
}
cleanup_all() {
echo "--- Final cleanup: Shutting down processes and dropping table ---"
if [ -n "$TOOLBOX_PID" ]; then
kill $TOOLBOX_PID || true
fi
if [ -n "$PROXY_PID" ]; then
kill $PROXY_PID || true
fi
}
trap cleanup_all EXIT
# Main script execution
install_system_packages
start_cloud_sql_proxy
export PGHOST=127.0.0.1
export PGPORT=5432
export PGPASSWORD="$DB_PASSWORD"
export GOOGLE_API_KEY="$GOOGLE_API_KEY"
setup_toolbox
for ORCH_DIR in "$QUICKSTART_PYTHON_DIR"/*/; do
if [ ! -d "$ORCH_DIR" ]; then
continue
fi
run_orch_test "$ORCH_DIR"
done

View File

@@ -0,0 +1,57 @@
# 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.
steps:
- name: "${_IMAGE}"
id: "py-pre-post-processing-test"
entrypoint: "bash"
args:
- -c
- |
set -ex
chmod +x .ci/sample_tests/run_tests.sh
.ci/sample_tests/run_tests.sh
env:
- "CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}"
- "GCP_PROJECT=${_GCP_PROJECT}"
- "DATABASE_NAME=${_DATABASE_NAME}"
- "DB_USER=${_DB_USER}"
- "TARGET_ROOT=${_TARGET_ROOT}"
- "TARGET_LANG=${_TARGET_LANG}"
- "TABLE_NAME=${_TABLE_NAME}"
- "SQL_FILE=${_SQL_FILE}"
- "AGENT_FILE_PATTERN=${_AGENT_FILE_PATTERN}"
secretEnv: ["TOOLS_YAML_CONTENT", "GOOGLE_API_KEY", "DB_PASSWORD"]
availableSecrets:
secretManager:
- versionName: projects/${_GCP_PROJECT}/secrets/${_TOOLS_YAML_SECRET}/versions/5
env: "TOOLS_YAML_CONTENT"
- versionName: projects/${_GCP_PROJECT_NUMBER}/secrets/${_API_KEY_SECRET}/versions/latest
env: "GOOGLE_API_KEY"
- versionName: projects/${_GCP_PROJECT}/secrets/${_DB_PASS_SECRET}/versions/latest
env: "DB_PASSWORD"
timeout: 1200s
substitutions:
_TARGET_LANG: "python"
_IMAGE: "gcr.io/google.com/cloudsdktool/cloud-sdk:537.0.0"
_TARGET_ROOT: "docs/en/samples/pre_post_processing/python"
_TABLE_NAME: "hotels_py_pre_post_processing"
_SQL_FILE: ".ci/sample_tests/setup_hotels.sql"
_AGENT_FILE_PATTERN: "agent.py"
options:
logging: CLOUD_LOGGING_ONLY

View File

@@ -23,13 +23,18 @@ steps:
- |
set -ex
export VERSION=$(cat ./cmd/version.txt)
chmod +x .ci/quickstart_test/run_go_tests.sh
.ci/quickstart_test/run_go_tests.sh
chmod +x .ci/sample_tests/run_tests.sh
.ci/sample_tests/run_tests.sh
env:
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
- 'GCP_PROJECT=${_GCP_PROJECT}'
- 'DATABASE_NAME=${_DATABASE_NAME}'
- 'DB_USER=${_DB_USER}'
- 'TARGET_ROOT=docs/en/getting-started/quickstart/go'
- 'TARGET_LANG=go'
- 'TABLE_NAME=hotels_go'
- 'SQL_FILE=.ci/sample_tests/setup_hotels.sql'
- 'AGENT_FILE_PATTERN=quickstart.go'
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
availableSecrets:

View File

@@ -23,13 +23,18 @@ steps:
- |
set -ex
export VERSION=$(cat ./cmd/version.txt)
chmod +x .ci/quickstart_test/run_js_tests.sh
.ci/quickstart_test/run_js_tests.sh
chmod +x .ci/sample_tests/run_tests.sh
.ci/sample_tests/run_tests.sh
env:
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
- 'GCP_PROJECT=${_GCP_PROJECT}'
- 'DATABASE_NAME=${_DATABASE_NAME}'
- 'DB_USER=${_DB_USER}'
- 'TARGET_ROOT=docs/en/getting-started/quickstart/js'
- 'TARGET_LANG=js'
- 'TABLE_NAME=hotels_js'
- 'SQL_FILE=.ci/sample_tests/setup_hotels.sql'
- 'AGENT_FILE_PATTERN=quickstart.js'
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
availableSecrets:

View File

@@ -23,13 +23,18 @@ steps:
- |
set -ex
export VERSION=$(cat ./cmd/version.txt)
chmod +x .ci/quickstart_test/run_py_tests.sh
.ci/quickstart_test/run_py_tests.sh
chmod +x .ci/sample_tests/run_tests.sh
.ci/sample_tests/run_tests.sh
env:
- 'CLOUD_SQL_INSTANCE=${_CLOUD_SQL_INSTANCE}'
- 'GCP_PROJECT=${_GCP_PROJECT}'
- 'DATABASE_NAME=${_DATABASE_NAME}'
- 'DB_USER=${_DB_USER}'
- 'TARGET_ROOT=docs/en/getting-started/quickstart/python'
- 'TARGET_LANG=python'
- 'TABLE_NAME=hotels_python'
- 'SQL_FILE=.ci/sample_tests/setup_hotels.sql'
- 'AGENT_FILE_PATTERN=quickstart.py'
secretEnv: ['TOOLS_YAML_CONTENT', 'GOOGLE_API_KEY', 'DB_PASSWORD']
availableSecrets:

View File

@@ -0,0 +1,202 @@
# 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.
set -e
# --- Configuration (from Environment Variables) ---
# TARGET_ROOT: The directory to search for tests (e.g., docs/en/getting-started/quickstart/js)
# TARGET_LANG: python, js, go
# TABLE_NAME: Database table name to use
# SQL_FILE: Path to the SQL setup file
# AGENT_FILE_PATTERN: Filename to look for (e.g., quickstart.js or agent.py)
VERSION=$(cat ./cmd/version.txt)
# Process IDs & Logs
PROXY_PID=""
TOOLBOX_PID=""
PROXY_LOG="cloud_sql_proxy.log"
TOOLBOX_LOG="toolbox_server.log"
install_system_packages() {
echo "Installing system packages..."
apt-get update && apt-get install -y \
postgresql-client \
wget \
gettext-base \
netcat-openbsd
if [[ "$TARGET_LANG" == "python" ]]; then
apt-get install -y python3-venv
fi
}
start_cloud_sql_proxy() {
echo "Starting Cloud SQL Proxy..."
wget -q "https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.10.0/cloud-sql-proxy.linux.amd64" -O /usr/local/bin/cloud-sql-proxy
chmod +x /usr/local/bin/cloud-sql-proxy
cloud-sql-proxy "${CLOUD_SQL_INSTANCE}" > "$PROXY_LOG" 2>&1 &
PROXY_PID=$!
# Health Check
for i in {1..30}; do
if nc -z 127.0.0.1 5432; then
echo "Cloud SQL Proxy is up and running."
return
fi
sleep 1
done
echo "ERROR: Cloud SQL Proxy failed to start. Logs:"
cat "$PROXY_LOG"
exit 1
}
setup_toolbox() {
echo "Setting up Toolbox server..."
TOOLBOX_YAML="/tools.yaml"
echo "${TOOLS_YAML_CONTENT}" > "$TOOLBOX_YAML"
wget -q "https://storage.googleapis.com/genai-toolbox/v${VERSION}/linux/amd64/toolbox" -O "/toolbox"
chmod +x "/toolbox"
/toolbox --tools-file "$TOOLBOX_YAML" > "$TOOLBOX_LOG" 2>&1 &
TOOLBOX_PID=$!
# Health Check
for i in {1..15}; do
if nc -z 127.0.0.1 5000; then
echo "Toolbox server is up and running."
return
fi
sleep 1
done
echo "ERROR: Toolbox server failed to start. Logs:"
cat "$TOOLBOX_LOG"
exit 1
}
setup_db_table() {
echo "Setting up database table $TABLE_NAME using $SQL_FILE..."
export TABLE_NAME
envsubst < "$SQL_FILE" | psql -h 127.0.0.1 -p 5432 -U "$DB_USER" -d "$DATABASE_NAME"
}
run_python_test() {
local dir=$1
local name=$(basename "$dir")
echo "--- Running Python Test: $name ---"
(
cd "$dir"
python3 -m venv .venv
source .venv/bin/activate
pip install -q -r requirements.txt pytest
cd ..
local test_file=$(find . -maxdepth 1 -name "*test.py" | head -n 1)
if [ -n "$test_file" ]; then
echo "Found native test: $test_file. Running pytest..."
export ORCH_NAME="$name"
export PYTHONPATH="../"
pytest "$test_file"
else
echo "No native test found. running agent directly..."
export PYTHONPATH="../"
python3 "${name}/${AGENT_FILE_PATTERN}"
fi
rm -rf "${name}/.venv"
)
}
run_js_test() {
local dir=$1
local name=$(basename "$dir")
echo "--- Running JS Test: $name ---"
(
cd "$dir"
if [ -f "package-lock.json" ]; then npm ci -q; else npm install -q; fi
cd ..
# Looking for a JS test file in the parent directory
local test_file=$(find . -maxdepth 1 -name "*test.js" | head -n 1)
if [ -n "$test_file" ]; then
echo "Found native test: $test_file. Running node --test..."
export ORCH_NAME="$name"
node --test "$test_file"
else
echo "No native test found. running agent directly..."
node "${name}/${AGENT_FILE_PATTERN}"
fi
rm -rf "${name}/node_modules"
)
}
run_go_test() {
local dir=$1
local name=$(basename "$dir")
if [ "$name" == "openAI" ]; then
echo -e "\nSkipping framework '${name}': Temporarily excluded."
return
fi
echo "--- Running Go Test: $name ---"
(
cd "$dir"
if [ -f "go.mod" ]; then
go mod tidy
fi
cd ..
local test_file=$(find . -maxdepth 1 -name "*test.go" | head -n 1)
if [ -n "$test_file" ]; then
echo "Found native test: $test_file. Running go test..."
export ORCH_NAME="$name"
go test -v ./...
else
echo "No native test found. running agent directly..."
cd "$name"
go run "."
fi
)
}
cleanup() {
echo "Cleaning up background processes..."
[ -n "$TOOLBOX_PID" ] && kill "$TOOLBOX_PID" || true
[ -n "$PROXY_PID" ] && kill "$PROXY_PID" || true
}
trap cleanup EXIT
# --- Execution ---
install_system_packages
start_cloud_sql_proxy
export PGHOST=127.0.0.1
export PGPORT=5432
export PGPASSWORD="$DB_PASSWORD"
export GOOGLE_API_KEY="$GOOGLE_API_KEY"
setup_toolbox
setup_db_table
echo "Scanning $TARGET_ROOT for tests with pattern $AGENT_FILE_PATTERN..."
find "$TARGET_ROOT" -name "$AGENT_FILE_PATTERN" | while read -r agent_file; do
sample_dir=$(dirname "$agent_file")
if [[ "$TARGET_LANG" == "python" ]]; then
run_python_test "$sample_dir"
elif [[ "$TARGET_LANG" == "js" ]]; then
run_js_test "$sample_dir"
elif [[ "$TARGET_LANG" == "go" ]]; then
run_go_test "$sample_dir"
fi
done

View File

@@ -40,7 +40,7 @@ jobs:
group: docs-deployment
cancel-in-progress: false
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
@@ -56,7 +56,7 @@ jobs:
node-version: "22"
- name: Cache dependencies
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

View File

@@ -30,14 +30,14 @@ jobs:
steps:
- name: Checkout main branch (for latest templates and theme)
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: 'main'
submodules: 'recursive'
fetch-depth: 0
- name: Checkout old content from tag into a temporary directory
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.event.inputs.version_tag }}
path: 'old_version_source' # Checkout into a temp subdir

View File

@@ -30,12 +30,14 @@ jobs:
cancel-in-progress: false
steps:
- name: Checkout Code at Tag
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: ${{ github.event.release.tag_name }}
- name: Get Version from Release Tag
run: echo "VERSION=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
run: echo "VERSION=${GITHUB_EVENT_RELEASE_TAG_NAME}" >> $GITHUB_ENV
env:
GITHUB_EVENT_RELEASE_TAG_NAME: ${{ github.event.release.tag_name }}
- name: Setup Hugo
uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3

View File

@@ -34,7 +34,7 @@ jobs:
group: "preview-${{ github.event.number }}"
cancel-in-progress: true
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
ref: versioned-gh-pages

View File

@@ -49,7 +49,7 @@ jobs:
group: "preview-${{ github.event.number }}"
cancel-in-progress: true
steps:
- uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
# Checkout the PR's HEAD commit (supports forks).
ref: ${{ github.event.pull_request.head.sha }}
@@ -67,7 +67,7 @@ jobs:
node-version: "22"
- name: Cache dependencies
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

View File

@@ -0,0 +1,72 @@
# 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.
name: Weekly Link Check
on:
# Run automatically every Monday at 00:00 UTC
schedule:
- cron: '0 0 * * 1'
# Optional: Keep this to allow manual triggering from the Actions tab
workflow_dispatch:
jobs:
link-check:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Restore lychee cache
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
restore-keys: cache-lychee-
- name: Link Checker
id: lychee-check
uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2
continue-on-error: true
with:
args: >
--quiet
--no-progress
--cache
--max-cache-age 1d
--exclude '^neo4j\+.*' --exclude '^bolt://.*'
README.md
docs/
output: lychee-report.md
format: markdown
fail: true
jobSummary: false
debug: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Display Failure Report
# Run this ONLY if the link checker failed
if: steps.lychee-check.outcome == 'failure'
run: |
echo "## Link Resolution Note" >> $GITHUB_STEP_SUMMARY
echo "Local links and directory changes work differently on GitHub than on the docsite." >> $GITHUB_STEP_SUMMARY
echo "You must ensure fixes pass the **GitHub check** and also work with **\`hugo server\`**." >> $GITHUB_STEP_SUMMARY
echo "See [Link Checking and Fixing with Lychee](https://github.com/googleapis/genai-toolbox/blob/main/DEVELOPER.md#link-checking-and-fixing-with-lychee) for more details." >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "### Broken Links Found" >> $GITHUB_STEP_SUMMARY
cat ./lychee-report.md >> $GITHUB_STEP_SUMMARY
exit 1

View File

@@ -22,39 +22,47 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Restore lychee cache
uses: actions/cache@8b402f58fbc84540c8b491a91e594a4576fec3d7 # v5
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v5
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
restore-keys: cache-lychee-
- name: Link Checker
id: lychee-check
uses: lycheeverse/lychee-action@a8c4c7cb88f0c7386610c35eb25108e448569cb0 # v2
continue-on-error: true
with:
args: >
--verbose
--quiet
--no-progress
--cache
--max-cache-age 1d
--exclude '^neo4j\+.*' --exclude '^bolt://.*'
README.md
docs/
output: /tmp/foo.txt
fail: true
jobSummary: true
debug: true
output: lychee-report.md
format: markdown
fail: true
jobSummary: false
debug: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# This step only runs if the 'lychee_check' step fails, ensuring the
# context note only appears when the developer needs to troubleshoot.
- name: Display Link Context Note on Failure
if: ${{ failure() }}
- name: Display Failure Report
# Run this ONLY if the link checker failed
if: steps.lychee-check.outcome == 'failure'
run: |
echo "## Link Resolution Note" >> $GITHUB_STEP_SUMMARY
echo "Local links and directory changes work differently on GitHub than on the docsite." >> $GITHUB_STEP_SUMMARY
echo "You must ensure fixes pass the **GitHub check** and also work with **\`hugo server\`**." >> $GITHUB_STEP_SUMMARY
echo "See [Link Checking and Fixing with Lychee](https://github.com/googleapis/genai-toolbox/blob/main/DEVELOPER.md#link-checking-and-fixing-with-lychee) for more details." >> $GITHUB_STEP_SUMMARY
echo "---" >> $GITHUB_STEP_SUMMARY
echo "### Broken Links Found" >> $GITHUB_STEP_SUMMARY
cat ./lychee-report.md >> $GITHUB_STEP_SUMMARY
exit 1

View File

@@ -29,7 +29,7 @@ jobs:
steps:
- name: Checkout code
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Wait for image in Artifact Registry
shell: bash

View File

@@ -23,8 +23,7 @@ https://cloud.dgraph.io/login
https://dgraph.io/docs
# MySQL Community downloads and main site (often protected by bot mitigation)
https://dev.mysql.com/downloads/installer/
https://www.mysql.com/
^https?://(.*\.)?mysql\.com/.*
# Claude desktop download link
https://claude.ai/download
@@ -37,9 +36,9 @@ https://dev.mysql.com/doc/refman/8.4/en/sql-prepared-statements.html
https://dev.mysql.com/doc/refman/8.4/en/user-names.html
# npmjs links can occasionally trigger rate limiting during high-frequency CI builds
https://www.npmjs.com/package/@toolbox-sdk/core
https://www.npmjs.com/package/@toolbox-sdk/adk
^https?://(www\.)?npmjs\.com/.*
https://www.oceanbase.com/
# Ignore social media and blog profiles to reduce external request overhead
https://medium.com/@mcp_toolbox
https://medium.com/@mcp_toolbox

View File

@@ -107,7 +107,7 @@ redeploying your application.
## Getting Started
### (Non-production) Running Toolbox
### Quickstart: Running Toolbox using NPX
You can run Toolbox directly with a [configuration file](#configuration):

131
cmd/invoke_tool_test.go Normal file
View File

@@ -0,0 +1,131 @@
// 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"
)
func TestInvokeTool(t *testing.T) {
// Create a temporary tools file
tmpDir := t.TempDir()
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"
echo-tool:
kind: sqlite-sql
source: my-sqlite
description: "echo tool"
statement: "SELECT ? as msg"
parameters:
- name: message
type: string
description: message to echo
`
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)
}
tcs := []struct {
desc string
args []string
want string
wantErr bool
errStr string
}{
{
desc: "success - basic tool call",
args: []string{"invoke", "hello-sqlite", "--tools-file", toolsFilePath},
want: `"greeting": "hello"`,
},
{
desc: "success - tool call with parameters",
args: []string{"invoke", "echo-tool", `{"message": "world"}`, "--tools-file", toolsFilePath},
want: `"msg": "world"`,
},
{
desc: "error - tool not found",
args: []string{"invoke", "non-existent", "--tools-file", toolsFilePath},
wantErr: true,
errStr: `tool "non-existent" not found`,
},
{
desc: "error - invalid JSON params",
args: []string{"invoke", "echo-tool", `invalid-json`, "--tools-file", toolsFilePath},
wantErr: true,
errStr: `params must be a valid JSON string`,
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
_, got, err := invokeCommandWithContext(context.Background(), tc.args)
if (err != nil) != tc.wantErr {
t.Fatalf("got error %v, wantErr %v", err, tc.wantErr)
}
if tc.wantErr && !strings.Contains(err.Error(), tc.errStr) {
t.Fatalf("got error %v, want error containing %q", err, tc.errStr)
}
if !tc.wantErr && !strings.Contains(got, tc.want) {
t.Fatalf("got %q, want it to contain %q", got, tc.want)
}
})
}
}
func TestInvokeTool_AuthUnsupported(t *testing.T) {
tmpDir := t.TempDir()
toolsFileContent := `
sources:
my-bq:
kind: bigquery
project: my-project
useClientOAuth: true
tools:
bq-tool:
kind: bigquery-sql
source: my-bq
description: "bq tool"
statement: "SELECT 1"
`
toolsFilePath := filepath.Join(tmpDir, "auth_tools.yaml")
if err := os.WriteFile(toolsFilePath, []byte(toolsFileContent), 0644); err != nil {
t.Fatalf("failed to write tools file: %v", err)
}
args := []string{"invoke", "bq-tool", "--tools-file", toolsFilePath}
_, _, err := invokeCommandWithContext(context.Background(), args)
if err == nil {
t.Fatal("expected error for tool requiring client auth, but got nil")
}
if !strings.Contains(err.Error(), "client authorization is not supported") {
t.Fatalf("unexpected error message: %v", err)
}
}

View File

@@ -34,6 +34,8 @@ import (
"github.com/fsnotify/fsnotify"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/auth"
"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/log"
"github.com/googleapis/genai-toolbox/internal/prebuiltconfigs"
@@ -91,6 +93,9 @@ import (
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/cloudhealthcaresearchdicominstances"
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/cloudhealthcaresearchdicomseries"
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudhealthcare/cloudhealthcaresearchdicomstudies"
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudloggingadmin/cloudloggingadminlistlognames"
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudloggingadmin/cloudloggingadminlistresourcetypes"
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudloggingadmin/cloudloggingadminquerylogs"
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudmonitoring"
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudsql/cloudsqlcloneinstance"
_ "github.com/googleapis/genai-toolbox/internal/tools/cloudsql/cloudsqlcreatebackup"
@@ -158,6 +163,7 @@ import (
_ "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerrundashboard"
_ "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerrunlook"
_ "github.com/googleapis/genai-toolbox/internal/tools/looker/lookerupdateprojectfile"
_ "github.com/googleapis/genai-toolbox/internal/tools/looker/lookervalidateproject"
_ "github.com/googleapis/genai-toolbox/internal/tools/mindsdb/mindsdbexecutesql"
_ "github.com/googleapis/genai-toolbox/internal/tools/mindsdb/mindsdbsql"
_ "github.com/googleapis/genai-toolbox/internal/tools/mongodb/mongodbaggregate"
@@ -244,6 +250,7 @@ import (
_ "github.com/googleapis/genai-toolbox/internal/sources/clickhouse"
_ "github.com/googleapis/genai-toolbox/internal/sources/cloudgda"
_ "github.com/googleapis/genai-toolbox/internal/sources/cloudhealthcare"
_ "github.com/googleapis/genai-toolbox/internal/sources/cloudloggingadmin"
_ "github.com/googleapis/genai-toolbox/internal/sources/cloudmonitoring"
_ "github.com/googleapis/genai-toolbox/internal/sources/cloudsqladmin"
_ "github.com/googleapis/genai-toolbox/internal/sources/cloudsqlmssql"
@@ -361,37 +368,44 @@ func NewCommand(opts ...Option) *Command {
baseCmd.SetErr(cmd.errStream)
flags := cmd.Flags()
persistentFlags := cmd.PersistentFlags()
flags.StringVarP(&cmd.cfg.Address, "address", "a", "127.0.0.1", "Address of the interface the server will listen on.")
flags.IntVarP(&cmd.cfg.Port, "port", "p", 5000, "Port the server will listen on.")
flags.StringVar(&cmd.tools_file, "tools_file", "", "File path specifying the tool configuration. Cannot be used with --tools-files, or --tools-folder.")
// deprecate tools_file
_ = flags.MarkDeprecated("tools_file", "please use --tools-file instead")
flags.StringVar(&cmd.tools_file, "tools-file", "", "File path specifying the tool configuration. Cannot be used with --tools-files, or --tools-folder.")
flags.StringSliceVar(&cmd.tools_files, "tools-files", []string{}, "Multiple file paths specifying tool configurations. Files will be merged. Cannot be used with --tools-file, or --tools-folder.")
flags.StringVar(&cmd.tools_folder, "tools-folder", "", "Directory path containing YAML tool configuration files. All .yaml and .yml files in the directory will be loaded and merged. Cannot be used with --tools-file, or --tools-files.")
flags.Var(&cmd.cfg.LogLevel, "log-level", "Specify the minimum level logged. Allowed: 'DEBUG', 'INFO', 'WARN', 'ERROR'.")
flags.Var(&cmd.cfg.LoggingFormat, "logging-format", "Specify logging format to use. Allowed: 'standard' or 'JSON'.")
flags.BoolVar(&cmd.cfg.TelemetryGCP, "telemetry-gcp", false, "Enable exporting directly to Google Cloud Monitoring.")
flags.StringVar(&cmd.cfg.TelemetryOTLP, "telemetry-otlp", "", "Enable exporting using OpenTelemetry Protocol (OTLP) to the specified endpoint (e.g. 'http://127.0.0.1:4318')")
flags.StringVar(&cmd.cfg.TelemetryServiceName, "telemetry-service-name", "toolbox", "Sets the value of the service.name resource attribute for telemetry data.")
persistentFlags.StringVar(&cmd.tools_file, "tools-file", "", "File path specifying the tool configuration. Cannot be used with --tools-files, or --tools-folder.")
persistentFlags.StringSliceVar(&cmd.tools_files, "tools-files", []string{}, "Multiple file paths specifying tool configurations. Files will be merged. Cannot be used with --tools-file, or --tools-folder.")
persistentFlags.StringVar(&cmd.tools_folder, "tools-folder", "", "Directory path containing YAML tool configuration files. All .yaml and .yml files in the directory will be loaded and merged. Cannot be used with --tools-file, or --tools-files.")
persistentFlags.Var(&cmd.cfg.LogLevel, "log-level", "Specify the minimum level logged. Allowed: 'DEBUG', 'INFO', 'WARN', 'ERROR'.")
persistentFlags.Var(&cmd.cfg.LoggingFormat, "logging-format", "Specify logging format to use. Allowed: 'standard' or 'JSON'.")
persistentFlags.BoolVar(&cmd.cfg.TelemetryGCP, "telemetry-gcp", false, "Enable exporting directly to Google Cloud Monitoring.")
persistentFlags.StringVar(&cmd.cfg.TelemetryOTLP, "telemetry-otlp", "", "Enable exporting using OpenTelemetry Protocol (OTLP) to the specified endpoint (e.g. 'http://127.0.0.1:4318')")
persistentFlags.StringVar(&cmd.cfg.TelemetryServiceName, "telemetry-service-name", "toolbox", "Sets the value of the service.name resource attribute for telemetry data.")
// Fetch prebuilt tools sources to customize the help description
prebuiltHelp := fmt.Sprintf(
"Use a prebuilt tool configuration by source type. Allowed: '%s'. Can be specified multiple times.",
strings.Join(prebuiltconfigs.GetPrebuiltSources(), "', '"),
)
flags.StringSliceVar(&cmd.prebuiltConfigs, "prebuilt", []string{}, prebuiltHelp)
persistentFlags.StringSliceVar(&cmd.prebuiltConfigs, "prebuilt", []string{}, prebuiltHelp)
flags.BoolVar(&cmd.cfg.Stdio, "stdio", false, "Listens via MCP STDIO instead of acting as a remote HTTP server.")
flags.BoolVar(&cmd.cfg.DisableReload, "disable-reload", false, "Disables dynamic reloading of tools file.")
flags.BoolVar(&cmd.cfg.UI, "ui", false, "Launches the Toolbox UI web server.")
// TODO: Insecure by default. Might consider updating this for v1.0.0
flags.StringSliceVar(&cmd.cfg.AllowedOrigins, "allowed-origins", []string{"*"}, "Specifies a list of origins permitted to access this server. Defaults to '*'.")
flags.StringSliceVar(&cmd.cfg.AllowedHosts, "allowed-hosts", []string{"*"}, "Specifies a list of hosts permitted to access this server. Defaults to '*'.")
flags.StringSliceVar(&cmd.cfg.UserAgentMetadata, "user-agent-metadata", []string{}, "Appends additional metadata to the User-Agent.")
persistentFlags.StringSliceVar(&cmd.cfg.UserAgentMetadata, "user-agent-metadata", []string{}, "Appends additional metadata to the User-Agent.")
// wrap RunE command so that we have access to original Command object
cmd.RunE = func(*cobra.Command, []string) error { return run(cmd) }
// Register subcommands for tool invocation
baseCmd.AddCommand(invoke.NewCommand(cmd))
// Register subcommands for skill generation
baseCmd.AddCommand(skills.NewCommand(cmd))
return cmd
}
@@ -915,70 +929,23 @@ func resolveWatcherInputs(toolsFile string, toolsFiles []string, toolsFolder str
return watchDirs, watchedFiles
}
func run(cmd *Command) error {
ctx, cancel := context.WithCancel(cmd.Context())
defer cancel()
func (cmd *Command) Config() server.ServerConfig {
return cmd.cfg
}
// watch for sigterm / sigint signals
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
go func(sCtx context.Context) {
var s os.Signal
select {
case <-sCtx.Done():
// this should only happen when the context supplied when testing is canceled
return
case s = <-signals:
}
switch s {
case syscall.SIGINT:
cmd.logger.DebugContext(sCtx, "Received SIGINT signal to shutdown.")
case syscall.SIGTERM:
cmd.logger.DebugContext(sCtx, "Sending SIGTERM signal to shutdown.")
}
cancel()
}(ctx)
func (cmd *Command) Out() io.Writer {
return cmd.outStream
}
// If stdio, set logger's out stream (usually DEBUG and INFO logs) to errStream
loggerOut := cmd.outStream
if cmd.cfg.Stdio {
loggerOut = cmd.errStream
}
func (cmd *Command) Logger() log.Logger {
return cmd.logger
}
// Handle logger separately from config
switch strings.ToLower(cmd.cfg.LoggingFormat.String()) {
case "json":
logger, err := log.NewStructuredLogger(loggerOut, cmd.errStream, cmd.cfg.LogLevel.String())
if err != nil {
return fmt.Errorf("unable to initialize logger: %w", err)
}
cmd.logger = logger
case "standard":
logger, err := log.NewStdLogger(loggerOut, cmd.errStream, cmd.cfg.LogLevel.String())
if err != nil {
return fmt.Errorf("unable to initialize logger: %w", err)
}
cmd.logger = logger
default:
return fmt.Errorf("logging format invalid")
}
ctx = util.WithLogger(ctx, cmd.logger)
// Set up OpenTelemetry
otelShutdown, err := telemetry.SetupOTel(ctx, cmd.cfg.Version, cmd.cfg.TelemetryOTLP, cmd.cfg.TelemetryGCP, cmd.cfg.TelemetryServiceName)
func (cmd *Command) LoadConfig(ctx context.Context) error {
logger, err := util.LoggerFromContext(ctx)
if err != nil {
errMsg := fmt.Errorf("error setting up OpenTelemetry: %w", err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
return errMsg
return err
}
defer func() {
err := otelShutdown(ctx)
if err != nil {
errMsg := fmt.Errorf("error shutting down OpenTelemetry: %w", err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
}
}()
var allToolsFiles []ToolsFile
@@ -988,23 +955,20 @@ func run(cmd *Command) error {
slices.Sort(cmd.prebuiltConfigs)
sourcesList := strings.Join(cmd.prebuiltConfigs, ", ")
logMsg := fmt.Sprintf("Using prebuilt tool configurations for: %s", sourcesList)
cmd.logger.InfoContext(ctx, logMsg)
logger.InfoContext(ctx, logMsg)
for _, configName := range cmd.prebuiltConfigs {
buf, err := prebuiltconfigs.Get(configName)
if err != nil {
cmd.logger.ErrorContext(ctx, err.Error())
logger.ErrorContext(ctx, err.Error())
return err
}
// Update version string
cmd.cfg.Version += "+prebuilt." + configName
// Parse into ToolsFile struct
parsed, err := parseToolsFile(ctx, buf)
if err != nil {
errMsg := fmt.Errorf("unable to parse prebuilt tool configuration for '%s': %w", configName, err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
allToolsFiles = append(allToolsFiles, parsed)
@@ -1030,7 +994,7 @@ func run(cmd *Command) error {
(cmd.tools_file != "" && cmd.tools_folder != "") ||
(len(cmd.tools_files) > 0 && cmd.tools_folder != "") {
errMsg := fmt.Errorf("--tools-file, --tools-files, and --tools-folder flags cannot be used simultaneously")
cmd.logger.ErrorContext(ctx, errMsg.Error())
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
@@ -1039,18 +1003,18 @@ func run(cmd *Command) error {
if len(cmd.tools_files) > 0 {
// Use tools-files
cmd.logger.InfoContext(ctx, fmt.Sprintf("Loading and merging %d tool configuration files", len(cmd.tools_files)))
logger.InfoContext(ctx, fmt.Sprintf("Loading and merging %d tool configuration files", len(cmd.tools_files)))
customTools, err = loadAndMergeToolsFiles(ctx, cmd.tools_files)
} else if cmd.tools_folder != "" {
// Use tools-folder
cmd.logger.InfoContext(ctx, fmt.Sprintf("Loading and merging all YAML files from directory: %s", cmd.tools_folder))
logger.InfoContext(ctx, fmt.Sprintf("Loading and merging all YAML files from directory: %s", cmd.tools_folder))
customTools, err = loadAndMergeToolsFolder(ctx, cmd.tools_folder)
} else {
// Use single file (tools-file or default `tools.yaml`)
buf, readFileErr := os.ReadFile(cmd.tools_file)
if readFileErr != nil {
errMsg := fmt.Errorf("unable to read tool file at %q: %w", cmd.tools_file, readFileErr)
cmd.logger.ErrorContext(ctx, errMsg.Error())
logger.ErrorContext(ctx, errMsg.Error())
return errMsg
}
customTools, err = parseToolsFile(ctx, buf)
@@ -1060,17 +1024,29 @@ func run(cmd *Command) error {
}
if err != nil {
cmd.logger.ErrorContext(ctx, err.Error())
logger.ErrorContext(ctx, err.Error())
return err
}
allToolsFiles = append(allToolsFiles, customTools)
}
// Modify version string based on loaded configurations
if len(cmd.prebuiltConfigs) > 0 {
tag := "prebuilt"
if isCustomConfigured {
tag = "custom"
}
// cmd.prebuiltConfigs is already sorted above
for _, configName := range cmd.prebuiltConfigs {
cmd.cfg.Version += fmt.Sprintf("+%s.%s", tag, configName)
}
}
// Merge Everything
// This will error if custom tools collide with prebuilt tools
finalToolsFile, err := mergeToolsFiles(allToolsFiles...)
if err != nil {
cmd.logger.ErrorContext(ctx, err.Error())
logger.ErrorContext(ctx, err.Error())
return err
}
@@ -1081,15 +1057,91 @@ func run(cmd *Command) error {
cmd.cfg.ToolsetConfigs = finalToolsFile.Toolsets
cmd.cfg.PromptConfigs = finalToolsFile.Prompts
instrumentation, err := telemetry.CreateTelemetryInstrumentation(versionString)
return nil
}
func (cmd *Command) Setup(ctx context.Context) (context.Context, func(context.Context) error, error) {
// If stdio, set logger's out stream (usually DEBUG and INFO logs) to errStream
loggerOut := cmd.outStream
if cmd.cfg.Stdio {
loggerOut = cmd.errStream
}
// Handle logger separately from config
logger, err := log.NewLogger(cmd.cfg.LoggingFormat.String(), cmd.cfg.LogLevel.String(), loggerOut, cmd.errStream)
if err != nil {
return ctx, nil, fmt.Errorf("unable to initialize logger: %w", err)
}
cmd.logger = logger
ctx = util.WithLogger(ctx, cmd.logger)
// Set up OpenTelemetry
otelShutdown, err := telemetry.SetupOTel(ctx, cmd.cfg.Version, cmd.cfg.TelemetryOTLP, cmd.cfg.TelemetryGCP, cmd.cfg.TelemetryServiceName)
if err != nil {
errMsg := fmt.Errorf("error setting up OpenTelemetry: %w", err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
return ctx, nil, errMsg
}
shutdownFunc := func(ctx context.Context) error {
err := otelShutdown(ctx)
if err != nil {
errMsg := fmt.Errorf("error shutting down OpenTelemetry: %w", err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
return err
}
return nil
}
instrumentation, err := telemetry.CreateTelemetryInstrumentation(cmd.cfg.Version)
if err != nil {
errMsg := fmt.Errorf("unable to create telemetry instrumentation: %w", err)
cmd.logger.ErrorContext(ctx, errMsg.Error())
return errMsg
return ctx, shutdownFunc, errMsg
}
ctx = util.WithInstrumentation(ctx, instrumentation)
return ctx, shutdownFunc, nil
}
func run(cmd *Command) error {
ctx, cancel := context.WithCancel(cmd.Context())
defer cancel()
// watch for sigterm / sigint signals
signals := make(chan os.Signal, 1)
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
go func(sCtx context.Context) {
var s os.Signal
select {
case <-sCtx.Done():
// this should only happen when the context supplied when testing is canceled
return
case s = <-signals:
}
switch s {
case syscall.SIGINT:
cmd.logger.DebugContext(sCtx, "Received SIGINT signal to shutdown.")
case syscall.SIGTERM:
cmd.logger.DebugContext(sCtx, "Sending SIGTERM signal to shutdown.")
}
cancel()
}(ctx)
ctx, shutdown, err := cmd.Setup(ctx)
if err != nil {
return err
}
defer func() {
_ = shutdown(ctx)
}()
if err := cmd.LoadConfig(ctx); err != nil {
return err
}
// start server
s, err := server.NewServer(ctx, cmd.cfg)
if err != nil {
@@ -1129,6 +1181,9 @@ func run(cmd *Command) error {
}()
}
// Determine if Custom Files are configured (re-check as loadAndMergeConfig might have updated defaults)
isCustomConfigured := cmd.tools_file != "" || len(cmd.tools_files) > 0 || cmd.tools_folder != ""
if isCustomConfigured && !cmd.cfg.DisableReload {
watchDirs, watchedFiles := resolveWatcherInputs(cmd.tools_file, cmd.tools_files, cmd.tools_folder)
// start watching the file(s) or folder for changes to trigger dynamic reloading

View File

@@ -1624,7 +1624,7 @@ func TestEnvVarReplacement(t *testing.T) {
${toolset_name}:
- example_tool
prompts:
${prompt_name}:
description: A test prompt for {{.name}}.
@@ -2054,6 +2054,7 @@ func TestSingleEdit(t *testing.T) {
func TestPrebuiltTools(t *testing.T) {
// Get prebuilt configs
alloydb_omni_config, _ := prebuiltconfigs.Get("alloydb-omni")
alloydb_admin_config, _ := prebuiltconfigs.Get("alloydb-postgres-admin")
alloydb_config, _ := prebuiltconfigs.Get("alloydb-postgres")
bigquery_config, _ := prebuiltconfigs.Get("bigquery")
@@ -2104,6 +2105,12 @@ func TestPrebuiltTools(t *testing.T) {
t.Setenv("ALLOYDB_POSTGRES_USER", "your_alloydb_user")
t.Setenv("ALLOYDB_POSTGRES_PASSWORD", "your_alloydb_password")
t.Setenv("ALLOYDB_OMNI_HOST", "localhost")
t.Setenv("ALLOYDB_OMNI_PORT", "5432")
t.Setenv("ALLOYDB_OMNI_DATABASE", "your_alloydb_db")
t.Setenv("ALLOYDB_OMNI_USER", "your_alloydb_user")
t.Setenv("ALLOYDB_OMNI_PASSWORD", "your_alloydb_password")
t.Setenv("CLICKHOUSE_PROTOCOL", "your_clickhouse_protocol")
t.Setenv("CLICKHOUSE_DATABASE", "your_clickhouse_database")
t.Setenv("CLICKHOUSE_PASSWORD", "your_clickhouse_password")
@@ -2197,6 +2204,16 @@ func TestPrebuiltTools(t *testing.T) {
in []byte
wantToolset server.ToolsetConfigs
}{
{
name: "alloydb omni prebuilt tools",
in: alloydb_omni_config,
wantToolset: server.ToolsetConfigs{
"alloydb_omni_database_tools": tools.ToolsetConfig{
Name: "alloydb_omni_database_tools",
ToolNames: []string{"execute_sql", "list_tables", "list_active_queries", "list_available_extensions", "list_installed_extensions", "list_autovacuum_configurations", "list_columnar_configurations", "list_columnar_recommended_columns", "list_memory_configurations", "list_top_bloated_tables", "list_replication_slots", "list_invalid_indexes", "get_query_plan", "list_views", "list_schemas", "database_overview", "list_triggers", "list_indexes", "list_sequences", "long_running_transactions", "list_locks", "replication_stats", "list_query_stats", "get_column_cardinality", "list_publication_tables", "list_tablespaces", "list_pg_settings", "list_database_stats", "list_roles", "list_table_stats", "list_stored_procedure"},
},
},
},
{
name: "alloydb postgres admin prebuilt tools",
in: alloydb_admin_config,
@@ -2353,7 +2370,7 @@ func TestPrebuiltTools(t *testing.T) {
wantToolset: server.ToolsetConfigs{
"looker_tools": tools.ToolsetConfig{
Name: "looker_tools",
ToolNames: []string{"get_models", "get_explores", "get_dimensions", "get_measures", "get_filters", "get_parameters", "query", "query_sql", "query_url", "get_looks", "run_look", "make_look", "get_dashboards", "run_dashboard", "make_dashboard", "add_dashboard_element", "add_dashboard_filter", "generate_embed_url", "health_pulse", "health_analyze", "health_vacuum", "dev_mode", "get_projects", "get_project_files", "get_project_file", "create_project_file", "update_project_file", "delete_project_file", "get_connections", "get_connection_schemas", "get_connection_databases", "get_connection_tables", "get_connection_table_columns"},
ToolNames: []string{"get_models", "get_explores", "get_dimensions", "get_measures", "get_filters", "get_parameters", "query", "query_sql", "query_url", "get_looks", "run_look", "make_look", "get_dashboards", "run_dashboard", "make_dashboard", "add_dashboard_element", "add_dashboard_filter", "generate_embed_url", "health_pulse", "health_analyze", "health_vacuum", "dev_mode", "get_projects", "get_project_files", "get_project_file", "create_project_file", "update_project_file", "delete_project_file", "validate_project", "get_connections", "get_connection_schemas", "get_connection_databases", "get_connection_tables", "get_connection_table_columns"},
},
},
},

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

@@ -23,6 +23,14 @@ To connect to the database to explore and query data, search the MCP store for t
In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt alloydb-postgres-admin```.
You'll now be able to see all enabled tools in the "Tools" tab.
> [!NOTE]

View File

@@ -27,6 +27,13 @@ For AlloyDB infrastructure management, search the MCP store for the AlloyDB for
## Install & Configuration
1. In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt alloydb-postgres```.
2. Add the required inputs for your [cluster](https://docs.cloud.google.com/alloydb/docs/cluster-list) in the configuration pop-up, then click "Save". You can update this configuration at any time in the "Configure" tab.

View File

@@ -21,6 +21,13 @@ An editor configured to use the BigQuery MCP server can use its AI capabilities
## Install & Configuration
1. In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt bigquery```.
2. Add the required inputs in the configuration pop-up, then click "Save". You can update this configuration at any time in the "Configure" tab.

View File

@@ -23,6 +23,14 @@ To connect to the database to explore and query data, search the MCP store for t
In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt cloud-sql-mssql-admin```.
You'll now be able to see all enabled tools in the "Tools" tab.
> [!NOTE]

View File

@@ -24,6 +24,13 @@ For Cloud SQL infrastructure management, search the MCP store for the Cloud SQL
## Install & Configuration
1. In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt cloud-sql-mssql```.
2. Add the required inputs for your [instance](https://cloud.google.com/sql/docs/sqlserver/instance-info) in the configuration pop-up, then click "Save". You can update this configuration at any time in the "Configure" tab.

View File

@@ -23,6 +23,14 @@ To connect to the database to explore and query data, search the MCP store for t
In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt cloud-sql-mysql-admin```.
You'll now be able to see all enabled tools in the "Tools" tab.
> [!NOTE]

View File

@@ -26,6 +26,13 @@ For Cloud SQL infrastructure management, search the MCP store for the Cloud SQL
## Install & Configuration
1. In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt cloud-sql-mysql```.
2. Add the required inputs for your [instance](https://cloud.google.com/sql/docs/mysql/instance-info) in the configuration pop-up, then click "Save". You can update this configuration at any time in the "Configure" tab.

View File

@@ -23,6 +23,14 @@ To connect to the database to explore and query data, search the MCP store for t
In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt cloud-sql-postgres-admin```.
You'll now be able to see all enabled tools in the "Tools" tab.
> [!NOTE]

View File

@@ -26,6 +26,13 @@ For Cloud SQL infrastructure management, search the MCP store for the Cloud SQL
## Install & Configuration
1. In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt cloud-sql-postgres```.
2. Add the required inputs for your [instance](https://cloud.google.com/sql/docs/postgres/instance-info) in the configuration pop-up, then click "Save". You can update this configuration at any time in the "Configure" tab.

View File

@@ -20,6 +20,13 @@ An editor configured to use the Dataplex MCP server can use its AI capabilities
## Install & Configuration
1. In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt dataplex```.
2. Add the required inputs in the configuration pop-up, then click "Save". You can update this configuration at any time in the "Configure" tab.

View File

@@ -21,6 +21,13 @@ An editor configured to use the Looker MCP server can use its AI capabilities to
## Install & Configuration
1. In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt looker```.
2. Add the required inputs for your [instance](https://docs.cloud.google.com/looker/docs/set-up-and-administer-looker) in the configuration pop-up, then click "Save". You can update this configuration at any time in the "Configure" tab.

View File

@@ -21,6 +21,13 @@ An editor configured to use the Cloud Spanner MCP server can use its AI capabili
## Install & Configuration
1. In the Antigravity MCP Store, click the "Install" button.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest --prebuilt spanner```.
2. Add the required inputs for your [instance](https://docs.cloud.google.com/spanner/docs/instances) in the configuration pop-up, then click "Save". You can update this configuration at any time in the "Configure" tab.

View File

@@ -12,10 +12,17 @@ The MCP Toolbox for Databases Server gives AI-powered development tools the abil
## Install & Configuration
1. In the Antigravity MCP Store, click the **Install** button. A configuration window will appear.
> [!NOTE]
> On first use, the installation process automatically downloads and uses
> [MCP Toolbox](https://www.npmjs.com/package/@toolbox-sdk/server)
> `>=0.26.0`. To update MCP Toolbox, use:
> ```npm i -g @toolbox-sdk/server@latest```
> To always run the latest version, update the MCP server configuration to use:
> ```npx -y @toolbox-sdk/server@latest```.
2. Create your [`tools.yaml` configuration file](https://googleapis.github.io/genai-toolbox/getting-started/configure/).
3. Create your [`tools.yaml` configuration file](https://googleapis.github.io/genai-toolbox/getting-started/configure/).
3. In the configuration window, enter the full absolute path to your `tools.yaml` file and click **Save**.
4. In the configuration window, enter the full absolute path to your `tools.yaml` file and click **Save**.
> [!NOTE]
> If you encounter issues with Windows Defender blocking the execution, you may need to configure an allowlist. See [Configure exclusions for Microsoft Defender Antivirus](https://learn.microsoft.com/en-us/microsoft-365/security/defender-endpoint/configure-exclusions-microsoft-defender-antivirus?view=o365-worldwide) for more details.

View File

@@ -77,7 +77,7 @@ redeploying your application.
## Getting Started
### (Non-production) Running Toolbox
### Quickstart: Running Toolbox using NPX
You can run Toolbox directly with a [configuration file](../configure.md):

View File

@@ -115,7 +115,7 @@ pip install google-genai
1. Update `my_agent/agent.py` with the following content to connect to Toolbox:
```py
{{< include "quickstart/python/adk/quickstart.py" >}}
{{< regionInclude "quickstart/python/adk/quickstart.py" "quickstart" >}}
```
<br/>

View File

@@ -18,6 +18,7 @@
"resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz",
"integrity": "sha512-DJS3s0OVH4zFDB1PzjxAsHqJT6sKVbRwwML0ZBP9PbU7Yebtu/7SWMRzvO2J3nUi9pRNITCfu4LJeooM2w4pjg==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"arrify": "^2.0.0",
"extend": "^3.0.2"
@@ -31,6 +32,7 @@
"resolved": "https://registry.npmjs.org/@google-cloud/projectify/-/projectify-4.0.0.tgz",
"integrity": "sha512-MmaX6HeSvyPbWGwFq7mXdo0uQZLGBYCwziiLIGq5JVX+/bdI3SAq6bP98trV5eTWfLuvsMcIC1YJOF2vfteLFA==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=14.0.0"
}
@@ -40,15 +42,17 @@
"resolved": "https://registry.npmjs.org/@google-cloud/promisify/-/promisify-4.0.0.tgz",
"integrity": "sha512-Orxzlfb9c67A15cq2JQEyVc7wEsmFBmHjZWZYQMUyJ1qivXyMwdyNOs9odi79hze+2zqdTtu1E19IM/FtqZ10g==",
"license": "Apache-2.0",
"peer": true,
"engines": {
"node": ">=14"
}
},
"node_modules/@google-cloud/storage": {
"version": "7.18.0",
"resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.18.0.tgz",
"integrity": "sha512-r3ZwDMiz4nwW6R922Z1pwpePxyRwE5GdevYX63hRmAQUkUQJcBH/79EnQPDv5cOv1mFBgevdNWQfi3tie3dHrQ==",
"version": "7.19.0",
"resolved": "https://registry.npmjs.org/@google-cloud/storage/-/storage-7.19.0.tgz",
"integrity": "sha512-n2FjE7NAOYyshogdc7KQOl/VZb4sneqPjWouSyia9CMDdMhRX5+RIbqalNmC7LOLzuLAN89VlF2HvG8na9G+zQ==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"@google-cloud/paginator": "^5.0.0",
"@google-cloud/projectify": "^4.0.0",
@@ -56,7 +60,7 @@
"abort-controller": "^3.0.0",
"async-retry": "^1.3.3",
"duplexify": "^4.1.3",
"fast-xml-parser": "^4.4.1",
"fast-xml-parser": "^5.3.4",
"gaxios": "^6.0.2",
"google-auth-library": "^9.6.3",
"html-entities": "^2.5.2",
@@ -75,6 +79,7 @@
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"license": "MIT",
"peer": true,
"bin": {
"uuid": "dist/bin/uuid"
}
@@ -97,7 +102,6 @@
"resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.14.0.tgz",
"integrity": "sha512-jirYprAAJU1svjwSDVCzyVq+FrJpJd5CSxR/g2Ga/gZ0ZYZpcWjMS75KJl9y71K1mDN+tcx6s21CzCbB2R840g==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"google-auth-library": "^9.14.2",
"ws": "^8.18.0"
@@ -136,7 +140,6 @@
"resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.5.tgz",
"integrity": "sha512-QakrKIGniGuRVfWBdMsDea/dx1PNE739QJ7gCM41s9q+qaCYTHCdsIBXQVVXry3mfWAiaM9kT22Hyz53Uw8mfg==",
"license": "MIT",
"peer": true,
"dependencies": {
"ajv": "^6.12.6",
"content-type": "^1.0.5",
@@ -299,6 +302,7 @@
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 10"
}
@@ -307,13 +311,15 @@
"version": "0.12.5",
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.5.tgz",
"integrity": "sha512-hWtVTC2q7hc7xZ/RLbxapMvDMgUnDvKvMOpKal4DrMyfGBUfB1oKaZlIRr6mJL+If3bAP6sV/QneGzF6tJjZDg==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/@types/node": {
"version": "24.10.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz",
"integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -323,6 +329,7 @@
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.13.tgz",
"integrity": "sha512-FGJ6udDNUCjd19pp0Q3iTiDkwhYup7J8hpMW9c4k53NrccQFFWKRho6hvtPPEhnXWKvukfwAlB6DbDz4yhH5Gg==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/caseless": "*",
"@types/node": "*",
@@ -335,6 +342,7 @@
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.5.tgz",
"integrity": "sha512-jqdObeR2rxZZbPSGL+3VckHMYtu+f9//KXBsVny6JSX/pa38Fy+bGjuG8eW/H6USNQWhLi8Num++cU2yOCNz4A==",
"license": "MIT",
"peer": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
@@ -352,6 +360,7 @@
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 0.6"
}
@@ -361,6 +370,7 @@
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"license": "MIT",
"peer": true,
"dependencies": {
"mime-db": "1.52.0"
},
@@ -372,13 +382,15 @@
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/abort-controller": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
"license": "MIT",
"peer": true,
"dependencies": {
"event-target-shim": "^5.0.0"
},
@@ -453,6 +465,7 @@
"resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz",
"integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=8"
}
@@ -462,6 +475,7 @@
"resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
"integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
"license": "MIT",
"peer": true,
"dependencies": {
"retry": "0.13.1"
}
@@ -754,6 +768,7 @@
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
"integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
"license": "MIT",
"peer": true,
"dependencies": {
"end-of-stream": "^1.4.1",
"inherits": "^2.0.3",
@@ -802,6 +817,7 @@
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
"license": "MIT",
"peer": true,
"dependencies": {
"once": "^1.4.0"
}
@@ -871,6 +887,7 @@
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=6"
}
@@ -901,7 +918,6 @@
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"license": "MIT",
"peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
@@ -973,9 +989,9 @@
"license": "MIT"
},
"node_modules/fast-xml-parser": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.3.tgz",
"integrity": "sha512-RKihhV+SHsIUGXObeVy9AXiBbFwkVk7Syp8XgwN5U3JV416+Gwp/GO9i0JYKmikykgz/UHRrrV4ROuZEo/T0ig==",
"version": "5.3.5",
"resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.5.tgz",
"integrity": "sha512-JeaA2Vm9ffQKp9VjvfzObuMCjUYAp5WDYhRYL5LrBPY/jUDlUtOvDfot0vKSkB9tuX885BDHjtw4fZadD95wnA==",
"funding": [
{
"type": "github",
@@ -983,8 +999,9 @@
}
],
"license": "MIT",
"peer": true,
"dependencies": {
"strnum": "^1.1.1"
"strnum": "^2.1.2"
},
"bin": {
"fxparser": "src/cli/cli.js"
@@ -1333,7 +1350,8 @@
"url": "https://patreon.com/mdevils"
}
],
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/http-errors": {
"version": "2.0.0",
@@ -1365,6 +1383,7 @@
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@tootallnate/once": "2",
"agent-base": "6",
@@ -1379,6 +1398,7 @@
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"debug": "4"
},
@@ -1555,6 +1575,7 @@
"resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
"integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
"license": "MIT",
"peer": true,
"bin": {
"mime": "cli.js"
},
@@ -1715,6 +1736,7 @@
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
"integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"yocto-queue": "^0.1.0"
},
@@ -1856,6 +1878,7 @@
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
"license": "MIT",
"peer": true,
"dependencies": {
"inherits": "^2.0.3",
"string_decoder": "^1.1.1",
@@ -1870,6 +1893,7 @@
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">= 4"
}
@@ -1879,6 +1903,7 @@
"resolved": "https://registry.npmjs.org/retry-request/-/retry-request-7.0.2.tgz",
"integrity": "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/request": "^2.48.8",
"extend": "^3.0.2",
@@ -2107,6 +2132,7 @@
"resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
"integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
"license": "MIT",
"peer": true,
"dependencies": {
"stubs": "^3.0.0"
}
@@ -2115,13 +2141,15 @@
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
"integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/string_decoder": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
"license": "MIT",
"peer": true,
"dependencies": {
"safe-buffer": "~5.2.0"
}
@@ -2223,28 +2251,31 @@
}
},
"node_modules/strnum": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-1.1.2.tgz",
"integrity": "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz",
"integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/NaturalIntelligence"
}
],
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/stubs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
"integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/teeny-request": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-9.0.0.tgz",
"integrity": "sha512-resvxdc6Mgb7YEThw6G6bExlXKkv6+YbuzGg9xuXxSgxJF7Ozs+o8Y9+2R3sArdWdW8nOokoQb1yrpFB0pQK2g==",
"license": "Apache-2.0",
"peer": true,
"dependencies": {
"http-proxy-agent": "^5.0.0",
"https-proxy-agent": "^5.0.0",
@@ -2261,6 +2292,7 @@
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"debug": "4"
},
@@ -2273,6 +2305,7 @@
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
"license": "MIT",
"peer": true,
"dependencies": {
"agent-base": "6",
"debug": "4"
@@ -2314,7 +2347,8 @@
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/unpipe": {
"version": "1.0.0",
@@ -2338,7 +2372,8 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT"
"license": "MIT",
"peer": true
},
"node_modules/uuid": {
"version": "9.0.1",
@@ -2525,6 +2560,7 @@
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
"license": "MIT",
"peer": true,
"engines": {
"node": ">=10"
},
@@ -2537,7 +2573,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@@ -3351,13 +3351,13 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"node_modules/axios": {
"version": "1.12.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.2.tgz",
"integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==",
"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"
}
},
@@ -4248,9 +4248,10 @@
}
},
"node_modules/form-data": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
"integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
"integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",

View File

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

View File

@@ -18,7 +18,8 @@
"node_modules/@cfworker/json-schema": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
"integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="
"integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
"peer": true
},
"node_modules/@google/generative-ai": {
"version": "0.24.1",
@@ -225,6 +226,7 @@
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"peer": true,
"engines": {
"node": ">=10"
},
@@ -308,6 +310,7 @@
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
"peer": true,
"engines": {
"node": ">=10"
},
@@ -420,6 +423,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@@ -821,6 +825,7 @@
"version": "1.0.21",
"resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz",
"integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==",
"peer": true,
"dependencies": {
"base64-js": "^1.5.1"
}
@@ -873,9 +878,9 @@
}
},
"node_modules/langsmith": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.4.3.tgz",
"integrity": "sha512-vuBAagBZulXj0rpZhUTxmHhrYIBk53z8e2Q8ty4OHVkahN4ul7Im3OZxD9jsXZB0EuncK1xRYtY8J3BW4vj1zw==",
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.5.2.tgz",
"integrity": "sha512-CfkcQsiajtTWknAcyItvJsKEQdY2VgDpm6U8pRI9wnM07mevnOv5EF+RcqWGwx37SEUxtyi2RXMwnKW8b06JtA==",
"license": "MIT",
"dependencies": {
"@types/uuid": "^10.0.0",
@@ -969,6 +974,7 @@
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
"peer": true,
"bin": {
"mustache": "bin/mustache"
}
@@ -1407,7 +1413,6 @@
"version": "3.25.76",
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View File

@@ -1,6 +1,21 @@
# [START quickstart]
import asyncio
from google.adk import Agent
from google.adk.apps import App
from google.adk.runners import InMemoryRunner
from google.adk.tools.toolbox_toolset import ToolboxToolset
from google.genai.types import Content, Part
prompt = """
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.
"""
# TODO(developer): update the TOOLBOX_URL to your toolbox endpoint
toolset = ToolboxToolset(
@@ -8,10 +23,35 @@ toolset = ToolboxToolset(
)
root_agent = Agent(
name='root_agent',
name='hotel_assistant',
model='gemini-2.5-flash',
instruction="You are a helpful AI assistant designed to provide accurate and useful information.",
instruction=prompt,
tools=[toolset],
)
app = App(root_agent=root_agent, name="my_agent")
# [END quickstart]
queries = [
"Find hotels in Basel with Basel in its name.",
"Can you book the Hilton Basel for me?",
"Oh wait, this is too expensive. Please cancel it and book the Hyatt Regency instead.",
"My check in dates would be from April 10, 2024 to April 19, 2024.",
]
async def main():
runner = InMemoryRunner(app=app)
session = await runner.session_service.create_session(
app_name=app.name, user_id="test_user"
)
for query in queries:
print(f"\nUser: {query}")
user_message = Content(parts=[Part.from_text(text=query)])
async for event in runner.run_async(user_id="test_user", session_id=session.id, new_message=user_message):
if event.is_final_response() and event.content and event.content.parts:
print(f"Agent: {event.content.parts[0].text}")
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -41,31 +41,29 @@ def golden_keywords():
class TestExecution:
"""Test framework execution and output validation."""
_cached_output = None
@pytest.fixture(scope="function")
def script_output(self, capsys):
"""Run the quickstart function and return its output."""
# TODO: Add better validation for ADK once we have a way to capture its
# output.
if ORCH_NAME == "adk":
return quickstart.app.root_agent.name
else:
if TestExecution._cached_output is None:
asyncio.run(quickstart.main())
return capsys.readouterr()
out, err = capsys.readouterr()
TestExecution._cached_output = (out, err)
class Output:
def __init__(self, out, err):
self.out = out
self.err = err
return Output(*TestExecution._cached_output)
def test_script_runs_without_errors(self, script_output):
"""Test that the script runs and produces no stderr."""
if ORCH_NAME == "adk":
return
assert script_output.err == "", f"Script produced stderr: {script_output.err}"
def test_keywords_in_output(self, script_output, golden_keywords):
"""Test that expected keywords are present in the script's output."""
if ORCH_NAME == "adk":
assert script_output == "root_agent"
return
output = script_output.out
missing_keywords = [kw for kw in golden_keywords if kw not in output]
assert not missing_keywords, f"Missing keywords in output: {missing_keywords}"

View File

@@ -32,7 +32,7 @@ to expose your developer assistant tools to a Postgres instance:
{{< notice tip >}}
This guide can be used with [AlloyDB
Omni](https://cloud.google.com/alloydb/omni/current/docs/overview).
Omni](https://cloud.google.com/alloydb/omni/docs/overview).
{{< /notice >}}
## Set up the database
@@ -40,10 +40,10 @@ Omni](https://cloud.google.com/alloydb/omni/current/docs/overview).
1. Create or select a PostgreSQL instance.
* [Install PostgreSQL locally](https://www.postgresql.org/download/)
* [Install AlloyDB Omni](https://cloud.google.com/alloydb/omni/current/docs/quickstart)
* [Install AlloyDB Omni](https://cloud.google.com/alloydb/omni/docs/quickstart)
1. Create or reuse [a database
user](https://cloud.google.com/alloydb/omni/current/docs/database-users/manage-users)
user](https://docs.cloud.google.com/alloydb/omni/containers/current/docs/database-users/manage-users)
and have the username and password ready.
## Install MCP Toolbox

View File

@@ -83,15 +83,12 @@ Toolbox instead of the local address.
2. Open your agent file (`my_agent/agent.py`).
3. Update the `ToolboxSyncClient` initialization to use your Cloud Run URL.
3. Update the `ToolboxToolset` initialization to point to your Cloud Run service URL. Replace the existing initialization code with the following:
{{% alert color="info" %}}
Since Cloud Run services are secured by default, you also need to provide an
authentication token.
{{% alert color="info" title="Note" %}}
Since Cloud Run services are secured by default, you also need to provide a workload identity.
{{% /alert %}}
Replace your existing client initialization code with the following:
```python
from google.adk import Agent
from google.adk.apps import App
@@ -132,14 +129,14 @@ app = App(root_agent=root_agent, name="my_agent")
Run the deployment command:
```bash
make backend
make deploy
```
This command will build your agent's container image and deploy it to Vertex AI.
## Step 6: Test your Deployment
Once the deployment command (`make backend`) completes, it will output the URL
Once the deployment command (`make deploy`) completes, it will output the URL
for the Agent Engine Playground. You can click on this URL to open the
Playground in your browser and start chatting with your agent to test the tools.

View File

@@ -0,0 +1,112 @@
---
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 <tool-source> skills-generate \
--name <skill-name> \
--toolset <toolset-name> \
--description <description> \
--output-dir <output-directory>
```
- `<tool-source>`: Can be `--tools-file`, `--tools-files`, `--tools-folder`, and `--prebuilt`. See the [CLI Reference](../reference/cli.md) for details.
- `--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

@@ -0,0 +1,75 @@
---
title: "Invoke Tools via CLI"
type: docs
weight: 10
description: >
Learn how to invoke your tools directly from the command line using the `invoke` command.
---
The `invoke` command allows you to invoke tools defined in your configuration directly from the CLI. This is useful for:
- **Ephemeral Invocation:** Executing a tool without spinning up a full MCP server/client.
- **Debugging:** Isolating tool execution logic and testing with various parameter combinations.
{{< 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.
{{< /notice >}}
## Before you begin
1. Make sure you have the `toolbox` binary installed or built.
2. Make sure you have a valid tool configuration file (e.g., `tools.yaml`).
### Command Usage
The basic syntax for the command is:
```bash
toolbox <tool-source> invoke <tool-name> [params]
```
- `<tool-source>`: Can be `--tools-file`, `--tools-files`, `--tools-folder`, and `--prebuilt`. See the [CLI Reference](../reference/cli.md) for details.
- `<tool-name>`: The name of the tool you want to call. This must match the name defined in your `tools.yaml`.
- `[params]`: (Optional) A JSON string representing the arguments for the tool.
## Examples
### 1. Calling a Tool without Parameters
If your tool takes no parameters, simply provide the tool name:
```bash
toolbox --tools-file tools.yaml invoke my-simple-tool
```
### 2. Calling a Tool with Parameters
For tools that require arguments, pass them as a JSON string. Ensure you escape quotes correctly for your shell.
**Example: A tool that takes parameters**
Assuming a tool named `mytool` taking `a` and `b`:
```bash
toolbox --tools-file tools.yaml invoke mytool '{"a": 10, "b": 20}'
```
**Example: A tool that queries a database**
```bash
toolbox --tools-file tools.yaml invoke db-query '{"sql": "SELECT * FROM users LIMIT 5"}'
```
### 3. Using Prebuilt Configurations
You can also use the `--prebuilt` flag to load prebuilt toolsets.
```bash
toolbox --prebuilt cloudsql-postgres invoke cloudsql-postgres-list-instances
```
## Troubleshooting
- **Tool not found:** Ensure the `<tool-name>` matches exactly what is in your YAML file and that the file is correctly loaded via `--tools-file`.
- **Invalid parameters:** Double-check your JSON syntax. The error message will usually indicate if the JSON parsing failed or if the parameters didn't match the tool's schema.
- **Auth errors:** The `invoke` command currently does not support flows requiring client-side authorization (like OAuth flow initiation via the CLI). It works best for tools using service-side authentication (e.g., Application Default Credentials).

View File

@@ -30,6 +30,50 @@ description: >
| | `--user-agent-metadata` | Appends additional metadata to the User-Agent. | |
| `-v` | `--version` | version for toolbox | |
## Sub Commands
<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.
**Syntax:**
```bash
toolbox invoke <tool-name> [params]
```
**Arguments:**
- `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 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
### Transport Configuration

View File

@@ -54,9 +54,9 @@ See [Usage Examples](../reference/cli.md#examples).
* `get_query_plan`: Generate the execution plan of a statement.
* `list_views`: Lists views in the database from pg_views with a default
limit of 50 rows. Returns schemaname, viewname and the ownername.
* `list_schemas`: Lists schemas in the database.
* `database_overview`: Fetches the current state of the PostgreSQL server.
* `list_triggers`: Lists triggers in the database.
* `list_schemas`: Lists schemas in the database.
* `database_overview`: Fetches the current state of the PostgreSQL server.
* `list_triggers`: Lists triggers in the database.
* `list_indexes`: List available user indexes in a PostgreSQL database.
* `list_sequences`: List sequences in a PostgreSQL database.
* `list_publication_tables`: List publication tables in a PostgreSQL database.
@@ -64,7 +64,7 @@ See [Usage Examples](../reference/cli.md#examples).
* `list_pg_settings`: List configuration parameters for the PostgreSQL server.
* `list_database_stats`: Lists the key performance and activity statistics for
each database in the AlloyDB instance.
* `list_roles`: Lists all the user-created roles in PostgreSQL database.
* `list_roles`: Lists all the user-created roles in PostgreSQL database.
## AlloyDB Postgres Admin
@@ -100,6 +100,43 @@ See [Usage Examples](../reference/cli.md#examples).
(timeseries metrics) for queries running in an AlloyDB instance using a
PromQL query.
## AlloyDB Omni
* `--prebuilt` value: `alloydb-omni`
* **Environment Variables:**
* `ALLOYDB_OMNI_HOST`: (Optional) The hostname or IP address (Default: localhost).
* `ALLOYDB_OMNI_PORT`: (Optional) The port number (Default: 5432).
* `ALLOYDB_OMNI_DATABASE`: The name of the database to connect to.
* `ALLOYDB_OMNI_USER`: The database username.
* `ALLOYDB_OMNI_PASSWORD`: (Optional) The password for the database user.
* `ALLOYDB_OMNI_QUERY_PARAMS`: (Optional) Connection query parameters.
* **Tools:**
* `execute_sql`: Executes a SQL query.
* `list_tables`: Lists tables in the database.
* `list_autovacuum_configurations`: Lists autovacuum configurations in the
database.
* `list_columnar_configurations`: List AlloyDB Omni columnar-related configurations.
* `list_columnar_recommended_columns`: Lists columns that AlloyDB Omni recommends adding to the columnar engine.
* `list_memory_configurations`: Lists memory-related configurations in the
database.
* `list_top_bloated_tables`: List top bloated tables in the database.
* `list_replication_slots`: Lists replication slots in the database.
* `list_invalid_indexes`: Lists invalid indexes in the database.
* `get_query_plan`: Generate the execution plan of a statement.
* `list_views`: Lists views in the database from pg_views with a default
limit of 50 rows. Returns schemaname, viewname and the ownername.
* `list_schemas`: Lists schemas in the database.
* `database_overview`: Fetches the current state of the PostgreSQL server.
* `list_triggers`: Lists triggers in the database.
* `list_indexes`: List available user indexes in a PostgreSQL database.
* `list_sequences`: List sequences in a PostgreSQL database.
* `list_publication_tables`: List publication tables in a PostgreSQL database.
* `list_tablespaces`: Lists tablespaces in the database.
* `list_pg_settings`: List configuration parameters for the PostgreSQL server.
* `list_database_stats`: Lists the key performance and activity statistics for
each database in the AlloyDB instance.
* `list_roles`: Lists all the user-created roles in PostgreSQL database.
## BigQuery
* `--prebuilt` value: `bigquery`
@@ -243,9 +280,9 @@ See [Usage Examples](../reference/cli.md#examples).
* `get_query_plan`: Generate the execution plan of a statement.
* `list_views`: Lists views in the database from pg_views with a default
limit of 50 rows. Returns schemaname, viewname and the ownername.
* `list_schemas`: Lists schemas in the database.
* `database_overview`: Fetches the current state of the PostgreSQL server.
* `list_triggers`: Lists triggers in the database.
* `list_schemas`: Lists schemas in the database.
* `database_overview`: Fetches the current state of the PostgreSQL server.
* `list_triggers`: Lists triggers in the database.
* `list_indexes`: List available user indexes in a PostgreSQL database.
* `list_sequences`: List sequences in a PostgreSQL database.
* `list_publication_tables`: List publication tables in a PostgreSQL database.
@@ -253,7 +290,7 @@ See [Usage Examples](../reference/cli.md#examples).
* `list_pg_settings`: List configuration parameters for the PostgreSQL server.
* `list_database_stats`: Lists the key performance and activity statistics for
each database in the postgreSQL instance.
* `list_roles`: Lists all the user-created roles in PostgreSQL database.
* `list_roles`: Lists all the user-created roles in PostgreSQL database.
## Cloud SQL for PostgreSQL Observability
@@ -377,10 +414,10 @@ See [Usage Examples](../reference/cli.md#examples).
entries.
* **Dataplex Editor** (`roles/dataplex.editor`) to modify entries.
* **Tools:**
* `dataplex_search_entries`: Searches for entries in Dataplex Catalog.
* `dataplex_lookup_entry`: Retrieves a specific entry from Dataplex
* `search_entries`: Searches for entries in Dataplex Catalog.
* `lookup_entry`: Retrieves a specific entry from Dataplex
Catalog.
* `dataplex_search_aspect_types`: Finds aspect types relevant to the
* `search_aspect_types`: Finds aspect types relevant to the
query.
## Firestore
@@ -451,6 +488,7 @@ See [Usage Examples](../reference/cli.md#examples).
* `create_project_file`: Create a new LookML file.
* `update_project_file`: Update an existing LookML file.
* `delete_project_file`: Delete a LookML file.
* `validate_project`: Check the syntax of a LookML project.
* `get_connections`: Get the available connections in a Looker instance.
* `get_connection_schemas`: Get the available schemas in a connection.
* `get_connection_databases`: Get the available databases in a connection.
@@ -564,9 +602,9 @@ See [Usage Examples](../reference/cli.md#examples).
* `get_query_plan`: Generate the execution plan of a statement.
* `list_views`: Lists views in the database from pg_views with a default
limit of 50 rows. Returns schemaname, viewname and the ownername.
* `list_schemas`: Lists schemas in the database.
* `database_overview`: Fetches the current state of the PostgreSQL server.
* `list_triggers`: Lists triggers in the database.
* `list_schemas`: Lists schemas in the database.
* `database_overview`: Fetches the current state of the PostgreSQL server.
* `list_triggers`: Lists triggers in the database.
* `list_indexes`: List available user indexes in a PostgreSQL database.
* `list_sequences`: List sequences in a PostgreSQL database.
* `list_publication_tables`: List publication tables in a PostgreSQL database.
@@ -574,7 +612,7 @@ See [Usage Examples](../reference/cli.md#examples).
* `list_pg_settings`: List configuration parameters for the PostgreSQL server.
* `list_database_stats`: Lists the key performance and activity statistics for
each database in the PostgreSQL server.
* `list_roles`: Lists all the user-created roles in PostgreSQL database.
* `list_roles`: Lists all the user-created roles in PostgreSQL database.
## Google Cloud Serverless for Apache Spark

View File

@@ -194,6 +194,15 @@ Use environment variable replacement with the format ${ENV_NAME}
instead of hardcoding your secrets into the configuration file.
{{< /notice >}}
### Managed Connection Pooling
Toolbox automatically supports [Managed Connection Pooling][alloydb-mcp]. If your AlloyDB instance has Managed Connection Pooling enabled, the connection will immediately benefit from increased throughput and reduced latency.
The interface is identical, so there's no additional configuration required on the client. For more information on configuring your instance, see the [AlloyDB Managed Connection Pooling documentation][alloydb-mcp-docs].
[alloydb-mcp]: https://cloud.google.com/blog/products/databases/alloydb-managed-connection-pooling
[alloydb-mcp-docs]: https://cloud.google.com/alloydb/docs/configure-managed-connection-pooling
## Reference
| **field** | **type** | **required** | **description** |

View File

@@ -0,0 +1,71 @@
---
title: "Cloud Logging Admin"
type: docs
weight: 1
description: >
The Cloud Logging Admin source enables tools to interact with the Cloud Logging API, allowing for the retrieval of log names, monitored resource types, and the querying of log data.
---
## About
The Cloud Logging Admin source provides a client to interact with the [Google
Cloud Logging API](https://cloud.google.com/logging/docs). This allows tools to list log names, monitored resource types, and query log entries.
Authentication can be handled in two ways:
1. **Application Default Credentials (ADC):** By default, the source uses ADC
to authenticate with the API.
2. **Client-side OAuth:** If `useClientOAuth` is set to `true`, the source will
expect an OAuth 2.0 access token to be provided by the client (e.g., a web
browser) for each request.
## Available Tools
- [`cloud-logging-admin-list-log-names`](../tools/cloudloggingadmin/cloud-logging-admin-list-log-names.md)
Lists the log names in the project.
- [`cloud-logging-admin-list-resource-types`](../tools/cloudloggingadmin/cloud-logging-admin-list-resource-types.md)
Lists the monitored resource types.
- [`cloud-logging-admin-query-logs`](../tools/cloudloggingadmin/cloud-logging-admin-query-logs.md)
Queries log entries.
## Example
Initialize a Cloud Logging Admin source that uses ADC:
```yaml
kind: sources
name: my-cloud-logging
type: cloud-logging-admin
project: my-project-id
```
Initialize a Cloud Logging Admin source that uses client-side OAuth:
```yaml
kind: sources
name: my-oauth-cloud-logging
type: cloud-logging-admin
project: my-project-id
useClientOAuth: true
```
Initialize a Cloud Logging Admin source that uses service account impersonation:
```yaml
kind: sources
name: my-impersonated-cloud-logging
type: cloud-logging-admin
project: my-project-id
impersonateServiceAccount: "my-service-account@my-project.iam.gserviceaccount.com"
```
## Reference
| **field** | **type** | **required** | **description** |
|-----------------------------|:--------:|:------------:|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| type | string | true | Must be "cloud-logging-admin". |
| project | string | true | ID of the GCP project. |
| useClientOAuth | boolean | false | If true, the source will use client-side OAuth for authorization. Otherwise, it will use Application Default Credentials. Defaults to `false`. Cannot be used with `impersonateServiceAccount`. |
| impersonateServiceAccount | string | false | The service account to impersonate for API calls. Cannot be used with `useClientOAuth`. |

View File

@@ -195,6 +195,15 @@ Use environment variable replacement with the format ${ENV_NAME}
instead of hardcoding your secrets into the configuration file.
{{< /notice >}}
### Managed Connection Pooling
Toolbox automatically supports [Managed Connection Pooling][csql-mcp]. If your Cloud SQL for PostgreSQL instance has Managed Connection Pooling enabled, the connection will immediately benefit from increased throughput and reduced latency.
The interface is identical, so there's no additional configuration required on the client. For more information on configuring your instance, see the [Cloud SQL Managed Connection Pooling documentation][csql-mcp-docs].
[csql-mcp]: https://docs.cloud.google.com/sql/docs/postgres/managed-connection-pooling
[csql-mcp-docs]: https://docs.cloud.google.com/sql/docs/postgres/configure-mcp
## Reference
| **field** | **type** | **required** | **description** |

View File

@@ -0,0 +1,8 @@
---
title: "Cloud Logging Admin"
linkTitle: "Cloud Logging Admin"
type: docs
weight: 1
description: >
Tools that work with Cloud Logging Admin Sources.
---

View File

@@ -0,0 +1,39 @@
---
title: "cloud-logging-admin-list-log-names"
type: docs
description: >
A "cloud-logging-admin-list-log-names" tool lists the log names in the project.
aliases:
- /resources/tools/cloud-logging-admin-list-log-names
---
## About
The `cloud-logging-admin-list-log-names` tool lists the log names available in the Google Cloud project.
It's compatible with the following sources:
- [cloud-logging-admin](../../sources/cloud-logging-admin.md)
## Example
```yaml
kind: tools
name: list_log_names
type: cloud-logging-admin-list-log-names
source: my-cloud-logging
description: Lists all log names in the project.
```
## Reference
| **field** | **type** | **required** | **description** |
|-------------|:--------:|:------------:|----------------------------------------------------|
| type | string | true | Must be "cloud-logging-admin-list-log-names". |
| source | string | true | Name of the cloud-logging-admin source. |
| description | string | true | Description of the tool that is passed to the LLM. |
### Parameters
| **parameter** | **type** | **required** | **description** |
|:--------------|:--------:|:------------:|:----------------|
| limit | integer | false | Maximum number of log entries to return (default: 200). |

View File

@@ -0,0 +1,34 @@
---
title: "cloud-logging-admin-list-resource-types"
type: docs
description: >
A "cloud-logging-admin-list-resource-types" tool lists the monitored resource types.
aliases:
- /resources/tools/cloud-logging-admin-list-resource-types
---
## About
The `cloud-logging-admin-list-resource-types` tool lists the monitored resource types available in Google Cloud Logging.
It's compatible with the following sources:
- [cloud-logging-admin](../../sources/cloud-logging-admin.md)
## Example
```yaml
kind: tools
name: list_resource_types
type: cloud-logging-admin-list-resource-types
source: my-cloud-logging
description: Lists monitored resource types.
```
## Reference
| **field** | **type** | **required** | **description** |
|-------------|:--------:|:------------:|----------------------------------------------------|
| type | string | true | Must be "cloud-logging-admin-list-resource-types".|
| source | string | true | Name of the cloud-logging-admin source. |
| description | string | true | Description of the tool that is passed to the LLM. |

View File

@@ -0,0 +1,44 @@
---
title: "cloud-logging-admin-query-logs"
type: docs
description: >
A "cloud-logging-admin-query-logs" tool queries log entries.
aliases:
- /resources/tools/cloud-logging-admin-query-logs
---
## About
The `cloud-logging-admin-query-logs` tool allows you to query log entries from Google Cloud Logging using the advanced logs filter syntax.
It's compatible with the following sources:
- [cloud-logging-admin](../../sources/cloud-logging-admin.md)
## Example
```yaml
kind: tools
name: query_logs
type: cloud-logging-admin-query-logs
source: my-cloud-logging
description: Queries log entries from Cloud Logging.
```
## Reference
| **field** | **type** | **required** | **description** |
|-------------|:--------:|:------------:|----------------------------------------------------|
| type | string | true | Must be "cloud-logging-admin-query-logs". |
| source | string | true | Name of the cloud-logging-admin source. |
| description | string | true | Description of the tool that is passed to the LLM. |
### Parameters
| **parameter** | **type** | **required** | **description** |
|:--------------|:--------:|:------------:|:----------------|
| filter | string | false | Cloud Logging filter query. Common fields: resource.type, resource.labels.*, logName, severity, textPayload, jsonPayload.*, protoPayload.*, labels.*, httpRequest.*. Operators: =, !=, <, <=, >, >=, :, =~, AND, OR, NOT. |
| newestFirst | boolean | false | Set to true for newest logs first. Defaults to oldest first. |
| startTime | string | false | Start time in RFC3339 format (e.g., 2025-12-09T00:00:00Z). Defaults to 30 days ago. |
| endTime | string | false | End time in RFC3339 format (e.g., 2025-12-09T23:59:59Z). Defaults to now. |
| verbose | boolean | false | Include additional fields (insertId, trace, spanId, httpRequest, labels, operation, sourceLocation). Defaults to false. |
| limit | integer | false | Maximum number of log entries to return. Default: `200`. |

View File

@@ -8,7 +8,7 @@ description: "Restores a backup of a Cloud SQL instance."
The `cloud-sql-restore-backup` tool restores a backup on a Cloud SQL instance using the Cloud SQL Admin API.
{{< notice info dd>}}
This tool uses a `source` of kind `cloud-sql-admin`.
This tool uses a `source` of type `cloud-sql-admin`.
{{< /notice >}}
## Examples
@@ -16,11 +16,11 @@ This tool uses a `source` of kind `cloud-sql-admin`.
Basic backup restore
```yaml
tools:
backup-restore-basic:
kind: cloud-sql-restore-backup
source: cloud-sql-admin-source
description: "Restores a backup onto the given Cloud SQL instance."
kind: tools
name: backup-restore-basic
type: cloud-sql-restore-backup
source: cloud-sql-admin-source
description: "Restores a backup onto the given Cloud SQL instance."
```
## Reference
@@ -28,7 +28,7 @@ tools:
### Tool Configuration
| **field** | **type** | **required** | **description** |
| -------------- | :------: | :----------: | ------------------------------------------------ |
| kind | string | true | Must be "cloud-sql-restore-backup". |
| type | string | true | Must be "cloud-sql-restore-backup". |
| source | string | true | The name of the `cloud-sql-admin` source to use. |
| description | string | false | A description of the tool. |

View File

@@ -0,0 +1,47 @@
---
title: "looker-validate-project"
type: docs
weight: 1
description: >
A "looker-validate-project" tool checks the syntax of a LookML project and reports any errors
aliases:
- /resources/tools/looker-validate-project
---
## About
A "looker-validate-project" tool checks the syntax of a LookML project and reports any errors
It's compatible with the following sources:
- [looker](../../sources/looker.md)
`looker-validate-project` accepts a project_id parameter.
## Example
```yaml
tools:
validate_project:
kind: looker-validate-project
source: looker-source
description: |
This tool checks a LookML project for syntax errors.
Prerequisite: The Looker session must be in Development Mode. Use `dev_mode: true` first.
Parameters:
- project_id (required): The unique ID of the LookML project.
Output:
A list of error details including the file path and line number, and also a list of models
that are not currently valid due to LookML errors.
```
## Reference
| **field** | **type** | **required** | **description** |
|-------------|:--------:|:------------:|----------------------------------------------------|
| kind | string | true | Must be "looker-validate-project". |
| source | string | true | Name of the source Looker instance. |
| description | string | true | Description of the tool that is passed to the LLM. |

View File

@@ -0,0 +1,54 @@
---
title: "Pre- and Post- Processing"
type: docs
weight: 1
description: >
Intercept and modify interactions between the agent and its tools either before or after a tool is executed.
---
Pre- and post- processing allow developers to intercept and modify interactions between the agent and its tools or the user.
{{< notice note >}}
These capabilities are typically features of **orchestration frameworks** (like LangChain, LangGraph, or Agent Builder) rather than the Toolbox SDK itself. However, Toolbox tools are designed to fully leverage these framework capabilities to support robust, secure, and compliant agent architectures.
{{< /notice >}}
## Types of Processing
### Pre-processing
Pre-processing occurs before a tool is executed or an agent processes a message. Key types include:
- **Input Sanitization & Redaction**: Detecting and masking sensitive information (like PII) in user queries or tool arguments to prevent it from being logged or sent to unauthorized systems.
- **Business Logic Validation**: Verifying that the proposed action complies with business rules (e.g., ensuring a requested hotel stay does not exceed 14 days, or checking if a user has sufficient permission).
- **Security Guardrails**: Analyzing inputs for potential prompt injection attacks or malicious payloads.
### Post-processing
Post-processing occurs after a tool has executed or the model has generated a response. Key types include:
- **Response Enrichment**: Injecting additional data into the tool output that wasn't part of the raw API response (e.g., calculating loyalty points earned based on the booking value).
- **Output Formatting**: Transforming raw data (like JSON or XML) into a more human-readable or model-friendly format to improve the agent's understanding.
- **Compliance Auditing**: Logging the final outcome of transactions, including the original request and the result, to a secure audit trail.
## Processing Scopes
While processing logic can be applied at various levels (Agent, Model, Tool), this guide primarily focuses on **Tool Level** processing, which is most relevant for granular control over tool execution.
### Tool Level (Primary Focus)
Wraps individual tool executions. This is best for logic specific to a single tool or a set of tools.
- **Scope**: Intercepts the raw inputs (arguments) to a tool and its outputs.
- **Use Cases**: Argument validation, output formatting, specific privacy rules for sensitive tools.
### Other Levels
It is helpful to understand how tool-level processing differs from other scopes:
- **Model Level**: Intercepts individual calls to the LLM (prompts and responses). Unlike tool-level, this applies globally to all text sent/received, making it better for global PII redaction or token tracking.
- **Agent Level**: Wraps the high-level execution loop (e.g., a "turn" in the conversation). Unlike tool-level, this envelopes the entire turn (user input to final response), making it suitable for session management or end-to-end auditing.
## Samples

View File

@@ -0,0 +1,40 @@
---
title: "Python"
type: docs
weight: 1
description: >
How to add pre- and post- processing to your Agents using Python.
---
## Prerequisites
This tutorial assumes that you have set up Toolbox with a basic agent as described in the [local quickstart](../../getting-started/local_quickstart.md).
This guide demonstrates how to implement these patterns in your Toolbox applications.
## Implementation
{{< tabpane persist=header >}}
{{% tab header="ADK" text=true %}}
Coming soon.
{{% /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.
```py
{{< include "python/langchain/agent.py" >}}
```
You can also add model-level (`wrap_model`) and agent-level (`before_agent`, `after_agent`) hooks to intercept messages at different stages of the execution loop. See the [LangChain Middleware documentation](https://docs.langchain.com/oss/python/langchain/middleware/custom#wrap-style-hooks) for details on these additional hook types.
{{% /tab %}}
{{< /tabpane >}}
## Results
The output should look similar to the following. Note that exact responses may vary due to the non-deterministic nature of LLMs and differences between orchestration frameworks.
```
AI: Booking Confirmed! You earned 500 Loyalty Points with this stay.
AI: Error: Maximum stay duration is 14 days.
```

View File

@@ -0,0 +1,19 @@
# 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.
# This file makes the 'pre_post_processing/python' directory a Python package.
# You can include any package-level initialization logic here if needed.
# For now, this file is empty.

View File

@@ -0,0 +1,51 @@
# 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.
import asyncio
import importlib
import os
from pathlib import Path
import pytest
ORCH_NAME = os.environ.get("ORCH_NAME")
module_path = f"python.{ORCH_NAME}.agent"
agent = importlib.import_module(module_path)
GOLDEN_KEYWORDS = [
"AI:",
"Loyalty Points",
"POLICY CHECK: Intercepting 'update-hotel'",
]
# --- Execution Tests ---
class TestExecution:
"""Test framework execution and output validation."""
@pytest.fixture(scope="function")
def script_output(self, capsys):
"""Run the agent function and return its output."""
asyncio.run(agent.main())
return capsys.readouterr()
def test_script_runs_without_errors(self, script_output):
"""Test that the script runs and produces no stderr."""
assert script_output.err == "", f"Script produced stderr: {script_output.err}"
def test_keywords_in_output(self, script_output):
"""Test that expected keywords are present in the script's output."""
output = script_output.out
print(f"\nAgent Output:\n{output}\n")
missing_keywords = [kw for kw in GOLDEN_KEYWORDS if kw not in output]
assert not missing_keywords, f"Missing keywords in output: {missing_keywords}"

View File

@@ -0,0 +1,116 @@
import asyncio
from datetime import datetime
from langchain.agents import create_agent
from langchain.agents.middleware import wrap_tool_call
from langchain_core.messages import ToolMessage
from langchain_google_vertexai import ChatVertexAI
from toolbox_langchain import ToolboxClient
system_prompt = """
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
@wrap_tool_call
async def enforce_business_rules(request, handler):
"""
Business Logic Validation:
Enforces max stay duration (e.g., max 14 days).
"""
tool_call = request.tool_call
name = tool_call["name"]
args = tool_call["args"]
print(f"POLICY CHECK: Intercepting '{name}'")
if name == "update-hotel":
if "checkin_date" in args and "checkout_date" in args:
try:
start = datetime.fromisoformat(args["checkin_date"])
end = datetime.fromisoformat(args["checkout_date"])
duration = (end - start).days
if duration > 14:
print("BLOCKED: Stay too long")
return ToolMessage(
content="Error: Maximum stay duration is 14 days.",
tool_call_id=tool_call["id"],
)
except ValueError:
pass # Ignore invalid date formats
# PRE: Code here runs BEFORE the tool execution
# EXEC: Execute the tool (or next middleware)
result = await handler(request)
# POST: Code here runs AFTER the tool execution
return result
# Post processing
@wrap_tool_call
async def enrich_response(request, handler):
"""
Post-Processing & Enrichment:
Adds loyalty points information to successful bookings.
Standardizes output format.
"""
# PRE: Code here runs BEFORE the tool execution
# EXEC: Execute the tool (or next middleware)
result = await handler(request)
# POST: Code here runs AFTER the tool execution
if isinstance(result, ToolMessage):
content = str(result.content)
tool_name = request.tool_call["name"]
if tool_name == "book-hotel" and "Error" not in content:
loyalty_bonus = 500
result.content = f"Booking Confirmed!\n You earned {loyalty_bonus} Loyalty Points with this stay.\n\nSystem Details: {content}"
return result
async def main():
async with ToolboxClient("http://127.0.0.1:5000") as client:
tools = await client.aload_toolset("my-toolset")
model = ChatVertexAI(model="gemini-2.5-flash")
agent = create_agent(
system_prompt=system_prompt,
model=model,
tools=tools,
# add any pre and post processing methods
middleware=[enforce_business_rules, enrich_response],
)
user_input = "Book hotel with id 3."
response = await agent.ainvoke(
{"messages": [{"role": "user", "content": user_input}]}
)
print("-" * 50)
last_ai_msg = response["messages"][-1].content
print(f"AI: {last_ai_msg}")
# Test Pre-processing
print("-" * 50)
user_input = "Update my hotel with id 3 with checkin date 2025-01-18 and checkout date 2025-01-20"
response = await agent.ainvoke(
{"messages": [{"role": "user", "content": user_input}]}
)
last_ai_msg = response["messages"][-1].content
print(f"AI: {last_ai_msg}")
if __name__ == "__main__":
asyncio.run(main())

View File

@@ -0,0 +1,3 @@
langchain==1.2.6
langchain-google-vertexai==3.2.2
toolbox-langchain==0.5.8

42
go.mod
View File

@@ -13,6 +13,7 @@ require (
cloud.google.com/go/dataproc/v2 v2.15.0
cloud.google.com/go/firestore v1.20.0
cloud.google.com/go/geminidataanalytics v0.3.0
cloud.google.com/go/logging v1.13.1
cloud.google.com/go/longrunning v0.7.0
cloud.google.com/go/spanner v1.86.1
github.com/ClickHouse/clickhouse-go/v2 v2.40.3
@@ -46,11 +47,12 @@ require (
github.com/sijms/go-ora/v2 v2.9.0
github.com/snowflakedb/gosnowflake v1.18.1
github.com/spf13/cobra v1.10.1
github.com/testcontainers/testcontainers-go v0.40.0
github.com/thlib/go-timezone-local v0.0.7
github.com/trinodb/trino-go-client v0.330.0
github.com/valkey-io/valkey-go v1.0.68
github.com/yugabyte/pgx/v5 v5.5.3-yb-5
go.mongodb.org/mongo-driver v1.17.4
go.mongodb.org/mongo-driver/v2 v2.4.2
go.opentelemetry.io/contrib/propagators/autoprop v0.62.0
go.opentelemetry.io/otel v1.38.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0
@@ -90,16 +92,19 @@ require (
cloud.google.com/go/iam v1.5.3 // indirect
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.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
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.5.3 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.54.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/PuerkitoBio/goquery v1.10.3 // indirect
github.com/VictoriaMetrics/easyproto v0.1.4 // indirect
github.com/ajg/form v1.5.1 // indirect
@@ -125,17 +130,29 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.34.4 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.38.4 // indirect
github.com/aws/smithy-go v1.23.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/couchbase/gocbcore/v10 v10.8.1 // indirect
github.com/couchbase/gocbcoreps v0.1.4 // indirect
github.com/couchbase/goprotostellar v1.0.2 // indirect
github.com/couchbase/tools-common/errors v1.0.0 // indirect
github.com/couchbaselabs/gocbconnstr/v2 v2.0.0 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/danieljoos/wincred v1.2.2 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v28.5.1+incompatible // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.7.0 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -144,6 +161,7 @@ require (
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/goccy/go-json v0.10.5 // indirect
@@ -179,27 +197,46 @@ require (
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.11 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 // indirect
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.1.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/nakagami/chacha20 v0.1.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/shirou/gopsutil/v4 v4.25.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.9 // indirect
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/errs v1.4.0 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect
@@ -232,6 +269,7 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.66.10 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect

67
go.sum
View File

@@ -370,8 +370,8 @@ cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6
cloud.google.com/go/lifesciences v0.8.0/go.mod h1:lFxiEOMqII6XggGbOnKiyZ7IBwoIqA84ClvoezaA/bo=
cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw=
cloud.google.com/go/logging v1.7.0/go.mod h1:3xjP2CjkM3ZkO73aj4ASA5wRPGGCRrPIAeNqVNkzY8M=
cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc=
cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA=
cloud.google.com/go/logging v1.13.1 h1:O7LvmO0kGLaHY/gq8cV7T0dyp6zJhYAOtZPX4TF3QtY=
cloud.google.com/go/logging v1.13.1/go.mod h1:XAQkfkMBxQRjQek96WLPNze7vsOmay9H5PqfsNYDqvw=
cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE=
cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc=
cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo=
@@ -647,6 +647,8 @@ github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMb
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0=
github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
@@ -800,6 +802,14 @@ github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443 h1:aQ3y1lwWyqYPiWZThqv
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/couchbase/gocb/v2 v2.11.1 h1:xWDco7Qk/XSvGUjbUWRaXi0V35nsMijJnm4vHXN/rqY=
github.com/couchbase/gocb/v2 v2.11.1/go.mod h1:aSh1Cmd1sPRpYyiBD5iWPehPWaTVF/oYhrtOAITWb/4=
github.com/couchbase/gocbcore/v10 v10.8.1 h1:i4SnH0DH9APGC4GS2vS2m+3u08V7oJwviamOXdgAZOQ=
@@ -816,8 +826,12 @@ github.com/couchbaselabs/gocaves/client v0.0.0-20250107114554-f96479220ae8 h1:MQ
github.com/couchbaselabs/gocaves/client v0.0.0-20250107114554-f96479220ae8/go.mod h1:AVekAZwIY2stsJOMWLAS/0uA/+qdp7pjO8EHnl61QkY=
github.com/couchbaselabs/gocbconnstr/v2 v2.0.0 h1:HU9DlAYYWR69jQnLN6cpg0fh0hxW/8d5hnglCXXjW78=
github.com/couchbaselabs/gocbconnstr/v2 v2.0.0/go.mod h1:o7T431UOfFVHDNvMBUmUxpHnhivwv7BziUao/nMl81E=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -826,10 +840,12 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY=
github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk=
github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
@@ -840,6 +856,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dvsekhvalnov/jose2go v1.7.0 h1:bnQc8+GMnidJZA8zc6lLEAb4xNrIqHwO+9TzqvtQZPo=
github.com/dvsekhvalnov/jose2go v1.7.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/elastic/elastic-transport-go/v8 v8.8.0 h1:7k1Ua+qluFr6p1jfJjGDl97ssJS/P7cHNInzfxgBQAo=
github.com/elastic/elastic-transport-go/v8 v8.8.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
github.com/elastic/go-elasticsearch/v9 v9.2.0 h1:COeL/g20+ixnUbffe4Wfbu88emrHjAq/LhVfmrjqRQs=
@@ -913,6 +931,8 @@ github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@@ -1174,9 +1194,13 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/looker-open-source/sdk-codegen/go v0.25.22 h1:DGYt1v2R2uE/m71sWAvgxsJnDLM9B7C40N5/CTDlE2A=
github.com/looker-open-source/sdk-codegen/go v0.25.22/go.mod h1:Br1ntSiruDJ/4nYNjpYyWyCbqJ7+GQceWbIgn0hYims=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA=
github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
@@ -1194,8 +1218,18 @@ github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8D
github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -1204,8 +1238,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
github.com/nakagami/chacha20 v0.1.0 h1:2fbf5KeVUw7oRpAe6/A7DqvBJLYYu0ka5WstFbnkEVo=
@@ -1254,6 +1288,8 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
@@ -1275,6 +1311,8 @@ github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfF
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs=
github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sijms/go-ora/v2 v2.9.0 h1:+iQbUeTeCOFMb5BsOMgUhV8KWyrv9yjKpcK4x7+MFrg=
@@ -1312,9 +1350,15 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU=
github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY=
github.com/thlib/go-timezone-local v0.0.7 h1:fX8zd3aJydqLlTs/TrROrIIdztzsdFV23OzOQx31jII=
github.com/thlib/go-timezone-local v0.0.7/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/trinodb/trino-go-client v0.330.0 h1:TBbHjFBuRjYbGtkNyRAJfzLOcwvz8ECihtMtxSzXqOc=
github.com/trinodb/trino-go-client v0.330.0/go.mod h1:BXj9QNy6pA4Gn8eIu9dVdRhetABCjFAOZ6xxsVsOZJE=
github.com/valkey-io/valkey-go v1.0.68 h1:bTbfonp49b41DqrF30q+y2JL3gcbjd2IiacFAtO4JBA=
@@ -1347,6 +1391,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/errs v1.4.0 h1:XNdoD/RRMKP7HD0UhJnIzUy74ISdGGxURlYG8HSWSfM=
@@ -1356,8 +1402,8 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs=
gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE=
go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.mongodb.org/mongo-driver/v2 v2.4.2 h1:HrJ+Auygxceby9MLp3YITobef5a8Bv4HcPFIkml1U7U=
go.mongodb.org/mongo-driver/v2 v2.4.2/go.mod h1:jHeEDJHJq7tm6ZF45Issun9dbogjfnPySb1vXA7EeAI=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -1627,6 +1673,7 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1648,6 +1695,7 @@ golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1700,6 +1748,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -2129,6 +2178,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

View File

@@ -0,0 +1,161 @@
// 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 invoke
import (
"context"
"encoding/json"
"fmt"
"io"
"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/util/parameters"
"github.com/spf13/cobra"
)
// RootCommand defines the interface for required by invoke 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
// Out returns the writer used for standard output.
Out() io.Writer
// 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
}
func NewCommand(rootCmd RootCommand) *cobra.Command {
cmd := &cobra.Command{
Use: "invoke <tool-name> [params]",
Short: "Execute a tool directly",
Long: `Execute a tool directly with parameters.
Params must be a JSON string.
Example:
toolbox invoke my-tool '{"param1": "value1"}'`,
Args: cobra.MinimumNArgs(1),
RunE: func(c *cobra.Command, args []string) error {
return runInvoke(c, args, rootCmd)
},
}
return cmd
}
func runInvoke(cmd *cobra.Command, args []string, rootCmd RootCommand) error {
ctx, cancel := context.WithCancel(cmd.Context())
defer cancel()
ctx, shutdown, err := rootCmd.Setup(ctx)
if err != nil {
return err
}
defer func() {
_ = shutdown(ctx)
}()
// Load and merge tool configurations
if err := rootCmd.LoadConfig(ctx); err != nil {
return err
}
// Initialize Resources
sourcesMap, authServicesMap, embeddingModelsMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap, err := server.InitializeConfigs(ctx, rootCmd.Config())
if err != nil {
errMsg := fmt.Errorf("failed to initialize resources: %w", err)
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
resourceMgr := resources.NewResourceManager(sourcesMap, authServicesMap, embeddingModelsMap, toolsMap, toolsetsMap, promptsMap, promptsetsMap)
// Execute Tool
toolName := args[0]
tool, ok := resourceMgr.GetTool(toolName)
if !ok {
errMsg := fmt.Errorf("tool %q not found", toolName)
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
var paramsInput string
if len(args) > 1 {
paramsInput = args[1]
}
params := make(map[string]any)
if paramsInput != "" {
if err := json.Unmarshal([]byte(paramsInput), &params); err != nil {
errMsg := fmt.Errorf("params must be a valid JSON string: %w", err)
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
}
parsedParams, err := parameters.ParseParams(tool.GetParameters(), params, nil)
if err != nil {
errMsg := fmt.Errorf("invalid parameters: %w", err)
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
parsedParams, err = tool.EmbedParams(ctx, parsedParams, resourceMgr.GetEmbeddingModelMap())
if err != nil {
errMsg := fmt.Errorf("error embedding parameters: %w", err)
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
// Client Auth not supported for ephemeral CLI call
requiresAuth, err := tool.RequiresClientAuthorization(resourceMgr)
if err != nil {
errMsg := fmt.Errorf("failed to check auth requirements: %w", err)
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
if requiresAuth {
errMsg := fmt.Errorf("client authorization is not supported")
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
result, err := tool.Invoke(ctx, resourceMgr, parsedParams, "")
if err != nil {
errMsg := fmt.Errorf("tool execution failed: %w", err)
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
// Print Result
output, err := json.MarshalIndent(result, "", " ")
if err != nil {
errMsg := fmt.Errorf("failed to marshal result: %w", err)
rootCmd.Logger().ErrorContext(ctx, errMsg.Error())
return errMsg
}
fmt.Fprintln(rootCmd.Out(), string(output))
return nil
}

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,296 @@
// 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/sources"
"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 buf bytes.Buffer
encoder := yaml.NewEncoder(&buf)
// Process Tool Config
toolWrapper := struct {
Kind string `yaml:"kind"`
Config tools.ToolConfig `yaml:",inline"`
}{
Kind: "tools",
Config: toolCfg,
}
if err := encoder.Encode(toolWrapper); err != nil {
return nil, fmt.Errorf("error encoding tool config: %w", err)
}
// Process Source 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)
}
if sourceName, ok := toolMap["source"].(string); ok && sourceName != "" {
sourceCfg, ok := cfg.SourceConfigs[sourceName]
if !ok {
return nil, fmt.Errorf("error finding source config: %s", sourceName)
}
sourceWrapper := struct {
Kind string `yaml:"kind"`
Config sources.SourceConfig `yaml:",inline"`
}{
Kind: "sources",
Config: sourceCfg,
}
if err := encoder.Encode(sourceWrapper); err != nil {
return nil, fmt.Errorf("error encoding source config: %w", err)
}
}
return buf.Bytes(), nil
}

View File

@@ -0,0 +1,347 @@
// 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 {
Name string `yaml:"name"`
Type string `yaml:"type"`
Source string `yaml:"source"`
Other string `yaml:"other"`
Parameters parameters.Parameters `yaml:"parameters"`
}
func (m MockToolConfig) ToolConfigType() string {
return m.Type
}
func (m MockToolConfig) Initialize(map[string]sources.Source) (tools.Tool, error) {
return nil, nil
}
type MockSourceConfig struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
ConnectionString string `yaml:"connection_string"`
}
func (m MockSourceConfig) SourceConfigType() string {
return m.Type
}
func (m MockSourceConfig) Initialize(context.Context, trace.Tracer) (sources.Source, error) {
return nil, 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{
Name: "tool1",
Type: "custom-tool",
Source: "src1",
Other: "foo",
},
"toolNoSource": MockToolConfig{
Name: "toolNoSource",
Type: "http",
},
"toolWithParams": MockToolConfig{
Name: "toolWithParams",
Type: "custom-tool",
Parameters: []parameters.Parameter{
parameters.NewStringParameter("param1", "desc1"),
},
},
"toolWithMissingSource": MockToolConfig{
Name: "toolWithMissingSource",
Type: "custom-tool",
Source: "missing-src",
},
},
SourceConfigs: server.SourceConfigs{
"src1": MockSourceConfig{
Name: "src1",
Type: "postgres",
ConnectionString: "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: "tool with parameters",
toolName: "toolWithParams",
wantContains: []string{
"kind: tools",
"name: toolWithParams",
"type: custom-tool",
"parameters:",
"- name: param1",
"type: string",
"description: desc1",
},
},
{
name: "non-existent tool",
toolName: "missing-tool",
wantErr: true,
},
{
name: "tool with missing source config",
toolName: "toolWithMissingSource",
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

@@ -22,6 +22,18 @@ import (
"strings"
)
// NewLogger creates a new logger based on the provided format and level.
func NewLogger(format, level string, out, err io.Writer) (Logger, error) {
switch strings.ToLower(format) {
case "json":
return NewStructuredLogger(out, err, level)
case "standard":
return NewStdLogger(out, err, level)
default:
return nil, fmt.Errorf("logging format invalid: %s", format)
}
}
// StdLogger is the standard logger
type StdLogger struct {
outLogger *slog.Logger

View File

@@ -21,6 +21,7 @@ import (
)
var expectedToolSources = []string{
"alloydb-omni",
"alloydb-postgres-admin",
"alloydb-postgres-observability",
"alloydb-postgres",
@@ -99,36 +100,40 @@ func TestLoadPrebuiltToolYAMLs(t *testing.T) {
}
func TestGetPrebuiltTool(t *testing.T) {
alloydb_admin_config, _ := Get("alloydb-postgres-admin")
alloydb_observability_config, _ := Get("alloydb-postgres-observability")
alloydb_config, _ := Get("alloydb-postgres")
bigquery_config, _ := Get("bigquery")
clickhouse_config, _ := Get("clickhouse")
cloudsqlpg_observability_config, _ := Get("cloud-sql-postgres-observability")
cloudsqlpg_config, _ := Get("cloud-sql-postgres")
cloudsqlpg_admin_config, _ := Get("cloud-sql-postgres-admin")
cloudsqlmysql_admin_config, _ := Get("cloud-sql-mysql-admin")
cloudsqlmssql_admin_config, _ := Get("cloud-sql-mssql-admin")
cloudsqlmysql_observability_config, _ := Get("cloud-sql-mysql-observability")
cloudsqlmysql_config, _ := Get("cloud-sql-mysql")
cloudsqlmssql_observability_config, _ := Get("cloud-sql-mssql-observability")
cloudsqlmssql_config, _ := Get("cloud-sql-mssql")
dataplex_config, _ := Get("dataplex")
firestoreconfig, _ := Get("firestore")
looker_config, _ := Get("looker")
lookerca_config, _ := Get("looker-conversational-analytics")
mysql_config, _ := Get("mysql")
mssql_config, _ := Get("mssql")
oceanbase_config, _ := Get("oceanbase")
postgresconfig, _ := Get("postgres")
singlestore_config, _ := Get("singlestore")
spanner_config, _ := Get("spanner")
spannerpg_config, _ := Get("spanner-postgres")
mindsdb_config, _ := Get("mindsdb")
sqlite_config, _ := Get("sqlite")
neo4jconfig, _ := Get("neo4j")
healthcare_config, _ := Get("cloud-healthcare")
snowflake_config, _ := Get("snowflake")
alloydb_omni_config := getOrFatal(t, "alloydb-omni")
alloydb_admin_config := getOrFatal(t, "alloydb-postgres-admin")
alloydb_observability_config := getOrFatal(t, "alloydb-postgres-observability")
alloydb_config := getOrFatal(t, "alloydb-postgres")
bigquery_config := getOrFatal(t, "bigquery")
clickhouse_config := getOrFatal(t, "clickhouse")
cloudsqlpg_observability_config := getOrFatal(t, "cloud-sql-postgres-observability")
cloudsqlpg_config := getOrFatal(t, "cloud-sql-postgres")
cloudsqlpg_admin_config := getOrFatal(t, "cloud-sql-postgres-admin")
cloudsqlmysql_admin_config := getOrFatal(t, "cloud-sql-mysql-admin")
cloudsqlmssql_admin_config := getOrFatal(t, "cloud-sql-mssql-admin")
cloudsqlmysql_observability_config := getOrFatal(t, "cloud-sql-mysql-observability")
cloudsqlmysql_config := getOrFatal(t, "cloud-sql-mysql")
cloudsqlmssql_observability_config := getOrFatal(t, "cloud-sql-mssql-observability")
cloudsqlmssql_config := getOrFatal(t, "cloud-sql-mssql")
dataplex_config := getOrFatal(t, "dataplex")
firestoreconfig := getOrFatal(t, "firestore")
looker_config := getOrFatal(t, "looker")
lookerca_config := getOrFatal(t, "looker-conversational-analytics")
mysql_config := getOrFatal(t, "mysql")
mssql_config := getOrFatal(t, "mssql")
oceanbase_config := getOrFatal(t, "oceanbase")
postgresconfig := getOrFatal(t, "postgres")
singlestore_config := getOrFatal(t, "singlestore")
spanner_config := getOrFatal(t, "spanner")
spannerpg_config := getOrFatal(t, "spanner-postgres")
mindsdb_config := getOrFatal(t, "mindsdb")
sqlite_config := getOrFatal(t, "sqlite")
neo4jconfig := getOrFatal(t, "neo4j")
healthcare_config := getOrFatal(t, "cloud-healthcare")
snowflake_config := getOrFatal(t, "snowflake")
if len(alloydb_omni_config) <= 0 {
t.Fatalf("unexpected error: could not fetch alloydb omni prebuilt tools yaml")
}
if len(alloydb_admin_config) <= 0 {
t.Fatalf("unexpected error: could not fetch alloydb admin prebuilt tools yaml")
}
@@ -233,3 +238,11 @@ func TestFailGetPrebuiltTool(t *testing.T) {
t.Fatalf("unexpected an error but got nil.")
}
}
func getOrFatal(t *testing.T, prebuiltSourceConfig string) []byte {
bytes, err := Get(prebuiltSourceConfig)
if err != nil {
t.Fatalf("Cannot get prebuilt config for %q, error %v", prebuiltSourceConfig, err)
}
return bytes
}

View File

@@ -0,0 +1,277 @@
# 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.
sources:
alloydb-omni-source:
kind: postgres
host: ${ALLOYDB_OMNI_HOST:localhost}
port: ${ALLOYDB_OMNI_PORT:5432}
database: ${ALLOYDB_OMNI_DATABASE}
user: ${ALLOYDB_OMNI_USER}
password: ${ALLOYDB_OMNI_PASSWORD:}
queryParams: ${ALLOYDB_OMNI_QUERY_PARAMS:}
tools:
execute_sql:
kind: postgres-execute-sql
source: alloydb-omni-source
description: Use this tool to execute sql.
list_tables:
kind: postgres-list-tables
source: alloydb-omni-source
description: "Lists detailed schema information (object type, columns, constraints, indexes, triggers, owner, comment) as JSON for user-created tables (ordinary or partitioned). Filters by a comma-separated list of names. If names are omitted, lists all tables in user schemas."
list_active_queries:
kind: postgres-list-active-queries
source: alloydb-omni-source
description: "List the top N (default 50) currently running queries (state='active') from pg_stat_activity, ordered by longest-running first. Returns pid, user, database, application_name, client_addr, state, wait_event_type/wait_event, backend/xact/query start times, computed query_duration, and the SQL text."
list_available_extensions:
kind: postgres-list-available-extensions
source: alloydb-omni-source
description: "Discover all PostgreSQL extensions available for installation on this server, returning name, default_version, and description."
list_installed_extensions:
kind: postgres-list-installed-extensions
source: alloydb-omni-source
description: "List all installed PostgreSQL extensions with their name, version, schema, owner, and description."
long_running_transactions:
kind: postgres-long-running-transactions
source: alloydb-omni-source
list_locks:
kind: postgres-list-locks
source: alloydb-omni-source
replication_stats:
kind: postgres-replication-stats
source: alloydb-omni-source
list_autovacuum_configurations:
kind: postgres-sql
source: alloydb-omni-source
description: "List PostgreSQL autovacuum-related configurations (name and current setting) from pg_settings."
statement: |
SELECT name,
setting
FROM pg_settings
WHERE category = 'Autovacuum';
list_columnar_configurations:
kind: postgres-sql
source: alloydb-omni-source
description: "List AlloyDB Omni columnar-related configurations (name and current setting) from pg_settings."
statement: |
SELECT name,
setting
FROM pg_settings
WHERE name like 'google_columnar_engine.%';
list_columnar_recommended_columns:
kind: postgres-sql
source: alloydb-omni-source
description: "Lists columns that AlloyDB Omni recommends adding to the columnar engine to improve query performance."
statement: select * from g_columnar_recommended_columns;
list_memory_configurations:
kind: postgres-sql
source: alloydb-omni-source
description: "List PostgreSQL memory-related configurations (name and current setting) from pg_settings."
statement: |
(
SELECT
name,
pg_size_pretty((setting::bigint * 1024)::bigint) setting
FROM pg_settings
WHERE name IN ('work_mem', 'maintenance_work_mem')
)
UNION ALL
(
SELECT
name,
pg_size_pretty((((setting::bigint) * 8) * 1024)::bigint)
FROM pg_settings
WHERE name IN ('shared_buffers', 'wal_buffers', 'effective_cache_size', 'temp_buffers')
)
ORDER BY 1 DESC;
list_top_bloated_tables:
kind: postgres-sql
source: alloydb-omni-source
description: |
List the top tables by dead-tuple (approximate bloat signal), returning schema, table, live/dead tuples, percentage, and last vacuum/analyze times.
statement: |
SELECT
schemaname AS schema_name,
relname AS relation_name,
n_live_tup AS live_tuples,
n_dead_tup AS dead_tuples,
TRUNC((n_dead_tup::NUMERIC / NULLIF(n_live_tup + n_dead_tup, 0)) * 100, 2) AS dead_tuple_percentage,
last_vacuum,
last_autovacuum,
last_analyze,
last_autoanalyze
FROM pg_stat_user_tables
ORDER BY n_dead_tup DESC
LIMIT COALESCE($1::int, 50);
parameters:
- name: limit
description: "The maximum number of results to return."
type: integer
default: 50
list_replication_slots:
kind: postgres-sql
source: alloydb-omni-source
description: "List key details for all PostgreSQL replication slots (e.g., type, database, active status) and calculates the size of the outstanding WAL that is being prevented from removal by the slot."
statement: |
SELECT
slot_name,
slot_type,
plugin,
database,
temporary,
active,
restart_lsn,
confirmed_flush_lsn,
xmin,
catalog_xmin,
pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained_wal
FROM pg_replication_slots;
list_invalid_indexes:
kind: postgres-sql
source: alloydb-omni-source
description: "Lists all invalid PostgreSQL indexes which are taking up disk space but are unusable by the query planner. Typically created by failed CREATE INDEX CONCURRENTLY operations."
statement: |
SELECT
nspname AS schema_name,
indexrelid::regclass AS index_name,
indrelid::regclass AS table_name,
pg_size_pretty(pg_total_relation_size(indexrelid)) AS index_size,
indisready,
indisvalid,
pg_get_indexdef(pg_class.oid) AS index_def
FROM pg_index
JOIN pg_class ON pg_class.oid = pg_index.indexrelid
JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace
WHERE indisvalid = FALSE;
get_query_plan:
kind: postgres-sql
source: alloydb-omni-source
description: "Generate a PostgreSQL EXPLAIN plan in JSON format for a single SQL statement—without executing it. This returns the optimizer's estimated plan, costs, and rows (no ANALYZE, no extra options). Use in production safely for plan inspection, regression checks, and query tuning workflows."
statement: |
EXPLAIN (FORMAT JSON) {{.query}};
templateParameters:
- name: query
type: string
description: "The SQL statement for which you want to generate plan (omit the EXPLAIN keyword)."
required: true
list_views:
kind: postgres-list-views
source: alloydb-omni-source
list_schemas:
kind: postgres-list-schemas
source: alloydb-omni-source
list_indexes:
kind: postgres-list-indexes
source: alloydb-omni-source
list_sequences:
kind: postgres-list-sequences
source: alloydb-omni-source
database_overview:
kind: postgres-database-overview
source: alloydb-omni-source
list_triggers:
kind: postgres-list-triggers
source: alloydb-omni-source
list_query_stats:
kind: postgres-list-query-stats
source: alloydb-omni-source
get_column_cardinality:
kind: postgres-get-column-cardinality
source: alloydb-omni-source
list_table_stats:
kind: postgres-list-table-stats
source: alloydb-omni-source
list_publication_tables:
kind: postgres-list-publication-tables
source: alloydb-omni-source
list_tablespaces:
kind: postgres-list-tablespaces
source: alloydb-omni-source
list_pg_settings:
kind: postgres-list-pg-settings
source: alloydb-omni-source
list_database_stats:
kind: postgres-list-database-stats
source: alloydb-omni-source
list_roles:
kind: postgres-list-roles
source: alloydb-omni-source
list_stored_procedure:
kind: postgres-list-stored-procedure
source: alloydb-omni-source
toolsets:
alloydb_omni_database_tools:
- execute_sql
- list_tables
- list_active_queries
- list_available_extensions
- list_installed_extensions
- list_autovacuum_configurations
- list_columnar_configurations
- list_columnar_recommended_columns
- list_memory_configurations
- list_top_bloated_tables
- list_replication_slots
- list_invalid_indexes
- get_query_plan
- list_views
- list_schemas
- database_overview
- list_triggers
- list_indexes
- list_sequences
- long_running_transactions
- list_locks
- replication_stats
- list_query_stats
- get_column_cardinality
- list_publication_tables
- list_tablespaces
- list_pg_settings
- list_database_stats
- list_roles
- list_table_stats
- list_stored_procedure

View File

@@ -959,6 +959,21 @@ tools:
Output:
A confirmation message upon successful file deletion.
validate_project:
kind: looker-validate-project
source: looker-source
description: |
This tool checks a LookML project for syntax errors.
Prerequisite: The Looker session must be in Development Mode. Use `dev_mode: true` first.
Parameters:
- project_id (required): The unique ID of the LookML project.
Output:
A list of error details including the file path and line number, and also a list of models
that are not currently valid due to LookML errors.
get_connections:
kind: looker-get-connections
source: looker-source
@@ -1072,6 +1087,7 @@ toolsets:
- create_project_file
- update_project_file
- delete_project_file
- validate_project
- get_connections
- get_connection_schemas
- get_connection_databases

View File

@@ -26,6 +26,7 @@ import (
"github.com/go-chi/render"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
@@ -231,7 +232,7 @@ func toolInvokeHandler(s *Server, w http.ResponseWriter, r *http.Request) {
return
}
params, err := tool.ParseParams(data, claimsFromAuth)
params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth)
if err != nil {
// If auth error, return 401
if errors.Is(err, util.ErrUnauthorized) {

View File

@@ -24,7 +24,6 @@ import (
"testing"
"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/prompts"
"github.com/googleapis/genai-toolbox/internal/server/resources"
@@ -41,140 +40,6 @@ var (
_ 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, 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
}
var tool1 = MockTool{
Name: "no_params",
Params: []parameters.Parameter{},

View File

@@ -28,6 +28,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/server/resources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
// ProcessMethod returns a response for the request.
@@ -176,7 +177,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
}
logger.DebugContext(ctx, "tool invocation authorized")
params, err := tool.ParseParams(data, claimsFromAuth)
params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth)
if err != nil {
err = fmt.Errorf("provided parameters were invalid: %w", err)
return jsonrpc.NewError(id, jsonrpc.INVALID_PARAMS, err.Error(), nil), err

View File

@@ -28,6 +28,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/server/resources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
// ProcessMethod returns a response for the request.
@@ -176,7 +177,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
}
logger.DebugContext(ctx, "tool invocation authorized")
params, err := tool.ParseParams(data, claimsFromAuth)
params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth)
if err != nil {
err = fmt.Errorf("provided parameters were invalid: %w", err)
return jsonrpc.NewError(id, jsonrpc.INVALID_PARAMS, err.Error(), nil), err

View File

@@ -28,6 +28,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/server/resources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
// ProcessMethod returns a response for the request.
@@ -169,7 +170,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
}
logger.DebugContext(ctx, "tool invocation authorized")
params, err := tool.ParseParams(data, claimsFromAuth)
params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth)
if err != nil {
err = fmt.Errorf("provided parameters were invalid: %w", err)
return jsonrpc.NewError(id, jsonrpc.INVALID_PARAMS, err.Error(), nil), err

View File

@@ -28,6 +28,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/server/resources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
// ProcessMethod returns a response for the request.
@@ -169,7 +170,7 @@ func toolsCallHandler(ctx context.Context, id jsonrpc.RequestId, resourceMgr *re
}
logger.DebugContext(ctx, "tool invocation authorized")
params, err := tool.ParseParams(data, claimsFromAuth)
params, err := parameters.ParseParams(tool.GetParameters(), data, claimsFromAuth)
if err != nil {
err = fmt.Errorf("provided parameters were invalid: %w", err)
return jsonrpc.NewError(id, jsonrpc.INVALID_PARAMS, err.Error(), nil), err

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

@@ -12,6 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { escapeHtml } from './sanitize.js';
/**
* Renders the Google Sign-In button using the GIS library.
* @param {string} toolId The ID of the tool.
@@ -112,13 +114,14 @@ function handleCredentialResponse(response, toolId, authProfileName) {
// creates the Google Auth method dropdown
export function createGoogleAuthMethodItem(toolId, authProfileName) {
const safeProfileName = escapeHtml(authProfileName);
const UNIQUE_ID_BASE = `${toolId}-${authProfileName}`;
const item = document.createElement('div');
item.className = 'auth-method-item';
item.innerHTML = `
<div class="auth-method-header">
<span class="auth-method-label">Google ID Token (${authProfileName})</span>
<span class="auth-method-label">Google ID Token (${safeProfileName})</span>
<button class="toggle-details-tab">Auto Setup</button>
</div>
<div class="auth-method-details" id="google-auth-details-${UNIQUE_ID_BASE}" style="display: none;">

View File

@@ -13,6 +13,7 @@
// limitations under the License.
import { renderToolInterface } from "./toolDisplay.js";
import { escapeHtml } from "./sanitize.js";
let toolDetailsAbortController = null;
@@ -34,7 +35,7 @@ export async function loadTools(secondNavContent, toolDisplayArea, toolsetName)
renderToolList(apiResponse, secondNavContent, toolDisplayArea);
} catch (error) {
console.error('Failed to load tools:', error);
secondNavContent.innerHTML = `<p class="error">Failed to load tools: <pre><code>${error}</code></pre></p>`;
secondNavContent.innerHTML = `<p class="error">Failed to load tools: <pre><code>${escapeHtml(String(error))}</code></pre></p>`;
}
}
@@ -168,7 +169,7 @@ async function fetchToolDetails(toolName, toolDisplayArea) {
console.debug("Previous fetch was aborted, expected behavior.");
} else {
console.error(`Failed to load details for tool "${toolName}":`, error);
toolDisplayArea.innerHTML = `<p class="error">Failed to load details for ${toolName}. ${error.message}</p>`;
toolDisplayArea.innerHTML = `<p class="error">Failed to load details for ${escapeHtml(toolName)}. ${escapeHtml(error.message)}</p>`;
}
}
}

View File

@@ -0,0 +1,43 @@
// 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.
/**
* Escapes special characters for safe rendering in HTML text contexts.
*
* This utility encodes user-controlled values to avoid unintended script
* execution when rendering content as HTML. It is intended as a defensive
* measure and does not perform HTML sanitization.
*
* @param {*} input The value to escape.
* @return {string} The escaped string safe for HTML rendering.
*/
const htmlEscapes = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;'
};
const escapeCharsRegex = /[&<>"'`]/g;
export function escapeHtml(input) {
if (input === null || input === undefined) {
return '';
}
const str = String(input);
return str.replace(escapeCharsRegex, (char) => htmlEscapes[char]);
}

View File

@@ -14,6 +14,7 @@
import { handleRunTool, displayResults } from './runTool.js';
import { createGoogleAuthMethodItem } from './auth.js'
import { escapeHtml } from './sanitize.js'
/**
* Helper function to create form inputs for parameters.
@@ -357,9 +358,9 @@ export function renderToolInterface(tool, containerElement) {
const descBox = document.createElement('div');
nameBox.className = 'tool-box tool-name';
nameBox.innerHTML = `<h5>Name:</h5><p>${tool.name}</p>`;
nameBox.innerHTML = `<h5>Name:</h5><p>${escapeHtml(tool.name)}</p>`;
descBox.className = 'tool-box tool-description';
descBox.innerHTML = `<h5>Description:</h5><p>${tool.description}</p>`;
descBox.innerHTML = `<h5>Description:</h5><p>${escapeHtml(tool.description)}</p>`;
toolInfoContainer.className = 'tool-info';
toolInfoContainer.appendChild(nameBox);

View File

@@ -236,9 +236,9 @@ func setupClientCaching(s *Source, baseCreator BigqueryClientCreator) {
}
// Initialize caches
s.bqClientCache = NewCache(onBqEvict)
s.bqRestCache = NewCache(nil)
s.dataplexCache = NewCache(onDataplexEvict)
s.bqClientCache = sources.NewCache(onBqEvict)
s.bqRestCache = sources.NewCache(nil)
s.dataplexCache = sources.NewCache(onDataplexEvict)
// Create the caching wrapper for the client creator
s.ClientCreator = func(tokenString string, wantRestService bool) (*bigqueryapi.Client, *bigqueryrestapi.Service, error) {
@@ -289,9 +289,9 @@ type Source struct {
Session *Session
// Caches for OAuth clients
bqClientCache *Cache
bqRestCache *Cache
dataplexCache *Cache
bqClientCache *sources.Cache
bqRestCache *sources.Cache
dataplexCache *sources.Cache
}
type Session struct {

View File

@@ -11,7 +11,7 @@
// 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 bigquery
package sources
import (
"sync"

View File

@@ -0,0 +1,439 @@
// 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 cloudloggingadmin
import (
"context"
"fmt"
"slices"
"strings"
"time"
"cloud.google.com/go/logging"
"cloud.google.com/go/logging/logadmin"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/util"
"go.opentelemetry.io/otel/trace"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/impersonate"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
const SourceType string = "cloud-logging-admin"
var _ sources.SourceConfig = Config{}
func init() {
if !sources.Register(SourceType, newConfig) {
panic(fmt.Sprintf("source type %q already registered", SourceType))
}
}
func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (sources.SourceConfig, error) {
actual := Config{Name: name}
if err := decoder.DecodeContext(ctx, &actual); err != nil {
return nil, err
}
return actual, nil
}
type Config struct {
Name string `yaml:"name" validate:"required"`
Type string `yaml:"type" validate:"required"`
Project string `yaml:"project" validate:"required"`
UseClientOAuth bool `yaml:"useClientOAuth"`
ImpersonateServiceAccount string `yaml:"impersonateServiceAccount"`
}
func (r Config) SourceConfigType() string {
return SourceType
}
func (r Config) Initialize(ctx context.Context, tracer trace.Tracer) (sources.Source, error) {
if r.UseClientOAuth && r.ImpersonateServiceAccount != "" {
return nil, fmt.Errorf("useClientOAuth cannot be used with impersonateServiceAccount")
}
var client *logadmin.Client
var tokenSource oauth2.TokenSource
var clientCreator LogAdminClientCreator
var err error
s := &Source{
Config: r,
Client: client,
TokenSource: tokenSource,
ClientCreator: clientCreator,
}
if r.UseClientOAuth {
// use client OAuth
baseClientCreator, err := newLogAdminClientCreator(ctx, tracer, r.Project, r.Name)
if err != nil {
return nil, fmt.Errorf("error constructing client creator: %w", err)
}
setupClientCaching(s, baseClientCreator)
} else {
client, tokenSource, err = initLogAdminConnection(ctx, tracer, r.Name, r.Project, r.ImpersonateServiceAccount)
if err != nil {
return nil, fmt.Errorf("error creating client from ADC %w", err)
}
s.Client = client
s.TokenSource = tokenSource
}
return s, nil
}
var _ sources.Source = &Source{}
type LogAdminClientCreator func(tokenString string) (*logadmin.Client, error)
type Source struct {
Config
Client *logadmin.Client
TokenSource oauth2.TokenSource
ClientCreator LogAdminClientCreator
// Caches for OAuth clients
logadminClientCache *sources.Cache
}
func (s *Source) SourceType() string {
// Returns logadmin source type
return SourceType
}
func (s *Source) ToConfig() sources.SourceConfig {
return s.Config
}
func (s *Source) UseClientAuthorization() bool {
return s.UseClientOAuth
}
func (s *Source) LogAdminClient() *logadmin.Client {
return s.Client
}
func (s *Source) LogAdminTokenSource() oauth2.TokenSource {
return s.TokenSource
}
func (s *Source) LogAdminClientCreator() LogAdminClientCreator {
return s.ClientCreator
}
func (s *Source) GetProject() string {
return s.Project
}
// getClient returns the appropriate client based on authentication mode
func (s *Source) getClient(accessToken string) (*logadmin.Client, error) {
if s.UseClientOAuth {
if s.ClientCreator == nil {
return nil, fmt.Errorf("client creator is not initialized")
}
return s.ClientCreator(accessToken)
}
if s.Client == nil {
return nil, fmt.Errorf("source client is not initialized")
}
return s.Client, nil
}
// ListLogNames lists all log names in the project
func (s *Source) ListLogNames(ctx context.Context, limit int, accessToken string) ([]string, error) {
client, err := s.getClient(accessToken)
if err != nil {
return nil, err
}
it := client.Logs(ctx)
var logNames []string
for len(logNames) < limit {
logName, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, err
}
logNames = append(logNames, logName)
}
return logNames, nil
}
// ListResourceTypes lists all resource types in the project
func (s *Source) ListResourceTypes(ctx context.Context, accessToken string) ([]string, error) {
client, err := s.getClient(accessToken)
if err != nil {
return nil, err
}
it := client.ResourceDescriptors(ctx)
var types []string
for {
desc, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, fmt.Errorf("failed to list resource descriptors: %w", err)
}
types = append(types, desc.Type)
}
slices.Sort(types)
return types, nil
}
// QueryLogsParams contains the parameters for querying logs
type QueryLogsParams struct {
Filter string
NewestFirst bool
StartTime string
EndTime string
Verbose bool
Limit int
}
// QueryLogs queries log entries based on the provided parameters
func (s *Source) QueryLogs(ctx context.Context, params QueryLogsParams, accessToken string) ([]map[string]any, error) {
client, err := s.getClient(accessToken)
if err != nil {
return nil, err
}
// Build filter
var filterParts []string
if params.Filter != "" {
filterParts = append(filterParts, params.Filter)
}
// Add timestamp filter
startTime := params.StartTime
if startTime != "" {
filterParts = append(filterParts, fmt.Sprintf(`timestamp>="%s"`, startTime))
}
if params.EndTime != "" {
filterParts = append(filterParts, fmt.Sprintf(`timestamp<="%s"`, params.EndTime))
}
combinedFilter := strings.Join(filterParts, " AND ")
// Add opts
opts := []logadmin.EntriesOption{
logadmin.Filter(combinedFilter),
}
// Set order
if params.NewestFirst {
opts = append(opts, logadmin.NewestFirst())
}
// Set up iterator
it := client.Entries(ctx, opts...)
var results []map[string]any
for len(results) < params.Limit {
entry, err := it.Next()
if err == iterator.Done {
break
}
if err != nil {
return nil, fmt.Errorf("failed to iterate entries: %w", err)
}
result := map[string]any{
"logName": entry.LogName,
"timestamp": entry.Timestamp.Format(time.RFC3339),
"severity": entry.Severity.String(),
"resource": map[string]any{
"type": entry.Resource.Type,
"labels": entry.Resource.Labels,
},
}
if entry.Payload != nil {
result["payload"] = entry.Payload
}
if params.Verbose {
result["insertId"] = entry.InsertID
if len(entry.Labels) > 0 {
result["labels"] = entry.Labels
}
if entry.HTTPRequest != nil {
httpRequestMap := map[string]any{
"status": entry.HTTPRequest.Status,
"latency": entry.HTTPRequest.Latency.String(),
"remoteIp": entry.HTTPRequest.RemoteIP,
}
if req := entry.HTTPRequest.Request; req != nil {
httpRequestMap["requestMethod"] = req.Method
httpRequestMap["requestUrl"] = req.URL.String()
httpRequestMap["userAgent"] = req.UserAgent()
}
result["httpRequest"] = httpRequestMap
}
if entry.Trace != "" {
result["trace"] = entry.Trace
}
if entry.SpanID != "" {
result["spanId"] = entry.SpanID
}
if entry.Operation != nil {
result["operation"] = map[string]any{
"id": entry.Operation.Id,
"producer": entry.Operation.Producer,
"first": entry.Operation.First,
"last": entry.Operation.Last,
}
}
if entry.SourceLocation != nil {
result["sourceLocation"] = map[string]any{
"file": entry.SourceLocation.File,
"line": entry.SourceLocation.Line,
"function": entry.SourceLocation.Function,
}
}
}
results = append(results, result)
}
return results, nil
}
func setupClientCaching(s *Source, baseCreator LogAdminClientCreator) {
onEvict := func(key string, value interface{}) {
if client, ok := value.(*logadmin.Client); ok && client != nil {
client.Close()
}
}
s.logadminClientCache = sources.NewCache(onEvict)
s.ClientCreator = func(tokenString string) (*logadmin.Client, error) {
if val, found := s.logadminClientCache.Get(tokenString); found {
return val.(*logadmin.Client), nil
}
client, err := baseCreator(tokenString)
if err != nil {
return nil, err
}
s.logadminClientCache.Set(tokenString, client)
return client, nil
}
}
func initLogAdminConnection(
ctx context.Context,
tracer trace.Tracer,
name string,
project string,
impersonateServiceAccount string,
) (*logadmin.Client, oauth2.TokenSource, error) {
ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceType, name)
defer span.End()
userAgent, err := util.UserAgentFromContext(ctx)
if err != nil {
return nil, nil, err
}
var tokenSource oauth2.TokenSource
var opts []option.ClientOption
if impersonateServiceAccount != "" {
// Create impersonated credentials token source with cloud-platform scope
// This broader scope is needed for tools like conversational analytics
cloudPlatformTokenSource, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
TargetPrincipal: impersonateServiceAccount,
Scopes: []string{"https://www.googleapis.com/auth/cloud-platform"},
})
if err != nil {
return nil, nil, fmt.Errorf("failed to create impersonated credentials for %q: %w", impersonateServiceAccount, err)
}
tokenSource = cloudPlatformTokenSource
opts = []option.ClientOption{
option.WithUserAgent(userAgent),
option.WithTokenSource(cloudPlatformTokenSource),
}
} else {
// Use default credentials
cred, err := google.FindDefaultCredentials(ctx, logging.AdminScope)
if err != nil {
return nil, nil, fmt.Errorf("failed to find default Google Cloud credentials with scope %q: %w", logging.AdminScope, err)
}
tokenSource = cred.TokenSource
opts = []option.ClientOption{
option.WithUserAgent(userAgent),
option.WithCredentials(cred),
}
}
client, err := logadmin.NewClient(ctx, project, opts...)
if err != nil {
return nil, nil, fmt.Errorf("failed to create Cloud Logging Admin client for project %q: %w", project, err)
}
return client, tokenSource, nil
}
func initLogAdminConnectionWithOAuthToken(
ctx context.Context,
tracer trace.Tracer,
project, name, userAgent, tokenString string,
) (*logadmin.Client, error) {
ctx, span := sources.InitConnectionSpan(ctx, tracer, SourceType, name)
defer span.End()
token := &oauth2.Token{
AccessToken: string(tokenString),
}
ts := oauth2.StaticTokenSource(token)
// Initialize the logadmin client with tokenSource
client, err := logadmin.NewClient(ctx, project, option.WithUserAgent(userAgent), option.WithTokenSource(ts))
if err != nil {
return nil, fmt.Errorf("failed to create logadmin client for project %q: %w", project, err)
}
return client, nil
}
func newLogAdminClientCreator(
ctx context.Context,
tracer trace.Tracer,
project, name string,
) (LogAdminClientCreator, error) {
userAgent, err := util.UserAgentFromContext(ctx)
if err != nil {
return nil, err
}
return func(tokenString string) (*logadmin.Client, error) {
return initLogAdminConnectionWithOAuthToken(ctx, tracer, project, name, userAgent, tokenString)
}, nil
}

View File

@@ -0,0 +1,137 @@
// 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 cloudloggingadmin_test
import (
"context"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/googleapis/genai-toolbox/internal/server"
"github.com/googleapis/genai-toolbox/internal/sources/cloudloggingadmin"
"github.com/googleapis/genai-toolbox/internal/testutils"
)
func TestParseFromYamlCloudLoggingAdmin(t *testing.T) {
tcs := []struct {
desc string
in string
want server.SourceConfigs
}{
{
desc: "basic example",
in: `
kind: sources
name: my-instance
type: cloud-logging-admin
project: my-project
`,
want: server.SourceConfigs{
"my-instance": cloudloggingadmin.Config{
Name: "my-instance",
Type: cloudloggingadmin.SourceType,
Project: "my-project",
},
},
},
{
desc: "with client oauth",
in: `
kind: sources
name: my-instance
type: cloud-logging-admin
project: my-project
useClientOAuth: true
`,
want: server.SourceConfigs{
"my-instance": cloudloggingadmin.Config{
Name: "my-instance",
Type: cloudloggingadmin.SourceType,
Project: "my-project",
UseClientOAuth: true,
},
},
},
{
desc: "with service account impersonation",
in: `
kind: sources
name: my-instance
type: cloud-logging-admin
project: my-project
impersonateServiceAccount: service-account@my-project.iam.gserviceaccount.com
`,
want: server.SourceConfigs{
"my-instance": cloudloggingadmin.Config{
Name: "my-instance",
Type: cloudloggingadmin.SourceType,
Project: "my-project",
ImpersonateServiceAccount: "service-account@my-project.iam.gserviceaccount.com",
},
},
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
got, _, _, _, _, _, err := server.UnmarshalResourceConfig(context.Background(), testutils.FormatYaml(tc.in))
if err != nil {
t.Fatalf("unable to unmarshal: %s", err)
}
if !cmp.Equal(tc.want, got) {
t.Fatalf("incorrect parse: want %v, got %v", tc.want, got)
}
})
}
}
func TestFailParseFromYaml(t *testing.T) {
tcs := []struct {
desc string
in string
err string
}{
{
desc: "extra field",
in: `
kind: sources
name: my-instance
type: cloud-logging-admin
project: my-project
foo: bar
`,
err: "error unmarshaling sources: unable to parse source \"my-instance\" as \"cloud-logging-admin\": [1:1] unknown field \"foo\"\n> 1 | foo: bar\n ^\n 2 | name: my-instance\n 3 | project: my-project\n 4 | type: cloud-logging-admin",
},
{
desc: "missing required field",
in: `
kind: sources
name: my-instance
type: cloud-logging-admin
`,
err: "error unmarshaling sources: unable to parse source \"my-instance\" as \"cloud-logging-admin\": Key: 'Config.Project' Error:Field validation for 'Project' failed on the 'required' tag",
},
}
for _, tc := range tcs {
t.Run(tc.desc, func(t *testing.T) {
_, _, _, _, _, _, err := server.UnmarshalResourceConfig(context.Background(), testutils.FormatYaml(tc.in))
if err == nil {
t.Fatalf("expect parsing to fail")
}
errStr := err.Error()
if errStr != tc.err {
t.Fatalf("unexpected error: got %q, want %q", errStr, tc.err)
}
})
}
}

View File

@@ -23,9 +23,9 @@ import (
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/util"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
"go.mongodb.org/mongo-driver/v2/mongo/options"
"go.opentelemetry.io/otel/trace"
)
@@ -148,7 +148,7 @@ func (s *Source) Aggregate(ctx context.Context, pipelineString string, canonical
return res, err
}
func (s *Source) Find(ctx context.Context, filterString, database, collection string, opts *options.FindOptions) ([]any, error) {
func (s *Source) Find(ctx context.Context, filterString, database, collection string, opts *options.FindOptionsBuilder) ([]any, error) {
var filter = bson.D{}
err := bson.UnmarshalExtJSON([]byte(filterString), false, &filter)
if err != nil {
@@ -163,7 +163,7 @@ func (s *Source) Find(ctx context.Context, filterString, database, collection st
return parseData(ctx, cur)
}
func (s *Source) FindOne(ctx context.Context, filterString, database, collection string, opts *options.FindOneOptions) ([]any, error) {
func (s *Source) FindOne(ctx context.Context, filterString, database, collection string, opts *options.FindOneOptionsBuilder) ([]any, error) {
var filter = bson.D{}
err := bson.UnmarshalExtJSON([]byte(filterString), false, &filter)
if err != nil {
@@ -233,7 +233,7 @@ func (s *Source) UpdateMany(ctx context.Context, filterString string, canonical
return nil, fmt.Errorf("unable to unmarshal update string: %w", err)
}
res, err := s.MongoClient().Database(database).Collection(collection).UpdateMany(ctx, filter, update, options.Update().SetUpsert(upsert))
res, err := s.MongoClient().Database(database).Collection(collection).UpdateMany(ctx, filter, update, options.UpdateMany().SetUpsert(upsert))
if err != nil {
return nil, fmt.Errorf("error updating collection: %w", err)
}
@@ -252,7 +252,7 @@ func (s *Source) UpdateOne(ctx context.Context, filterString string, canonical b
return nil, fmt.Errorf("unable to unmarshal update string: %w", err)
}
res, err := s.MongoClient().Database(database).Collection(collection).UpdateOne(ctx, filter, update, options.Update().SetUpsert(upsert))
res, err := s.MongoClient().Database(database).Collection(collection).UpdateOne(ctx, filter, update, options.UpdateOne().SetUpsert(upsert))
if err != nil {
return nil, fmt.Errorf("error updating collection: %w", err)
}
@@ -266,7 +266,7 @@ func (s *Source) DeleteMany(ctx context.Context, filterString, database, collect
return nil, err
}
res, err := s.MongoClient().Database(database).Collection(collection).DeleteMany(ctx, filter, options.Delete())
res, err := s.MongoClient().Database(database).Collection(collection).DeleteMany(ctx, filter, options.DeleteMany())
if err != nil {
return nil, err
}
@@ -284,7 +284,7 @@ func (s *Source) DeleteOne(ctx context.Context, filterString, database, collecti
return nil, err
}
res, err := s.MongoClient().Database(database).Collection(collection).DeleteOne(ctx, filter, options.Delete())
res, err := s.MongoClient().Database(database).Collection(collection).DeleteOne(ctx, filter, options.DeleteOne())
if err != nil {
return nil, err
}
@@ -303,7 +303,7 @@ func initMongoDBClient(ctx context.Context, tracer trace.Tracer, name, uri strin
// Create a new MongoDB client
clientOpts := options.Client().ApplyURI(uri).SetAppName(userAgent)
client, err := mongo.Connect(ctx, clientOpts)
client, err := mongo.Connect(clientOpts)
if err != nil {
return nil, fmt.Errorf("unable to create MongoDB client: %w", err)
}

View File

@@ -162,11 +162,6 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return source.CreateCluster(ctx, project, location, network, user, password, clusterID, string(accessToken))
}
// ParseParams parses the parameters for the tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.AllParams, data, claims)
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.AllParams, paramValues, embeddingModelsMap, nil)
}

View File

@@ -168,11 +168,6 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return source.CreateInstance(ctx, project, location, cluster, instanceID, instanceType, displayName, nodeCount, string(accessToken))
}
// ParseParams parses the parameters for the tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.AllParams, data, claims)
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.AllParams, paramValues, embeddingModelsMap, nil)
}

View File

@@ -173,11 +173,6 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return source.CreateUser(ctx, userType, password, roles, string(accessToken), project, location, cluster, userID)
}
// ParseParams parses the parameters for the tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.AllParams, data, claims)
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.AllParams, paramValues, embeddingModelsMap, nil)
}

View File

@@ -144,11 +144,6 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return source.GetCluster(ctx, project, location, cluster, string(accessToken))
}
// ParseParams parses the parameters for the tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.AllParams, data, claims)
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.AllParams, paramValues, embeddingModelsMap, nil)
}

View File

@@ -148,11 +148,6 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return source.GetInstance(ctx, project, location, cluster, instance, string(accessToken))
}
// ParseParams parses the parameters for the tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.AllParams, data, claims)
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.AllParams, paramValues, embeddingModelsMap, nil)
}

View File

@@ -148,11 +148,6 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return source.GetUsers(ctx, project, location, cluster, user, string(accessToken))
}
// ParseParams parses the parameters for the tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.AllParams, data, claims)
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.AllParams, paramValues, embeddingModelsMap, nil)
}

View File

@@ -138,11 +138,6 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return source.ListCluster(ctx, project, location, string(accessToken))
}
// ParseParams parses the parameters for the tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.AllParams, data, claims)
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.AllParams, paramValues, embeddingModelsMap, nil)
}

View File

@@ -143,11 +143,6 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
return source.ListInstance(ctx, project, location, cluster, string(accessToken))
}
// ParseParams parses the parameters for the tool.
func (t Tool) ParseParams(data map[string]any, claims map[string]map[string]any) (parameters.ParamValues, error) {
return parameters.ParseParams(t.AllParams, data, claims)
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
return parameters.EmbedParams(ctx, t.AllParams, paramValues, embeddingModelsMap, nil)
}

Some files were not shown because too many files have changed in this diff Show More