solace-agent-mesh 1.3.0__py3-none-any.whl → 1.3.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- solace_agent_mesh/agent/adk/setup.py +141 -34
- solace_agent_mesh/agent/sac/app.py +1 -2
- solace_agent_mesh/agent/tools/__init__.py +1 -0
- solace_agent_mesh/agent/tools/dynamic_tool.py +362 -0
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.d97b8e94.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/483cef9a.4e972867.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55f47984.cf3781c4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/664b740a.1b744a32.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/75384d09.c193a8f0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9a09e75d.d6607c56.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/aba87c2f.071e2d94.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ae0e903d.4d8dda10.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c835a94d.146e3186.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.7334119c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.1c79039d.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.858117b7.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +29 -0
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +25 -0
- solace_agent_mesh/assets/docs/docs/documentation/{migration-guides/a2a-upgrade-to-0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html → Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html } +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/{migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html → Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html } +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +19 -27
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +5 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +63 -0
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/lunr-index-1757531604543.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1757531604543.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/assets/docs/sitemap.xml +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-vY5eu2lI.js → authCallback-CAX9u8a7.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-BeBkzgWW.js → client-DXU9SPI5.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{main-Bjys1KQs.js → main-C1k9E0aC.js} +26 -26
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CE0AeXyK.js → vendor-B0BEKoAR.js} +69 -74
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
- solace_agent_mesh/gateway/http_sse/dependencies.py +6 -6
- solace_agent_mesh/gateway/http_sse/main.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/{agents.py → agent_cards.py} +7 -7
- solace_agent_mesh/gateway/http_sse/services/{agent_service.py → agent_card_service.py} +19 -19
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.1.dist-info}/METADATA +1 -1
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.1.dist-info}/RECORD +77 -70
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.8ccb9901.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/55f47984.c484bf96.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/75384d09.bf78fbdb.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/aba87c2f.76376d7c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.08d30374.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1757433031159.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1757433031159.json +0 -1
- /solace_agent_mesh/assets/docs/assets/js/{main.08d30374.js.LICENSE.txt → main.1c79039d.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.1.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.1.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="generator" content="Docusaurus v3.8.1">
|
|
6
6
|
<title data-rh="true">Page Not Found | Solace Agent Mesh</title><meta data-rh="true" name="viewport" content="width=device-width,initial-scale=1"><meta data-rh="true" name="twitter:card" content="summary_large_image"><meta data-rh="true" property="og:image" content="https://solacelabs.github.io/solace-agent-mesh/img/logo.png"><meta data-rh="true" name="twitter:image" content="https://solacelabs.github.io/solace-agent-mesh/img/logo.png"><meta data-rh="true" property="og:url" content="https://solacelabs.github.io/solace-agent-mesh/404.html"><meta data-rh="true" property="og:locale" content="en"><meta data-rh="true" name="docusaurus_locale" content="en"><meta data-rh="true" name="docusaurus_tag" content="default"><meta data-rh="true" name="docsearch:language" content="en"><meta data-rh="true" name="docsearch:docusaurus_tag" content="default"><meta data-rh="true" property="og:title" content="Page Not Found | Solace Agent Mesh"><link data-rh="true" rel="icon" href="/solace-agent-mesh/img/logo.png"><link data-rh="true" rel="canonical" href="https://solacelabs.github.io/solace-agent-mesh/404.html"><link data-rh="true" rel="alternate" href="https://solacelabs.github.io/solace-agent-mesh/404.html" hreflang="en"><link data-rh="true" rel="alternate" href="https://solacelabs.github.io/solace-agent-mesh/404.html" hreflang="x-default"><link rel="stylesheet" href="/solace-agent-mesh/assets/css/styles.906a1503.css">
|
|
7
|
-
<script src="/solace-agent-mesh/assets/js/runtime~main.
|
|
8
|
-
<script src="/solace-agent-mesh/assets/js/main.
|
|
7
|
+
<script src="/solace-agent-mesh/assets/js/runtime~main.858117b7.js" defer="defer"></script>
|
|
8
|
+
<script src="/solace-agent-mesh/assets/js/main.1c79039d.js" defer="defer"></script>
|
|
9
9
|
</head>
|
|
10
10
|
<body class="navigation-with-keyboard">
|
|
11
11
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;"><defs>
|
|
12
12
|
<symbol id="theme-svg-external-link" viewBox="0 0 24 24"><path fill="currentColor" d="M21 13v10h-21v-19h12v2h-10v15h17v-8h2zm3-12h-10.988l4.035 4-6.977 7.07 2.828 2.828 6.977-7.07 4.125 4.172v-11z"/></symbol>
|
|
13
13
|
</defs></svg>
|
|
14
|
-
<script>!function(){var t=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();document.documentElement.setAttribute("data-theme",t||(window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light")),document.documentElement.setAttribute("data-theme-choice",t||"system")}(),function(){try{const c=new URLSearchParams(window.location.search).entries();for(var[t,e]of c)if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id="__docusaurus"><link rel="preload" as="image" href="/solace-agent-mesh/img/logo.png"><link rel="preload" as="image" href="/solace-agent-mesh/img/solace-logo.png"><div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#__docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="theme-layout-navbar navbar navbar--fixed-top"><div class="navbar__inner"><div class="theme-layout-navbar-left navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/solace-agent-mesh/docs/documentation/getting-started/introduction"><div class="navbar__logo"><img src="/solace-agent-mesh/img/logo.png" alt="Solace Agent Mesh Logo" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="/solace-agent-mesh/img/logo.png" alt="Solace Agent Mesh Logo" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">Solace Agent Mesh</b></a><a class="navbar__item navbar__link" href="/solace-agent-mesh/docs/documentation/getting-started/introduction">Documentation</a></div><div class="theme-layout-navbar-right navbar__items navbar__items--right"><a href="https://github.com/SolaceLabs/solace-agent-mesh/" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">GitHub<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type="button" disabled="" title="system mode" aria-label="Switch between dark and light mode (currently system mode)"><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP lightToggleIcon_pyhR"><path fill="currentColor" d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP darkToggleIcon_wfgR"><path fill="currentColor" d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP systemToggleIcon_QzmC"><path fill="currentColor" d="m12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9zm4.95-13.95c1.313 1.313 2.05 3.093 2.05 4.95s-0.738 3.637-2.05 4.95c-1.313 1.313-3.093 2.05-4.95 2.05v-14c1.857 0 3.637 0.737 4.95 2.05z"></path></svg></button></div><div class="navbarSearchContainer_Bca1"><div class="navbar__search"><span aria-label="expand searchbar" role="button" class="search-icon" tabindex="0"></span><input id="search_input_react" type="search" placeholder="Loading..." aria-label="Search" class="navbar__search-input search-bar" disabled=""></div></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="__docusaurus_skipToContent_fallback" class="theme-layout-main main-wrapper mainWrapper_z2l0"><main class="container margin-vert--xl"><div class="row"><div class="col col--6 col--offset-3"><h1 class="hero__title">Page Not Found</h1><p>We could not find what you were looking for.</p><p>Please contact the owner of the site that linked you to the original URL and let them know their link is broken.</p></div></div></main></div><footer class="theme-layout-footer footer footer--dark"><div class="container container-fluid"><div class="row footer__links"><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Solace Agent Mesh</div><ul class="footer__items clean-list"><li class="footer__item"><a class="footer__link-item" href="/solace-agent-mesh/docs/documentation/getting-started/introduction">Documentation</a></li><li class="footer__item"><a href="https://github.com/SolaceLabs/solace-agent-mesh/" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://github.com/SolaceLabs/solace-agent-mesh-core-plugins/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Official Plugins<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Company</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://solace.com/products/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Products<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://solace.com/contact/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Contact<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://solace.com/support/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Support<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://solace.com/legal/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Privacy and Legal<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Community</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://www.linkedin.com/company/solacedotcom/" target="_blank" rel="noopener noreferrer" class="footer__link-item">LinkedIn<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://github.com/SolaceLabs" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://www.youtube.com/SolaceSystems" target="_blank" rel="noopener noreferrer" class="footer__link-item">YouTube<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://twitter.com/solacedotcom" target="_blank" rel="noopener noreferrer" class="footer__link-item">X<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div></div><div class="footer__bottom text--center"><div class="margin-bottom--sm"><img src="/solace-agent-mesh/img/solace-logo.png" alt="Solace Logo" class="footer__logo themedComponent_mlkZ themedComponent--light_NVdE" width="10%" height="10%"><img src="/solace-agent-mesh/img/solace-logo.png" alt="Solace Logo" class="footer__logo themedComponent_mlkZ themedComponent--dark_xIcU" width="10%" height="10%"></div><div class="footer__copyright">Solace Agent Mesh. Copyright © 2025 Solace. Version: 1.3.
|
|
14
|
+
<script>!function(){var t=function(){try{return new URLSearchParams(window.location.search).get("docusaurus-theme")}catch(t){}}()||function(){try{return window.localStorage.getItem("theme")}catch(t){}}();document.documentElement.setAttribute("data-theme",t||(window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light")),document.documentElement.setAttribute("data-theme-choice",t||"system")}(),function(){try{const c=new URLSearchParams(window.location.search).entries();for(var[t,e]of c)if(t.startsWith("docusaurus-data-")){var a=t.replace("docusaurus-data-","data-");document.documentElement.setAttribute(a,e)}}catch(t){}}()</script><div id="__docusaurus"><link rel="preload" as="image" href="/solace-agent-mesh/img/logo.png"><link rel="preload" as="image" href="/solace-agent-mesh/img/solace-logo.png"><div role="region" aria-label="Skip to main content"><a class="skipToContent_fXgn" href="#__docusaurus_skipToContent_fallback">Skip to main content</a></div><nav aria-label="Main" class="theme-layout-navbar navbar navbar--fixed-top"><div class="navbar__inner"><div class="theme-layout-navbar-left navbar__items"><button aria-label="Toggle navigation bar" aria-expanded="false" class="navbar__toggle clean-btn" type="button"><svg width="30" height="30" viewBox="0 0 30 30" aria-hidden="true"><path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M4 7h22M4 15h22M4 23h22"></path></svg></button><a class="navbar__brand" href="/solace-agent-mesh/docs/documentation/getting-started/introduction"><div class="navbar__logo"><img src="/solace-agent-mesh/img/logo.png" alt="Solace Agent Mesh Logo" class="themedComponent_mlkZ themedComponent--light_NVdE"><img src="/solace-agent-mesh/img/logo.png" alt="Solace Agent Mesh Logo" class="themedComponent_mlkZ themedComponent--dark_xIcU"></div><b class="navbar__title text--truncate">Solace Agent Mesh</b></a><a class="navbar__item navbar__link" href="/solace-agent-mesh/docs/documentation/getting-started/introduction">Documentation</a></div><div class="theme-layout-navbar-right navbar__items navbar__items--right"><a href="https://github.com/SolaceLabs/solace-agent-mesh/" target="_blank" rel="noopener noreferrer" class="navbar__item navbar__link">GitHub<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a><div class="toggle_vylO colorModeToggle_DEke"><button class="clean-btn toggleButton_gllP toggleButtonDisabled_aARS" type="button" disabled="" title="system mode" aria-label="Switch between dark and light mode (currently system mode)"><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP lightToggleIcon_pyhR"><path fill="currentColor" d="M12,9c1.65,0,3,1.35,3,3s-1.35,3-3,3s-3-1.35-3-3S10.35,9,12,9 M12,7c-2.76,0-5,2.24-5,5s2.24,5,5,5s5-2.24,5-5 S14.76,7,12,7L12,7z M2,13l2,0c0.55,0,1-0.45,1-1s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S1.45,13,2,13z M20,13l2,0c0.55,0,1-0.45,1-1 s-0.45-1-1-1l-2,0c-0.55,0-1,0.45-1,1S19.45,13,20,13z M11,2v2c0,0.55,0.45,1,1,1s1-0.45,1-1V2c0-0.55-0.45-1-1-1S11,1.45,11,2z M11,20v2c0,0.55,0.45,1,1,1s1-0.45,1-1v-2c0-0.55-0.45-1-1-1C11.45,19,11,19.45,11,20z M5.99,4.58c-0.39-0.39-1.03-0.39-1.41,0 c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0s0.39-1.03,0-1.41L5.99,4.58z M18.36,16.95 c-0.39-0.39-1.03-0.39-1.41,0c-0.39,0.39-0.39,1.03,0,1.41l1.06,1.06c0.39,0.39,1.03,0.39,1.41,0c0.39-0.39,0.39-1.03,0-1.41 L18.36,16.95z M19.42,5.99c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06c-0.39,0.39-0.39,1.03,0,1.41 s1.03,0.39,1.41,0L19.42,5.99z M7.05,18.36c0.39-0.39,0.39-1.03,0-1.41c-0.39-0.39-1.03-0.39-1.41,0l-1.06,1.06 c-0.39,0.39-0.39,1.03,0,1.41s1.03,0.39,1.41,0L7.05,18.36z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP darkToggleIcon_wfgR"><path fill="currentColor" d="M9.37,5.51C9.19,6.15,9.1,6.82,9.1,7.5c0,4.08,3.32,7.4,7.4,7.4c0.68,0,1.35-0.09,1.99-0.27C17.45,17.19,14.93,19,12,19 c-3.86,0-7-3.14-7-7C5,9.07,6.81,6.55,9.37,5.51z M12,3c-4.97,0-9,4.03-9,9s4.03,9,9,9s9-4.03,9-9c0-0.46-0.04-0.92-0.1-1.36 c-0.98,1.37-2.58,2.26-4.4,2.26c-2.98,0-5.4-2.42-5.4-5.4c0-1.81,0.89-3.42,2.26-4.4C12.92,3.04,12.46,3,12,3L12,3z"></path></svg><svg viewBox="0 0 24 24" width="24" height="24" aria-hidden="true" class="toggleIcon_g3eP systemToggleIcon_QzmC"><path fill="currentColor" d="m12 21c4.971 0 9-4.029 9-9s-4.029-9-9-9-9 4.029-9 9 4.029 9 9 9zm4.95-13.95c1.313 1.313 2.05 3.093 2.05 4.95s-0.738 3.637-2.05 4.95c-1.313 1.313-3.093 2.05-4.95 2.05v-14c1.857 0 3.637 0.737 4.95 2.05z"></path></svg></button></div><div class="navbarSearchContainer_Bca1"><div class="navbar__search"><span aria-label="expand searchbar" role="button" class="search-icon" tabindex="0"></span><input id="search_input_react" type="search" placeholder="Loading..." aria-label="Search" class="navbar__search-input search-bar" disabled=""></div></div></div></div><div role="presentation" class="navbar-sidebar__backdrop"></div></nav><div id="__docusaurus_skipToContent_fallback" class="theme-layout-main main-wrapper mainWrapper_z2l0"><main class="container margin-vert--xl"><div class="row"><div class="col col--6 col--offset-3"><h1 class="hero__title">Page Not Found</h1><p>We could not find what you were looking for.</p><p>Please contact the owner of the site that linked you to the original URL and let them know their link is broken.</p></div></div></main></div><footer class="theme-layout-footer footer footer--dark"><div class="container container-fluid"><div class="row footer__links"><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Solace Agent Mesh</div><ul class="footer__items clean-list"><li class="footer__item"><a class="footer__link-item" href="/solace-agent-mesh/docs/documentation/getting-started/introduction">Documentation</a></li><li class="footer__item"><a href="https://github.com/SolaceLabs/solace-agent-mesh/" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://github.com/SolaceLabs/solace-agent-mesh-core-plugins/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Official Plugins<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Company</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://solace.com/products/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Products<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://solace.com/contact/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Contact<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://solace.com/support/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Support<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://solace.com/legal/" target="_blank" rel="noopener noreferrer" class="footer__link-item">Privacy and Legal<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div><div class="theme-layout-footer-column col footer__col"><div class="footer__title">Community</div><ul class="footer__items clean-list"><li class="footer__item"><a href="https://www.linkedin.com/company/solacedotcom/" target="_blank" rel="noopener noreferrer" class="footer__link-item">LinkedIn<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://github.com/SolaceLabs" target="_blank" rel="noopener noreferrer" class="footer__link-item">GitHub<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://www.youtube.com/SolaceSystems" target="_blank" rel="noopener noreferrer" class="footer__link-item">YouTube<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li><li class="footer__item"><a href="https://twitter.com/solacedotcom" target="_blank" rel="noopener noreferrer" class="footer__link-item">X<svg width="13.5" height="13.5" aria-hidden="true" class="iconExternalLink_nPIU"><use href="#theme-svg-external-link"></use></svg></a></li></ul></div></div><div class="footer__bottom text--center"><div class="margin-bottom--sm"><img src="/solace-agent-mesh/img/solace-logo.png" alt="Solace Logo" class="footer__logo themedComponent_mlkZ themedComponent--light_NVdE" width="10%" height="10%"><img src="/solace-agent-mesh/img/solace-logo.png" alt="Solace Logo" class="footer__logo themedComponent_mlkZ themedComponent--dark_xIcU" width="10%" height="10%"></div><div class="footer__copyright">Solace Agent Mesh. Copyright © 2025 Solace. Version: 1.3.1</div></div></div></footer></div>
|
|
15
15
|
</body>
|
|
16
16
|
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunksolace_agenitc_mesh_docs=self.webpackChunksolace_agenitc_mesh_docs||[]).push([[3974],{6145:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>a,contentTitle:()=>r,default:()=>g,frontMatter:()=>l,metadata:()=>o,toc:()=>c});const o=JSON.parse('{"id":"documentation/user-guide/create-agents","title":"Create Agents","description":"Introduction","source":"@site/docs/documentation/user-guide/create-agents.md","sourceDirName":"documentation/user-guide","slug":"/documentation/user-guide/create-agents","permalink":"/solace-agent-mesh/docs/documentation/user-guide/create-agents","draft":false,"unlisted":false,"editUrl":"https://github.com/SolaceLabs/solace-agent-mesh/edit/main/docs/docs/documentation/user-guide/create-agents.md","tags":[],"version":"current","sidebarPosition":30,"frontMatter":{"title":"Create Agents","sidebar_position":30},"sidebar":"docSidebar","previous":{"title":"Structure","permalink":"/solace-agent-mesh/docs/documentation/user-guide/structure"},"next":{"title":"Creating Python Tools","permalink":"/solace-agent-mesh/docs/documentation/user-guide/creating-python-tools"}}');var i=t(4848),s=t(8453);const l={title:"Create Agents",sidebar_position:30},r="Creating Agents",a={},c=[{value:"Introduction",id:"introduction",level:2},{value:"Quick Start: Creating Your First Agent",id:"quick-start-creating-your-first-agent",level:2},{value:"CLI Options",id:"cli-options",level:3},{value:"Core Concepts",id:"core-concepts",level:2},{value:"Tools",id:"tools",level:3},{value:"Configuration File",id:"configuration-file",level:3},{value:"Tool Configuration",id:"tool-configuration",level:3},{value:"ToolContext",id:"toolcontext",level:3},{value:"Lifecycle Functions",id:"lifecycle-functions",level:3},{value:"Step-by-Step Guide",id:"step-by-step-guide",level:2},{value:"Step 1: Initialize your Agent",id:"step-1-initialize-your-agent",level:3},{value:"Step 2: The Tool Function",id:"step-2-the-tool-function",level:3},{value:"Step 3: The Agent Configuration",id:"step-3-the-agent-configuration",level:3},{value:"Step 4: The Lifecycle Function",id:"step-4-the-lifecycle-function",level:3},{value:"Advanced Concepts",id:"advanced-concepts",level:2},{value:"Working with Artifacts",id:"working-with-artifacts",level:3},{value:"Using Multiple Tool Configurations",id:"using-multiple-tool-configurations",level:3},{value:"Running Your Agent",id:"running-your-agent",level:2},{value:"Architecture Overview",id:"architecture-overview",level:2},{value:"Best Practices",id:"best-practices",level:2},{value:"Tool Design",id:"tool-design",level:3},{value:"Configuration",id:"configuration",level:3},{value:"Testing",id:"testing",level:3}];function d(e){const n={a:"a",admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",mermaid:"mermaid",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"creating-agents",children:"Creating Agents"})}),"\n",(0,i.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["For a more comprehensive tutorial example, see the ",(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/tutorials/custom-agent",children:"Build Your Own Agent"})," guide.\nThis page provides an in-depth theoretical overview of creating agents in Solace Agent Mesh."]})}),"\n",(0,i.jsxs)(n.p,{children:["Solace Agent Mesh (SAM) is a powerful platform that enables you to create intelligent agents that can communicate with each other and perform complex tasks. At its core, SAM uses a ",(0,i.jsx)(n.strong,{children:"tool-based architecture"})," where LLM-powered agents are equipped with specific capabilities (tools) that they can use to accomplish user requests."]}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsxs)(n.strong,{children:["Before continuing with this tutorial, make sure you are familiar with the basic ",(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/concepts/agents",children:"agent concept"}),"."]})}),"\n",(0,i.jsx)(n.p,{children:"This tutorial guides you through creating your first SAM agent from scratch. You will learn how to:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Define tools as Python functions"}),"\n",(0,i.jsx)(n.li,{children:"Configure an agent using YAML"}),"\n",(0,i.jsx)(n.li,{children:"Set up agent lifecycle functions"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:'By the end of this tutorial, you should have a working "Hello World" agent that demonstrates the fundamental concepts of SAM agent development.'}),"\n",(0,i.jsx)(n.h2,{id:"quick-start-creating-your-first-agent",children:"Quick Start: Creating Your First Agent"}),"\n",(0,i.jsxs)(n.p,{children:["You can create an agent directly using the SAM CLI ",(0,i.jsx)(n.code,{children:"sam add agent"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam add agent my-first-agent\n"})}),"\n",(0,i.jsx)(n.p,{children:"This command:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Launches an interactive setup (or use ",(0,i.jsx)(n.code,{children:"--gui"})," for browser-based configuration)"]}),"\n",(0,i.jsx)(n.li,{children:"Generates the necessary files and configuration"}),"\n",(0,i.jsx)(n.li,{children:"Sets up the basic agent structure"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"Note that create agent as plugin is preferred over create agent directly."}),"\n",(0,i.jsx)(n.h3,{id:"cli-options",children:"CLI Options"}),"\n",(0,i.jsx)(n.p,{children:"You can customize the agent creation with provided CLI options."}),"\n",(0,i.jsx)(n.p,{children:"For a complete list of options, run:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam add agent --help\n"})}),"\n",(0,i.jsx)(n.h2,{id:"core-concepts",children:"Core Concepts"}),"\n",(0,i.jsx)(n.p,{children:"Before diving into the implementation, it is important to understand the key concepts that make SAM agents work:"}),"\n",(0,i.jsx)(n.h3,{id:"tools",children:"Tools"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Tools are the fundamental building blocks of SAM agents."})," Each tool is implemented as a Python function that performs a specific task. Tools can:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Process text and data"}),"\n",(0,i.jsx)(n.li,{children:"Interact with external APIs"}),"\n",(0,i.jsx)(n.li,{children:"Create and manipulate files"}),"\n",(0,i.jsx)(n.li,{children:"Communicate with other agents"}),"\n",(0,i.jsx)(n.li,{children:"Access databases and services"}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"The LLM (Large Language Model) orchestrating your agent decides which tools to use based on the user's request and the tool descriptions you provide."}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["Solace Agent Mesh provides a set of ",(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/user-guide/builtin-tools/",children:"built-in tools"})," plus support for ",(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/tutorials/mcp-integration",children:"model context protocol (MCP)"})," servers, which can be configured in the tools list of your agent configuration."]})}),"\n",(0,i.jsx)(n.h3,{id:"configuration-file",children:"Configuration File"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"config.yaml"})," (for plugin template) or ",(0,i.jsx)(n.code,{children:"agent-name.yaml"})," (for agent instances) file is the blueprint of your agent. It defines:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Agent identity"}),": Name, description, and capabilities"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Model configuration"}),": Which LLM to use"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Tools list"}),": Defines which tools the agent can access and how they're configured"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Lifecycle functions"}),": Setup and cleanup procedures"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Framework services"}),": Session management, artifact storage, and so on."]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/concepts/agents#agent-card",children:"Agent card"})}),": Metadata describing the agent capabilities, skills and its visibility in the system"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"tool-configuration",children:"Tool Configuration"}),"\n",(0,i.jsxs)(n.p,{children:["Within the ",(0,i.jsx)(n.code,{children:"tools"})," list in your YAML config, each tool can have its own ",(0,i.jsx)(n.code,{children:"tool_config"})," section. This allows you to:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Configure the same tool function for different purposes"}),"\n",(0,i.jsx)(n.li,{children:"Pass specific parameters to tool instances"}),"\n",(0,i.jsx)(n.li,{children:"Customize tool behavior per agent"}),"\n"]}),"\n",(0,i.jsxs)(n.admonition,{type:"info",children:[(0,i.jsxs)(n.p,{children:['For tools of type "python", you can also use the ',(0,i.jsx)(n.code,{children:"tool_name"})," and ",(0,i.jsx)(n.code,{children:"tool_description"})," to overwrite the function name and description in the tool docstring."]}),(0,i.jsx)(n.p,{children:"This is useful when using a generic tool function for multiple purposes, allowing you to provide a more descriptive name and description for each instance."})]}),"\n",(0,i.jsx)(n.h3,{id:"toolcontext",children:"ToolContext"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"ToolContext"})," object (passed as one of the arguments to your tool function) provides your tools with access to SAM core services:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Logging"}),": Structured logging for debugging and monitoring"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Artifact Service"}),": File storage and retrieval"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Session Information"}),": Current user and session context"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Agent State"}),": Shared data between tool calls"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"lifecycle-functions",children:"Lifecycle Functions"}),"\n",(0,i.jsx)(n.p,{children:"Lifecycle functions allow you to manage resources that should persist for the agent's lifetime:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"agent_init_function"})}),": Runs when the agent starts (for example, database connections)"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"agent_cleanup_function"})}),": Runs when the agent shuts down (for example, closing connections)"]}),"\n"]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsx)(n.p,{children:"Lifecycle functions are optional but recommended for managing resources effectively."})}),"\n",(0,i.jsx)(n.h2,{id:"step-by-step-guide",children:"Step-by-Step Guide"}),"\n",(0,i.jsx)(n.p,{children:"Create a simple agent that can greet users and demonstrate the core concepts."}),"\n",(0,i.jsxs)(n.p,{children:["You can create an agent either by using the ",(0,i.jsx)(n.code,{children:"sam add agent"})," command or by creating a new plugin of type agent, ",(0,i.jsx)(n.code,{children:"sam plugin create my-hello-agent --type agent"}),"."]}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["For information and recommendations about these options, see ",(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/concepts/plugins#agent-or-plugin-which-to-use",children:(0,i.jsx)(n.code,{children:"Agent or Plugin: Which To use?"})}),"."]})}),"\n",(0,i.jsx)(n.h3,{id:"step-1-initialize-your-agent",children:"Step 1: Initialize your Agent"}),"\n",(0,i.jsxs)(n.p,{children:["In this tutorial, you create a new agent by creating a new plugin of type agent.\nFor an example of custom agents, see ",(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/tutorials/custom-agent",children:"Build Your Own Agent"})," guide."]}),"\n",(0,i.jsx)(n.p,{children:"Although the directory structure for plugins is slightly different than the one for agents, both require a YAML configuration file, and a python module with the tools and lifecycle functions you want."}),"\n",(0,i.jsx)(n.p,{children:"To create a new agent plugin, run the following command:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam plugin create my-hello-agent --type agent\n"})}),"\n",(0,i.jsx)(n.p,{children:"And follow the prompts to set up your agent. The prompts create a new directory structure for your agent."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"my-hello-agent/\n\u251c\u2500\u2500 src/\n\u2502 \u2514\u2500\u2500 my_hello_agent/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 tools.py\n\u2502 \u2514\u2500\u2500 lifecycle.py # This file won't be automatically created\n\u251c\u2500\u2500 config.yaml\n\u251c\u2500\u2500 pyproject.toml\n\u2514\u2500\u2500 README.md\n"})}),"\n",(0,i.jsx)(n.mermaid,{value:"graph TD\n A[my-hello-agent/] --\x3e B[src/]\n A --\x3e C[config.yaml]\n A --\x3e D[pyproject.toml]\n A --\x3e E[README.md]\n \n B --\x3e F[my_hello_agent/]\n F --\x3e G[__init__.py]\n F --\x3e H[tools.py]\n F --\x3e I[lifecycle.py]\n \n style C fill:#b60000,stroke:#333,stroke-width:2px\n style H fill:#b60000,stroke:#333,stroke-width:2px\n style I fill:#007000,stroke:#333,stroke-width:2px"}),"\n",(0,i.jsx)(n.h3,{id:"step-2-the-tool-function",children:"Step 2: The Tool Function"}),"\n",(0,i.jsx)(n.p,{children:"Create your first tool function:\nThe following arguments are provided by the framework:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"tool_context: SAM framework context"}),"\n",(0,i.jsx)(n.li,{children:"tool_config: Tool-specific configuration (from config.yaml)"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["For a complete guide on creating python tools, see our ",(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/user-guide/creating-python-tools",children:"Creating Python Tools"})})," documentation."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'# src/my_hello_agent/tools.py\n"""\nTools for the Hello World agent.\n"""\n\nfrom typing import Any, Dict, Optional\nfrom google.adk.tools import ToolContext\nfrom solace_ai_connector.common.log import log\n\n\nasync def hello_tool(\n name: str,\n tool_context: Optional[ToolContext] = None,\n tool_config: Optional[Dict[str, Any]] = None\n) -> Dict[str, Any]:\n """\n Greets a user with a personalized message.\n \n Args:\n name: The name of the person to greet\n \n Returns:\n A dictionary with the greeting message\n """\n log_identifier = "[HelloTool]"\n log.info(f"{log_identifier} Greeting user: {name}")\n \n # Get configuration from tool_config\n greeting_prefix = "Hello"\n if tool_config:\n greeting_prefix = tool_config.get("greeting_prefix", "Hello")\n \n # Create the greeting message\n greeting_message = f"{greeting_prefix}, {name}! Welcome to Solace Agent Mesh!"\n \n log.info(f"{log_identifier} Generated greeting: {greeting_message}")\n \n return {\n "status": "success",\n "message": greeting_message,\n "greeted_name": name\n }\n\n\nasync def farewell_tool(\n name: str,\n tool_context: Optional[ToolContext] = None,\n tool_config: Optional[Dict[str, Any]] = None\n) -> Dict[str, Any]:\n """\n Says goodbye to a user.\n \n Args:\n name: The name of the person to say goodbye to\n \n Returns:\n A dictionary with the farewell message\n """\n log_identifier = "[FarewellTool]"\n log.info(f"{log_identifier} Saying goodbye to user: {name}")\n \n # Get configuration from tool_config\n farewell_prefix = "Goodbye"\n if tool_config:\n farewell_prefix = tool_config.get("farewell_prefix", "Goodbye")\n \n # Create the farewell message\n farewell_message = f"{farewell_prefix}, {name}! Thanks for using Solace Agent Mesh!"\n \n log.info(f"{log_identifier} Generated farewell: {farewell_message}")\n \n return {\n "status": "success",\n "message": farewell_message,\n "farewell_name": name\n }\n'})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Key Points:"})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Function Signature"}),": All tool functions should be ",(0,i.jsx)(n.code,{children:"async"})," and accept ",(0,i.jsx)(n.code,{children:"tool_context"})," and ",(0,i.jsx)(n.code,{children:"tool_config"})," parameters"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Return Format"}),": Return a dictionary with at least a ",(0,i.jsx)(n.code,{children:"status"})," field"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Logging"}),": Use the SAM logger for consistent logging"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Configuration"}),": Access tool-specific config via the ",(0,i.jsx)(n.code,{children:"tool_config"})," parameter"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"step-3-the-agent-configuration",children:"Step 3: The Agent Configuration"}),"\n",(0,i.jsx)(n.p,{children:"Create the main configuration file for your agent:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:'# ... (additional services and configurations)\n\napps:\n - name: my-hello-agent\n app_module: solace_agent_mesh.agent.sac.app \n broker:\n <<: *broker_connection\n \n # Agent-specific configuration\n app_config:\n # Basic agent identity\n namespace: ${NAMESPACE} \n supports_streaming: true \n agent_name: "HelloAgent"\n display_name: "Hello World Agent"\n \n # LLM model configuration\n model: *general_model \n \n # Agent instructions (system prompt)\n instruction: |\n You are a friendly Hello World agent. Your purpose is to greet users and \n demonstrate the capabilities of Solace Agent Mesh. You can:\n \n 1. Greet users with personalized messages using the hello_tool\n 2. Say goodbye to users using the farewell_tool\n \n Always be polite and helpful. When greeting someone, ask for their name \n if they haven\'t provided it.\n \n # Lifecycle functions\n agent_init_function:\n module: "my_hello_agent.lifecycle" # This should point to your lifecycle python module\n name: "initialize_hello_agent"\n base_path: .\n config:\n startup_message: "Hello Agent is starting up!"\n log_level: "INFO"\n \n agent_cleanup_function:\n module: "my_hello_agent.lifecycle"\n base_path: .\n name: "cleanup_hello_agent"\n \n # Tools configuration\n tools:\n # Hello tool with custom greeting\n - tool_type: python\n component_module: "my_hello_agent.tools"\n component_base_path: .\n function_name: "hello_tool"\n tool_name: "greet_user" # Renaming the tool, must use this name in the agent card\n tool_config:\n greeting_prefix: "Hello there"\n \n # Farewell tool with custom farewell\n - tool_type: python\n component_module: "my_hello_agent.tools"\n component_base_path: .\n function_name: "farewell_tool"\n tool_name: "say_goodbye"\n tool_config:\n farewell_prefix: "See you later"\n \n # Built-in artifact tools for file operations\n - tool_type: builtin-group\n group_name: "artifact_management"\n \n # Agent card (describes the agent\'s capabilities)\n agent_card:\n description: "A friendly Hello World agent that demonstrates SAM capabilities"\n defaultInputModes: ["text"]\n defaultOutputModes: ["text"]\n skills:\n - id: "greet_user"\n name: "Greet User"\n description: "Greets users with personalized messages"\n - id: "say_goodbye"\n name: "Say Goodbye"\n description: "Says goodbye to users"\n - id: "file_operations"\n name: "File Operations"\n description: "Create, read, and manage files and artifacts"\n \n # Session and artifact services\n session_service: *default_session_service\n artifact_service: *default_artifact_service\n# ... (additional services and configurations)\n'})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Key Sections Explained:"})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"namespace"})}),": Unique identifier for your agent in the mesh"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"model"})}),": LLM configuration (can be a string or detailed config)"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"instruction"})}),": The system prompt that defines your agent's behavior"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"tools"})}),": List of tools your agent can use, with their configurations"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:(0,i.jsx)(n.code,{children:"agent_card"})}),": Metadata describing your agent's capabilities"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"step-4-the-lifecycle-function",children:"Step 4: The Lifecycle Function"}),"\n",(0,i.jsx)(n.p,{children:"Lifecycle functions are completely optional but useful for managing resources. They run when the agent starts and stops."}),"\n",(0,i.jsx)(n.p,{children:"The lifecycle file is not automatically created, so you need to create it manually:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"touch src/my_hello_agent/lifecycle.py\n"})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'# src/my_hello_agent/lifecycle.py\n"""\nLifecycle functions for the Hello World agent.\n"""\n\nfrom typing import Any, Dict\nfrom pydantic import BaseModel, Field\nfrom solace_ai_connector.common.log import log\n\n\nclass HelloAgentInitConfig(BaseModel):\n """\n Configuration model for the Hello Agent initialization.\n """\n startup_message: str = Field(description="Message to log on startup")\n log_level: str = Field(default="INFO", description="Logging level for the agent")\n\n\ndef initialize_hello_agent(host_component: Any, init_config: HelloAgentInitConfig):\n """\n Initializes the Hello World agent.\n \n Args:\n host_component: The agent host component\n init_config: Validated initialization configuration\n """\n log_identifier = f"[{host_component.agent_name}:init]"\n log.info(f"{log_identifier} Starting Hello Agent initialization...")\n \n # Log the startup message from config\n log.info(f"{log_identifier} {init_config.startup_message}")\n \n # You could initialize shared resources here, such as:\n # - Database connections\n # - API clients\n # - Caches or shared data structures\n \n # Store any shared state in the agent\n host_component.set_agent_specific_state("initialized_at", "2024-01-01T00:00:00Z")\n host_component.set_agent_specific_state("greeting_count", 0)\n \n log.info(f"{log_identifier} Hello Agent initialization completed successfully")\n\n\ndef cleanup_hello_agent(host_component: Any):\n """\n Cleans up resources when the Hello World agent shuts down.\n \n Args:\n host_component: The agent host component\n """\n log_identifier = f"[{host_component.agent_name}:cleanup]"\n log.info(f"{log_identifier} Starting Hello Agent cleanup...")\n \n # Retrieve any shared state\n greeting_count = host_component.get_agent_specific_state("greeting_count", 0)\n log.info(f"{log_identifier} Agent processed {greeting_count} greetings during its lifetime")\n \n # Clean up resources here, such as:\n # - Closing database connections\n # - Shutting down background tasks\n # - Saving final state\n \n log.info(f"{log_identifier} Hello Agent cleanup completed")\n'})}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Key Points:"})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Pydantic Models"}),": Use Pydantic for configuration validation"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Shared State"}),": Store data that persists across tool calls"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Resource Management"}),": Initialize connections in ",(0,i.jsx)(n.code,{children:"init"}),", clean up in ",(0,i.jsx)(n.code,{children:"cleanup"})]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"advanced-concepts",children:"Advanced Concepts"}),"\n",(0,i.jsx)(n.h3,{id:"working-with-artifacts",children:"Working with Artifacts"}),"\n",(0,i.jsx)(n.p,{children:"You can enhance our hello tool to save greetings to a file using SAM's artifact service:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'\nfrom datetime import datetime\nfrom solace_agent_mesh.agent.utils.artifact_helpers import save_artifact_with_metadata\n\nasync def hello_tool_with_artifact(\n name: str,\n save_to_file: bool = False,\n tool_context: Optional[ToolContext] = None,\n tool_config: Optional[Dict[str, Any]] = None\n) -> Dict[str, Any]:\n """\n Greets a user and optionally saves the greeting to a file.\n """\n log_identifier = "[HelloToolWithArtifact]"\n \n # Generate greeting (same as before)\n greeting_prefix = tool_config.get("greeting_prefix", "Hello") if tool_config else "Hello"\n greeting_message = f"{greeting_prefix}, {name}! Welcome to Solace Agent Mesh!"\n \n result = {\n "status": "success",\n "message": greeting_message,\n "greeted_name": name\n }\n \n # Save to artifact if requested\n if save_to_file and tool_context:\n try:\n # Prepare content\n timestamp = datetime.now(timezone.utc)\n filename = f"greeting_{name}_{timestamp}.txt"\n content = f"Greeting: {greeting_message}\\nTimestamp: {timestamp}\\n"\n \n # Save artifact\n artifact_service = tool_context._invocation_context.artifact_service\n await save_artifact_with_metadata(\n artifact_service=artifact_service,\n app_name=tool_context._invocation_context.app_name,\n user_id=tool_context._invocation_context.user_id,\n session_id=tool_context._invocation_context.session.id,\n filename=filename,\n content_bytes=content.encode(\'utf-8\'),\n mime_type="application/json",\n metadata_dict={\n "description": "Greeting message",\n "source": "Greeting Agent",\n },\n timestamp=timestamp\n )\n \n result["artifact_saved"] = filename\n log.info(f"{log_identifier} Saved greeting to artifact: {filename}")\n \n except Exception as e:\n log.error(f"{log_identifier} Failed to save artifact: {e}")\n result["artifact_error"] = str(e)\n \n return result\n'})}),"\n",(0,i.jsx)(n.h3,{id:"using-multiple-tool-configurations",children:"Using Multiple Tool Configurations"}),"\n",(0,i.jsx)(n.p,{children:"You can configure the same tool function multiple times with different settings:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:'tools:\n # Formal greeting\n - tool_type: python\n component_module: "my_hello_agent.tools"\n function_name: "hello_tool"\n tool_name: "formal_greeting"\n tool_config:\n greeting_prefix: "Good day"\n \n # Casual greeting\n - tool_type: python\n component_module: "my_hello_agent.tools"\n function_name: "hello_tool"\n tool_name: "casual_greeting"\n tool_config:\n greeting_prefix: "Hey there"\n \n # Enthusiastic greeting\n - tool_type: python\n component_module: "my_hello_agent.tools"\n function_name: "hello_tool"\n tool_name: "enthusiastic_greeting"\n tool_config:\n greeting_prefix: "Hello and welcome"\n'})}),"\n",(0,i.jsx)(n.p,{children:"This gives your agent multiple greeting styles to choose from based on the context."}),"\n",(0,i.jsx)(n.h2,{id:"running-your-agent",children:"Running Your Agent"}),"\n",(0,i.jsx)(n.p,{children:"To run a plugin agent, you first need to package and install it as a plugin."}),"\n",(0,i.jsxs)(n.admonition,{title:"Quick Debug",type:"tip",children:[(0,i.jsxs)(n.p,{children:["For debugging or isolated development testing, you can run your agent from the ",(0,i.jsx)(n.code,{children:"src"})," directory directly using the SAM CLI."]}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"cd src\nsam run ../config.yaml\n"})}),(0,i.jsx)(n.p,{children:"Changing to the src directory allows the module path to be set correctly so that SAM can find your functions without your having to install them in your python environment as a plugin package."})]}),"\n",(0,i.jsxs)(n.p,{children:["To properly instantiate your plugin agent, first build the plugin.\nThe following command will produce a python wheel file under ",(0,i.jsx)(n.code,{children:"dist"})," directory:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam plugin build\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Check into ",(0,i.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/getting-started/quick-start#create-a-project",children:"your SAM project directory"}),", and add the plugin wheel with a given name:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"sam plugin add my-first-weather-agent --plugin PATH/TO/weather-agent/dist/weather-agent.whl\n"})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["Using the ",(0,i.jsx)(n.code,{children:"sam plugin add"})," command installs your plugin as a python dependency into your python environment.\nThis also means changing the source code without reinstalling the plugin will not reflect the changes."]})}),"\n",(0,i.jsx)(n.p,{children:"Now, you can run the complete SAM application along with your newly added agent:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"sam run\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Alternatively, only run the newly added agent using ",(0,i.jsx)(n.code,{children:"sam run configs/agents/my-first-weather-agent.yaml"})]}),"\n",(0,i.jsx)(n.h2,{id:"architecture-overview",children:"Architecture Overview"}),"\n",(0,i.jsx)(n.p,{children:"Here is how all the pieces fit together:"}),"\n",(0,i.jsx)(n.mermaid,{value:"graph TD\n subgraph Agent Configuration\n direction LR\n A[config.yaml] --\x3e|Defines| B(Agent Properties);\n A --\x3e|Lists & Configures| C(Tools);\n end\n\n subgraph Agent Host\n direction TB\n D[SAM Host] --\x3e|Loads| A;\n D --\x3e|Instantiates| E[Agent];\n E --\x3e|Initializes with| F[Lifecycle Functions];\n end\n\n subgraph Tool Implementation\n direction LR\n G[Python Module tools.py] --\x3e|Contains| H[Tool Functions];\n end\n\n subgraph Execution Flow\n direction TB\n I[User Request] --\x3e J[LLM Orchestrator];\n J --\x3e|Selects Tool| K{ADKToolWrapper};\n K --\x3e|Calls| H;\n H --\x3e|Accesses| L[ToolContext];\n H --\x3e|Uses| M[tool_config];\n H --\x3e|Returns Result| J;\n end\n\n C --\x3e|Wrapped by| K;\n\n style A fill:#b60000,stroke:#faa,stroke-width:2px\n style H fill:#b60000,stroke:#faa,stroke-width:2px\n style F fill:#007000,stroke:#faa,stroke-width:2px"}),"\n",(0,i.jsx)(n.h2,{id:"best-practices",children:"Best Practices"}),"\n",(0,i.jsx)(n.h3,{id:"tool-design",children:"Tool Design"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Single Responsibility"}),": Each tool should do one thing well"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Clear Documentation"}),": Write detailed docstrings for your tools"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Error Handling"}),": Always return structured error responses"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Logging"}),": Use consistent logging for debugging and monitoring"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"configuration",children:"Configuration"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Environment Variables"}),": Use environment variables for sensitive data"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Validation"}),": Use Pydantic models for configuration validation"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Documentation"}),": Comment your configuration files thoroughly"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"testing",children:"Testing"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Unit Tests"}),": Test your tool functions independently"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Integration Tests"}),": Test your agent with real SAM infrastructure"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Mock Dependencies"}),": Mock external services for reliable testing"]}),"\n"]})]})}function g(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>l,x:()=>r});var o=t(6540);const i={},s=o.createContext(i);function l(e){const n=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:l(e.components),o.createElement(s.Provider,{value:n},e.children)}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunksolace_agenitc_mesh_docs=self.webpackChunksolace_agenitc_mesh_docs||[]).push([[2274],{4700:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>x,frontMatter:()=>r,metadata:()=>o,toc:()=>l});const o=JSON.parse('{"id":"documentation/Enterprise/single-sign-on","title":"SSO","description":"How to enable SSO","source":"@site/docs/documentation/Enterprise/single-sign-on.md","sourceDirName":"documentation/Enterprise","slug":"/documentation/Enterprise/single-sign-on","permalink":"/solace-agent-mesh/docs/documentation/Enterprise/single-sign-on","draft":false,"unlisted":false,"editUrl":"https://github.com/SolaceLabs/solace-agent-mesh/edit/main/docs/docs/documentation/Enterprise/single-sign-on.md","tags":[],"version":"current","sidebarPosition":10,"frontMatter":{"title":"SSO","sidebar_position":10},"sidebar":"docSidebar","previous":{"title":"Installation","permalink":"/solace-agent-mesh/docs/documentation/Enterprise/installation"}}');var i=t(4848),s=t(8453);const r={title:"SSO",sidebar_position:10},a=void 0,c={},l=[{value:"How to enable SSO",id:"how-to-enable-sso",level:2},{value:"Running SAM Enterprise with SSO enabled",id:"running-sam-enterprise-with-sso-enabled",level:2}];function h(e){const n={a:"a",admonition:"admonition",code:"code",h2:"h2",p:"p",pre:"pre",strong:"strong",...(0,s.R)(),...e.components},{Details:t}=n;return t||function(e,n){throw new Error("Expected "+(n?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h2,{id:"how-to-enable-sso",children:"How to enable SSO"}),"\n",(0,i.jsx)(n.p,{children:"Before running the Docker container, create two configuration files for SSO in your Named Docker Volume. Use the content provided below for each file:"}),"\n",(0,i.jsxs)(t,{children:[(0,i.jsx)("summary",{children:"Configuration files for SSO"}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"oauth2_server.yaml"})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:'---\n# Example gateway configuration with OAuth2 service integration\n# This shows how to configure a gateway to use the OAuth2 authentication service\n\nlog:\n stdout_log_level: INFO\n log_file_level: DEBUG\n log_file: oauth_server.log\n\n!include shared_config.yaml\n\nshared_config:\n # OAuth2 service configuration\n - oauth2_config: &oauth2_config\n enabled: true\n config_file: "config/sso_vol/oauth2_config.yaml"\n host: ${OAUTH2_HOST, localhost}\n port: ${OAUTH2_PORT, 9000}\n ssl_cert: "" # Optional: path to SSL certificate\n ssl_key: "" # Optional: path to SSL private key\n\nflows:\n # Initialize OAuth2 service\n - name: oauth2_service\n components:\n - component_name: oauth2_auth_service\n component_module: src.components.oauth2_component\n component_config:\n <<: *oauth2_config\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"oauth2_config.yaml"})}),(0,i.jsx)(n.p,{children:"In the oauth2_config.yaml file, uncomment the authentication provider you want to use.\nNote that the Azure provider is configured as the default option."}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:'---\n# OAuth2 Service Configuration\n# This file configures the OAuth2 authentication service that supports multiple providers\n# All providers now use the unified OIDC approach with automatic endpoint discovery\n\n# Enable or disable the OAuth2 service\nenabled: ${OAUTH2_ENABLED:false}\n\n# Development mode - enables insecure transport and relaxed token scope for local development\n# Set OAUTH2_DEV_MODE=true for local development (NEVER use in production!)\ndevelopment_mode: ${OAUTH2_DEV_MODE:false}\n\n# OAuth2 providers configuration\n# All providers now use the unified OIDCProvider with automatic endpoint discovery\nproviders:\n # Google OAuth2 provider\n # google:\n # # OIDC issuer URL - endpoints will be discovered automatically\n # issuer: "https://accounts.google.com"\n # client_id: ${GOOGLE_CLIENT_ID}\n # client_secret: ${GOOGLE_CLIENT_SECRET}\n # redirect_uri: ${GOOGLE_REDIRECT_URI:http://localhost:8080/callback}\n # scope: "openid email profile"\n\n # Azure/Microsoft OAuth2 provider\n azure:\n # Azure OIDC issuer URL includes tenant ID\n issuer: https://login.microsoftonline.com/${AZURE_TENANT_ID}/v2.0\n client_id: ${AZURE_CLIENT_ID}\n client_secret: ${AZURE_CLIENT_SECRET}\n redirect_uri: ${AZURE_REDIRECT_URI:http://localhost:8080/callback}\n scope: "openid email profile offline_access"\n\n # Auth0 OAuth2 provider\n # auth0:\n # # Auth0 issuer URL\n # issuer: ${AUTH0_ISSUER:https://your-domain.auth0.com/}\n # client_id: ${AUTH0_CLIENT_ID}\n # client_secret: ${AUTH0_CLIENT_SECRET}\n # redirect_uri: ${AUTH0_REDIRECT_URI:http://localhost:8080/callback}\n # scope: "openid email profile"\n # # Optional: Auth0 audience for API access\n # audience: ${AUTH0_AUDIENCE:}\n\n # # Okta OAuth2 provider (example)\n # okta:\n # issuer: ${OKTA_ISSUER:https://your-okta-domain.okta.com/oauth2/default}\n # client_id: ${OKTA_CLIENT_ID}\n # client_secret: ${OKTA_CLIENT_SECRET}\n # redirect_uri: ${OKTA_REDIRECT_URI:http://localhost:8080/callback}\n # scope: "openid email profile"\n\n # # Keycloak OAuth2 provider (example)\n # keycloak:\n # issuer: ${KEYCLOAK_ISSUER:https://your-keycloak.com/auth/realms/your-realm}\n # client_id: ${KEYCLOAK_CLIENT_ID}\n # client_secret: ${KEYCLOAK_CLIENT_SECRET}\n # redirect_uri: ${KEYCLOAK_REDIRECT_URI:http://localhost:8080/callback}\n # scope: "openid email profile"\n\n # # Generic OIDC provider (for any standard OIDC-compliant provider)\n # custom_oidc:\n # # Just provide the issuer URL and the service will discover all endpoints\n # issuer: ${CUSTOM_OIDC_ISSUER:https://your-provider.com}\n # client_id: ${CUSTOM_OIDC_CLIENT_ID}\n # client_secret: ${CUSTOM_OIDC_CLIENT_SECRET}\n # redirect_uri: ${CUSTOM_OIDC_REDIRECT_URI:http://localhost:8080/callback}\n # scope: "openid email profile"\n\n# Logging configuration\nlogging:\n level: ${OAUTH2_LOG_LEVEL:INFO}\n\n# Session configuration\nsession:\n # Session timeout in seconds (default: 1 hour)\n timeout: ${OAUTH2_SESSION_TIMEOUT:3600}\n\n# Security configuration\nsecurity:\n # CORS settings\n cors:\n enabled: ${OAUTH2_CORS_ENABLED:true}\n origins: ${OAUTH2_CORS_ORIGINS:*}\n\n # Rate limiting\n rate_limit:\n enabled: ${OAUTH2_RATE_LIMIT_ENABLED:true}\n requests_per_minute: ${OAUTH2_RATE_LIMIT_RPM:60}\n'})})]}),"\n",(0,i.jsx)(n.h2,{id:"running-sam-enterprise-with-sso-enabled",children:"Running SAM Enterprise with SSO enabled"}),"\n",(0,i.jsx)(n.p,{children:"Here is an example of Docker run command with Azure SSO provider for production use case:"}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:["You may need to include ",(0,i.jsx)(n.code,{children:"--platform linux/amd64"})," depending on the host machine you\u2019re using."]})}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'docker run -itd -p 8000:8000 -p 9000:9000 \\\n -e LLM_SERVICE_API_KEY="<YOUR_LLM_TOKEN>" \\\n -e LLM_SERVICE_ENDPOINT="<YOUR_LLM_SERVICE_ENDPOINT>" \\\n -e LLM_SERVICE_PLANNING_MODEL_NAME="<YOUR_MODEL_NAME>" \\\n -e LLM_SERVICE_GENERAL_MODEL_NAME="<YOUR_MODEL_NAME>" \\\n -e NAMESPACE="<YOUR_NAMESPACE>" \\\n -e SOLACE_DEV_MODE="false" \\\n -e SOLACE_BROKER_URL="<YOUR_BROKER_URL>" \\\n -e SOLACE_BROKER_VPN="<YOUR_BROKER_VPN>" \\\n -e SOLACE_BROKER_USERNAME="<YOUR_BROKER_USERNAME>" \\\n -e SOLACE_BROKER_PASSWORD="<YOUR_BROKER_PASSWORD>" \\\n -e FASTAPI_HOST="0.0.0.0" \\\n -e FASTAPI_PORT="8000" \\\n -e AZURE_TENANT_ID="xxxxxxxxx-xxxxxx-xxxxxxxx-xxxxxxxxxx" \\\n -e AZURE_CLIENT_ID="xxxxxxxxx-xxxxxx-xxxxxxxx-xxxxxxxxxx" \\\n -e AZURE_CLIENT_SECRET="xxxxxxxxx-xxxxxx-xxxxxxxx-xxxxxxxxxx" \\\n -e OAUTH2_ENABLED="true" \\\n -e OAUTH2_LOG_LEVEL="DEBUG" \\\n -e OAUTH2_DEV_MODE="true" \\\n -e OAUTH2_HOST="0.0.0.0" \\\n -e OAUTH2_PORT="9000" \\\n -e FRONTEND_USE_AUTHORIZATION="true" \\\n -e FRONTEND_REDIRECT_URL="http://localhost:8000" \\\n -e FRONTEND_AUTH_LOGIN_URL="http://localhost:8000/api/v1/auth/login" \\\n -e EXTERNAL_AUTH_SERVICE_URL="http://localhost:9000" \\\n -e EXTERNAL_AUTH_PROVIDER="azure" \\\n -e EXTERNAL_AUTH_CALLBACK="http://localhost:8000/api/v1/auth/callback" \\\n -v <YOUR_NAMED_DOCKER_VOLUME>:/app/config/sso_vol/ \\\n --name sam-ent-prod-sso \\\nsolace-agent-mesh-enterprise:<tag> run config/sso_vol/oauth2_server.yaml config/webui_backend.yaml config/a2a_orchestrator.yaml config/a2a_agents.yaml\n'})}),"\n",(0,i.jsxs)(n.p,{children:["You can then access SAM Enterprise UI through ",(0,i.jsx)(n.a,{href:"http://localhost:8000",children:"http://localhost:8000"})]}),"\n",(0,i.jsxs)(t,{children:[(0,i.jsx)("summary",{children:"Configuration Options"}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Specify the hostname and port for the UI running in the docker container. The main UI runs on port 8000 by default. Using 0.0.0.0 as the host allows external access to the container."})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e FASTAPI_HOST="0.0.0.0" \\\n-e FASTAPI_PORT="8000" \\ \n'})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Enable single sign-on processing on the frontend."})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e FRONTEND_USE_AUTHORIZATION="true" \\\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsxs)(n.strong,{children:["Specify the main URL of the UI. For instance, this could be ",(0,i.jsx)(n.a,{href:"https://www.example.com",children:"https://www.example.com"})]})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e FRONTEND_REDIRECT_URL="http://localhost:8000" \\\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsxs)(n.strong,{children:["Set the login URL used by the main UI. For instance, this could be ",(0,i.jsx)(n.a,{href:"https://www.example.com/api/v1/auth/login",children:"https://www.example.com/api/v1/auth/login"})]})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e FRONTEND_AUTH_LOGIN_URL="http://localhost:8000/api/v1/auth/login" \\\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Enable the OAUTH2 server and set the log level"})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e OAUTH2_ENABLED="true" \\\n-e OAUTH2_LOG_LEVEL="DEBUG" \\\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Specify the hostname and port for the authorization server running in the docker container. Using 0.0.0.0 as the host allows external access to the container."})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e OAUTH2_HOST="0.0.0.0" \\\n-e OAUTH2_PORT="9000" \\\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Specify whether the Oauth2 checks use dev mode. When dev mode is true the following environment variables are added to allow http access and relax the token scope. This MUST be set false in a production environment."})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e OAUTH2_DEV_MODE="true" \\\n'})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'OAUTHLIB_RELAX_TOKEN_SCOPE="1"\nOAUTHLIB_INSECURE_TRANSPORT="1"\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Configure the environment variables for your chosen authentication provider. Refer to the oauth2_config.yaml file to identify the required variables. For example, with Azure set the following"})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e AZURE_TENANT_ID="xxxxxxxxx-xxxxxx-xxxxxxxx-xxxxxxxxxx" \\\n-e AZURE_CLIENT_ID="xxxxxxxxx-xxxxxx-xxxxxxxx-xxxxxxxxxx" \\\n-e AZURE_CLIENT_SECRET="xxxxxxxxx-xxxxxx-xxxxxxxx-xxxxxxxxxx" \\\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Configure the authorization server's public URL (accessible from outside the Docker container) and specify the OAuth2 provider\u2019s name from oauth2_config.yaml (this example uses the azure profile):"})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e EXTERNAL_AUTH_SERVICE_URL="http://localhost:9000" \\\n-e EXTERNAL_AUTH_PROVIDER="azure" \\\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsxs)(n.strong,{children:["Lastly, set the callback URL that your auth provider will use to redirect with the auth code. For instance, this could be ",(0,i.jsx)(n.a,{href:"https://www.example.com/api/v1/auth/callback",children:"https://www.example.com/api/v1/auth/callback"})]})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'-e EXTERNAL_AUTH_CALLBACK="http://localhost:8000/api/v1/auth/callback" \\\n'})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"Note that both the main UI and authorization server ports must be mapped to the host machine, as shown in the Docker run command above:"})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"-p 8000:8000 -p 9000:9000 \\\n"})}),(0,i.jsx)(n.p,{children:(0,i.jsx)(n.strong,{children:"The oauth 2 configuration files must be mounted inside the container:"})}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"-v <VOLUME_PATH_TO_YOUR_OAUTH2_SERVER_YAML_FILE>/oauth2_server.yaml:/app/config/oauth2_server.yaml \\\n-v <VOLUME_PATH_TO_YOUR_OAUTH2_CONFIG_YAML_FILE>/oauth2_config.yaml:/app/config/oauth2_config.yaml \\\n"})})]})]})}function x(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(h,{...e})}):h(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>r,x:()=>a});var o=t(6540);const i={},s=o.createContext(i);function r(e){const n=o.useContext(s);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),o.createElement(s.Provider,{value:n},e.children)}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunksolace_agenitc_mesh_docs=self.webpackChunksolace_agenitc_mesh_docs||[]).push([[5627],{5232:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>o,default:()=>_,frontMatter:()=>r,metadata:()=>a,toc:()=>c});const a=JSON.parse('{"id":"documentation/user-guide/create-gateways","title":"Create Gateways","description":"Gateways in Solace Agent Mesh (SAM) serve as bridges between external systems and the A2A (Agent-to-Agent) ecosystem. They enable your agents to receive information from and send responses to diverse external platforms like chat systems, web applications, IoT devices, APIs, and file systems.","source":"@site/docs/documentation/user-guide/create-gateways.md","sourceDirName":"documentation/user-guide","slug":"/documentation/user-guide/create-gateways","permalink":"/solace-agent-mesh/docs/documentation/user-guide/create-gateways","draft":false,"unlisted":false,"editUrl":"https://github.com/SolaceLabs/solace-agent-mesh/edit/main/docs/docs/documentation/user-guide/create-gateways.md","tags":[],"version":"current","sidebarPosition":40,"frontMatter":{"title":"Create Gateways","sidebar_position":40},"sidebar":"docSidebar","previous":{"title":"Creating Python Tools","permalink":"/solace-agent-mesh/docs/documentation/user-guide/creating-python-tools"},"next":{"title":"Creating Service Providers","permalink":"/solace-agent-mesh/docs/documentation/user-guide/creating-service-providers"}}');var i=t(4848),s=t(8453);const r={title:"Create Gateways",sidebar_position:40},o="Create Gateways",l={},c=[{value:"What Are Gateways?",id:"what-are-gateways",level:2},{value:"Quick Start: Creating Your First Gateway",id:"quick-start-creating-your-first-gateway",level:2},{value:"CLI Options",id:"cli-options",level:3},{value:"Gateway Architecture",id:"gateway-architecture",level:2},{value:"Gateway App",id:"gateway-app",level:3},{value:"Gateway Component",id:"gateway-component",level:3},{value:"Step-by-Step Tutorial",id:"step-by-step-tutorial",level:2},{value:"Step 1: Generate the Gateway Structure",id:"step-1-generate-the-gateway-structure",level:3},{value:"Step 2: Define Configuration Schema",id:"step-2-define-configuration-schema",level:3},{value:"Step 3: Implement Core Logic",id:"step-3-implement-core-logic",level:3},{value:"Step 4: Configure the Gateway",id:"step-4-configure-the-gateway",level:3},{value:"Step 5: Install Dependencies",id:"step-5-install-dependencies",level:3},{value:"Step 6: Run Your Gateway",id:"step-6-run-your-gateway",level:3},{value:"Advanced Gateway Patterns",id:"advanced-gateway-patterns",level:2},{value:"Authentication and Authorization",id:"authentication-and-authorization",level:3},{value:"File Handling with Artifacts",id:"file-handling-with-artifacts",level:3},{value:"Streaming Responses",id:"streaming-responses",level:3},{value:"Error Handling and Retry Logic",id:"error-handling-and-retry-logic",level:3},{value:"Best Practices",id:"best-practices",level:2},{value:"1. Configuration Management",id:"1-configuration-management",level:3},{value:"2. Error Handling",id:"2-error-handling",level:3},{value:"3. Security",id:"3-security",level:3},{value:"4. Performance",id:"4-performance",level:3},{value:"5. Monitoring and Logging",id:"5-monitoring-and-logging",level:3},{value:"Common Gateway Patterns",id:"common-gateway-patterns",level:2},{value:"HTTP/REST API Gateway",id:"httprest-api-gateway",level:3},{value:"WebSocket Gateway",id:"websocket-gateway",level:3},{value:"Message Queue Gateway",id:"message-queue-gateway",level:3},{value:"Packaging as a Plugin",id:"packaging-as-a-plugin",level:2},{value:"1. Create Plugin Structure",id:"1-create-plugin-structure",level:3},{value:"2. Configure <code>pyproject.toml</code>",id:"2-configure-pyprojecttoml",level:3},{value:"3. Build and Install",id:"3-build-and-install",level:3},{value:"Troubleshooting",id:"troubleshooting",level:2},{value:"Common Issues",id:"common-issues",level:3},{value:"Gateway Fails to Start",id:"gateway-fails-to-start",level:4},{value:"Tasks Not Reaching Agents",id:"tasks-not-reaching-agents",level:4},{value:"Authentication Failures",id:"authentication-failures",level:4},{value:"File/Artifact Issues",id:"fileartifact-issues",level:4},{value:"Debugging Tips",id:"debugging-tips",level:3}];function d(e){const n={admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.header,{children:(0,i.jsx)(n.h1,{id:"create-gateways",children:"Create Gateways"})}),"\n",(0,i.jsx)(n.p,{children:"Gateways in Solace Agent Mesh (SAM) serve as bridges between external systems and the A2A (Agent-to-Agent) ecosystem. They enable your agents to receive information from and send responses to diverse external platforms like chat systems, web applications, IoT devices, APIs, and file systems."}),"\n",(0,i.jsx)(n.p,{children:"This guide walks you through the steps of creating custom gateways, from basic concepts to advanced implementations."}),"\n",(0,i.jsx)(n.h2,{id:"what-are-gateways",children:"What Are Gateways?"}),"\n",(0,i.jsx)(n.p,{children:"A gateway acts as a translator and coordinator that:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Receives"})," events, messages, or data from external systems"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Authenticates"})," and authorizes external interactions"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Translates"})," external data into standardized A2A ",(0,i.jsx)(n.code,{children:"Task"})," format"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Submits"})," tasks to target A2A agents for processing"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Receives"})," responses and status updates from agents"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Translates"})," A2A responses back to external system format"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.strong,{children:"Sends"})," results back to the originating external system"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"quick-start-creating-your-first-gateway",children:"Quick Start: Creating Your First Gateway"}),"\n",(0,i.jsxs)(n.p,{children:["You can create a gateway directly using the SAM CLI ",(0,i.jsx)(n.code,{children:"sam add gateway"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam add gateway my-custom-gateway\n"})}),"\n",(0,i.jsx)(n.p,{children:"This command:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Launches an interactive setup (or use ",(0,i.jsx)(n.code,{children:"--gui"})," for browser-based configuration)"]}),"\n",(0,i.jsx)(n.li,{children:"Generates the necessary files and configuration"}),"\n",(0,i.jsx)(n.li,{children:"Sets up the basic gateway structure"}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"cli-options",children:"CLI Options"}),"\n",(0,i.jsx)(n.p,{children:"You can customize the gateway creation with these options:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:'sam add gateway my-gateway \\\n --namespace "myorg/dev" \\\n --gateway-id "my-custom-gw-id" \\\n --artifact-service-type "filesystem" \\\n --artifact-service-base-path "var/data/my-gateway-artifacts" \\\n --system-purpose "This gateway processes external data feeds" \\\n --response-format "Agents should respond with structured JSON"\n'})}),"\n",(0,i.jsx)(n.p,{children:"For a complete list of options, run:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam add gateway --help\n"})}),"\n",(0,i.jsx)(n.h2,{id:"gateway-architecture",children:"Gateway Architecture"}),"\n",(0,i.jsx)(n.p,{children:"Every SAM gateway consists of two main components:"}),"\n",(0,i.jsx)(n.h3,{id:"gateway-app",children:"Gateway App"}),"\n",(0,i.jsxs)(n.p,{children:["Gateway App (",(0,i.jsx)(n.code,{children:"app.py"}),"):"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Defines configuration schema"}),"\n",(0,i.jsx)(n.li,{children:"Manages gateway-level settings"}),"\n",(0,i.jsx)(n.li,{children:"Links to the gateway component"}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"gateway-component",children:"Gateway Component"}),"\n",(0,i.jsxs)(n.p,{children:["Gateway Component (",(0,i.jsx)(n.code,{children:"component.py"}),"):"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Contains the core business logic"}),"\n",(0,i.jsx)(n.li,{children:"Handles external system integration"}),"\n",(0,i.jsx)(n.li,{children:"Implements required abstract methods"}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"step-by-step-tutorial",children:"Step-by-Step Tutorial"}),"\n",(0,i.jsxs)(n.p,{children:["Let's create a practical example, ",(0,i.jsx)(n.strong,{children:"Directory Monitor Gateway"}),", a gateway that monitors a directory for new files and sends them to agents for processing."]}),"\n",(0,i.jsxs)(n.p,{children:["You can create a gateway using either ",(0,i.jsx)(n.code,{children:"sam add gateway <your_gateway_name>"})," command directly or ",(0,i.jsx)(n.code,{children:"sam plugin create <your_gateway_plugin_name> --type gateway"})," command as gateway plugin."]}),"\n",(0,i.jsxs)(n.admonition,{title:"Gateway as plugin",type:"tip",children:[(0,i.jsx)(n.p,{children:"Gateways can also be implemented as plugins. This allows you to easily package your gateway logic and reuse it across different projects."}),(0,i.jsxs)(n.p,{children:["To create a plugin of type gateway, use the ",(0,i.jsx)(n.code,{children:"sam plugin create <your_gateway_plugin_name> --type gateway"})," command."]}),(0,i.jsx)(n.p,{children:"For a complete list of options, run:"}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam plugin create --help\n"})}),(0,i.jsxs)(n.p,{children:["To create a gateway instance based on a plugin, use the ",(0,i.jsx)(n.code,{children:"sam plugin add <your_gateway_name> --plugin <your_gateway_plugin>"})," command."]}),(0,i.jsx)(n.p,{children:"For a complete list of options, run:"}),(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam plugin add --help\n"})}),(0,i.jsx)(n.p,{children:"Although the specific directory structure may differ from standalone gateways, the core concepts remain the same. The core files remain the same: app.py, component.py, and the YAML configuration file."})]}),"\n",(0,i.jsx)(n.h3,{id:"step-1-generate-the-gateway-structure",children:"Step 1: Generate the Gateway Structure"}),"\n",(0,i.jsxs)(n.p,{children:["This tutorial shows you how to create a new gateway with the ",(0,i.jsx)(n.code,{children:"sam add gateway"})," command."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam add gateway dir-monitor\n"})}),"\n",(0,i.jsx)(n.p,{children:"This creates:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"configs/gateways/dir_monitor_config.yaml"})," - Configuration file"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"src/dir_monitor/app.py"})," - Gateway app class"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"src/dir_monitor/component.py"})," - Gateway component class"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"step-2-define-configuration-schema",children:"Step 2: Define Configuration Schema"}),"\n",(0,i.jsxs)(n.p,{children:["Define Configuration Schema (",(0,i.jsx)(n.code,{children:"app.py"}),")"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'# src/dir_monitor/app.py\nfrom typing import Any, Dict, List, Type\nfrom solace_ai_connector.common.log import log\nfrom solace_agent_mesh.gateway.base.app import BaseGatewayApp\nfrom solace_agent_mesh.gateway.base.component import BaseGatewayComponent\nfrom .component import DirMonitorGatewayComponent\n\n# Module info required by SAC\ninfo = {\n "class_name": "DirMonitorGatewayApp",\n "description": "Custom App class for the A2A DirMonitor Gateway.",\n}\n\nclass DirMonitorGatewayApp(BaseGatewayApp):\n """\n Directory Monitor Gateway App\n Extends BaseGatewayApp with directory monitoring specific configuration.\n """\n\n # Define gateway-specific configuration parameters\n SPECIFIC_APP_SCHEMA_PARAMS: List[Dict[str, Any]] = [\n {\n "name": "directory_path",\n "required": True,\n "type": "string",\n "description": "The directory path to monitor for changes.",\n },\n {\n "name": "target_agent_name",\n "required": False,\n "type": "string",\n "default": "OrchestratorAgent",\n "description": "The A2A agent to send tasks to.",\n },\n {\n "name": "default_user_identity",\n "required": False,\n "type": "string",\n "default": "dir_monitor_user",\n "description": "Default user identity for A2A tasks.",\n },\n {\n "name": "error_directory_path",\n "required": True,\n "type": "string",\n "description": "Directory to move files if processing fails.",\n },\n ]\n\n def __init__(self, app_info: Dict[str, Any], **kwargs):\n log_prefix = app_info.get("name", "DirMonitorGatewayApp")\n log.info("[%s] Initializing Directory Monitor Gateway App...", log_prefix)\n super().__init__(app_info=app_info, **kwargs)\n log.info("[%s] Directory Monitor Gateway App initialized.", self.name)\n\n def _get_gateway_component_class(self) -> Type[BaseGatewayComponent]:\n """Returns the gateway component class for this app."""\n return DirMonitorGatewayComponent\n'})}),"\n",(0,i.jsx)(n.h3,{id:"step-3-implement-core-logic",children:"Step 3: Implement Core Logic"}),"\n",(0,i.jsxs)(n.p,{children:["Implement Core Logic (",(0,i.jsx)(n.code,{children:"component.py"}),")"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'# src/dir_monitor/component.py\nimport asyncio\nimport os\nimport shutil\nimport mimetypes\nimport threading\nfrom typing import Any, Dict, List, Optional, Tuple, Union\nfrom datetime import datetime, timezone\n\nfrom solace_ai_connector.common.log import log\n\n# Import watchdog for file system monitoring\ntry:\n from watchdog.observers import Observer\n from watchdog.events import FileSystemEventHandler\n WATCHDOG_AVAILABLE = True\nexcept ImportError:\n WATCHDOG_AVAILABLE = False\n Observer = None\n FileSystemEventHandler = None\n\nfrom solace_agent_mesh.gateway.base.component import BaseGatewayComponent\nfrom solace_agent_mesh.common.types import (\n Part as A2APart,\n TextPart,\n FilePart,\n Task,\n TaskStatusUpdateEvent,\n TaskArtifactUpdateEvent,\n JSONRPCError,\n FileContent,\n)\nfrom solace_agent_mesh.agent.utils.artifact_helpers import save_artifact_with_metadata\n\n# Component info\ninfo = {\n "class_name": "DirMonitorGatewayComponent",\n "description": "Monitors directories for new files and processes them via A2A agents.",\n}\n\nclass DirMonitorGatewayComponent(BaseGatewayComponent):\n """\n Directory Monitor Gateway Component\n Watches a directory and creates A2A tasks for new files.\n """\n\n def __init__(self, **kwargs: Any):\n super().__init__(**kwargs)\n log.info("%s Initializing Directory Monitor Gateway Component...", self.log_identifier)\n\n # Check if watchdog is available\n if not WATCHDOG_AVAILABLE:\n log.error("%s Watchdog library not found. Install with: pip install watchdog", \n self.log_identifier)\n raise ImportError("Watchdog library required for directory monitoring")\n\n # Load configuration\n try:\n self.directory_path = self.get_config("directory_path")\n self.target_agent_name = self.get_config("target_agent_name", "OrchestratorAgent")\n self.default_user_identity_id = self.get_config("default_user_identity", "dir_monitor_user")\n self.error_directory_path = self.get_config("error_directory_path")\n\n # Validate directories\n if not os.path.isdir(self.directory_path):\n raise ValueError(f"Monitor directory not found: {self.directory_path}")\n \n os.makedirs(self.error_directory_path, exist_ok=True)\n log.info("%s Monitoring: %s, Error dir: %s", \n self.log_identifier, self.directory_path, self.error_directory_path)\n\n except Exception as e:\n log.error("%s Configuration error: %s", self.log_identifier, e)\n raise\n\n # Initialize monitoring components\n self.observer: Optional[Observer] = None\n self.watchdog_thread: Optional[threading.Thread] = None\n\n log.info("%s Directory Monitor Gateway Component initialized.", self.log_identifier)\n\n class DirWatchEventHandler(FileSystemEventHandler):\n """Handles file system events from Watchdog."""\n \n def __init__(self, component_ref: \'DirMonitorGatewayComponent\'):\n super().__init__()\n self.component_ref = component_ref\n self.log_identifier = f"{component_ref.log_identifier}[FileHandler]"\n\n def on_created(self, event):\n if event.is_directory:\n return\n\n file_path = event.src_path\n log.info("%s New file detected: %s", self.log_identifier, file_path)\n\n # Bridge to async loop\n if self.component_ref.async_loop and self.component_ref.async_loop.is_running():\n asyncio.run_coroutine_threadsafe(\n self.component_ref._process_new_file(file_path),\n self.component_ref.async_loop\n )\n else:\n log.error("%s Async loop not available for file: %s", \n self.log_identifier, file_path)\n\n def generate_uuid(self) -> str:\n """Generate a unique identifier."""\n import uuid\n return str(uuid.uuid4())\n\n def _start_listener(self) -> None:\n """Start the directory monitoring listener."""\n log_id_prefix = f"{self.log_identifier}[StartListener]"\n log.info("%s Starting directory monitor for: %s", log_id_prefix, self.directory_path)\n\n if not WATCHDOG_AVAILABLE:\n log.error("%s Watchdog not available", log_id_prefix)\n self.stop_signal.set()\n return\n\n # Set up file system observer\n self.observer = Observer()\n event_handler = self.DirWatchEventHandler(self)\n self.observer.schedule(event_handler, self.directory_path, recursive=False)\n\n # Start observer in separate thread\n self.watchdog_thread = threading.Thread(\n target=self._run_observer,\n name=f"{self.name}_WatchdogThread",\n daemon=True\n )\n self.watchdog_thread.start()\n log.info("%s Directory monitor started", log_id_prefix)\n\n def _run_observer(self):\n """Run the watchdog observer."""\n if not self.observer:\n return\n \n log_id_prefix = f"{self.log_identifier}[Observer]"\n try:\n log.info("%s Starting file system observer...", log_id_prefix)\n self.observer.start()\n \n # Wait for stop signal\n while not self.stop_signal.is_set() and self.observer.is_alive():\n self.stop_signal.wait(timeout=1)\n \n log.info("%s Observer loop exiting", log_id_prefix)\n except Exception as e:\n log.exception("%s Observer error: %s", log_id_prefix, e)\n self.stop_signal.set()\n finally:\n if self.observer.is_alive():\n self.observer.stop()\n self.observer.join()\n log.info("%s Observer stopped", log_id_prefix)\n\n def _stop_listener(self) -> None:\n """Stop the directory monitoring listener."""\n log_id_prefix = f"{self.log_identifier}[StopListener]"\n log.info("%s Stopping directory monitor...", log_id_prefix)\n \n if self.observer and self.observer.is_alive():\n log.info("%s Stopping observer...", log_id_prefix)\n self.observer.stop()\n \n if self.watchdog_thread and self.watchdog_thread.is_alive():\n log.info("%s Joining observer thread...", log_id_prefix)\n self.watchdog_thread.join(timeout=5)\n if self.watchdog_thread.is_alive():\n log.warning("%s Observer thread did not join cleanly", log_id_prefix)\n \n log.info("%s Directory monitor stopped", log_id_prefix)\n\n async def _process_new_file(self, file_path: str):\n """Process a newly detected file."""\n log_id_prefix = f"{self.log_identifier}[ProcessFile:{os.path.basename(file_path)}]"\n log.info("%s Processing new file: %s", log_id_prefix, file_path)\n \n error_context = {\n "file_path": file_path,\n "a2a_session_id": f"dir_monitor-error-{self.generate_uuid()}"\n }\n\n try:\n # Step 1: Authenticate and enrich user\n user_identity_profile = await self.authenticate_and_enrich_user(file_path)\n if not user_identity_profile:\n log.error("%s Authentication failed for file: %s", log_id_prefix, file_path)\n error_obj = JSONRPCError(code=-32001, message="Authentication failed")\n await self._send_error_to_external(error_context, error_obj)\n return\n\n # Step 2: Translate external input to A2A format\n target_agent_name, a2a_parts, external_request_context = await self._translate_external_input(\n file_path, user_identity_profile\n )\n\n if not target_agent_name or not a2a_parts:\n log.error("%s Failed to translate file to A2A task: %s", log_id_prefix, file_path)\n error_obj = JSONRPCError(code=-32002, message="Failed to translate file to A2A task")\n final_error_context = {**error_context, **external_request_context}\n await self._send_error_to_external(final_error_context, error_obj)\n return\n\n # Step 3: Submit A2A task\n log.info("%s Submitting A2A task for file: %s to agent: %s", \n log_id_prefix, file_path, target_agent_name)\n await self.submit_a2a_task(\n target_agent_name=target_agent_name,\n a2a_parts=a2a_parts,\n external_request_context=external_request_context,\n user_identity=user_identity_profile\n )\n log.info("%s A2A task submitted for file: %s", log_id_prefix, file_path)\n\n except FileNotFoundError:\n log.error("%s File not found during processing: %s", log_id_prefix, file_path)\n except Exception as e:\n log.exception("%s Unexpected error processing file %s: %s", log_id_prefix, file_path, e)\n error_obj = JSONRPCError(code=-32000, message=f"Unexpected error: {e}")\n await self._send_error_to_external(error_context, error_obj)\n\n async def _extract_initial_claims(self, external_event_data: Any) -> Optional[Dict[str, Any]]:\n """Extract user identity claims from file event."""\n file_path = str(external_event_data)\n log_id_prefix = f"{self.log_identifier}[ExtractClaims:{os.path.basename(file_path)}]"\n \n claims = {\n "id": self.default_user_identity_id,\n "source": "dir_monitor",\n "file_path": file_path\n }\n log.debug("%s Extracted claims for file %s: %s", log_id_prefix, file_path, claims)\n return claims\n\n async def _translate_external_input(\n self, external_event_data: Any, authenticated_user_identity: Dict[str, Any]\n ) -> Tuple[Optional[str], List[A2APart], Dict[str, Any]]:\n """Translate file event to A2A task format."""\n file_path = str(external_event_data)\n log_id_prefix = f"{self.log_identifier}[TranslateInput:{os.path.basename(file_path)}]"\n\n user_id_for_a2a = authenticated_user_identity.get("id", self.default_user_identity_id)\n a2a_session_id = f"dir_monitor-session-{self.generate_uuid()}"\n \n # Prepare external request context\n external_request_context: Dict[str, Any] = {\n "file_path": file_path,\n "user_id_for_a2a": user_id_for_a2a,\n "app_name_for_artifacts": self.gateway_id,\n "user_id_for_artifacts": user_id_for_a2a,\n "a2a_session_id": a2a_session_id,\n }\n a2a_parts: List[A2APart] = []\n\n try:\n # Check if file exists\n if not os.path.exists(file_path):\n log.error("%s File does not exist: %s", log_id_prefix, file_path)\n raise FileNotFoundError(f"File not found: {file_path}")\n\n # Read file content\n with open(file_path, "rb") as f:\n content_bytes = f.read()\n \n # Determine MIME type\n mime_type, _ = mimetypes.guess_type(file_path)\n if mime_type is None:\n mime_type = "application/octet-stream"\n\n # Save file as artifact\n if not self.shared_artifact_service:\n log.error("%s Artifact service not available for file: %s", \n log_id_prefix, os.path.basename(file_path))\n return None, [], external_request_context\n\n artifact_metadata = {\n "source": "dir_monitor_gateway",\n "original_filename": os.path.basename(file_path),\n "detected_mime_type": mime_type,\n "processing_timestamp_utc": datetime.now(timezone.utc).isoformat(),\n }\n\n log.debug("%s Saving artifact for file: %s", log_id_prefix, file_path)\n save_result = await save_artifact_with_metadata(\n artifact_service=self.shared_artifact_service,\n app_name=self.gateway_id,\n user_id=str(user_id_for_a2a),\n session_id=a2a_session_id,\n filename=os.path.basename(file_path),\n content_bytes=content_bytes,\n mime_type=mime_type,\n metadata_dict=artifact_metadata,\n timestamp=datetime.now(timezone.utc),\n )\n\n if save_result["status"] not in ["success", "partial_success"]:\n log.error("%s Failed to save file as artifact: %s", \n log_id_prefix, save_result.get("message"))\n return None, [], external_request_context\n\n # Create artifact URI\n data_version = save_result.get("data_version", 0)\n artifact_uri = f"artifact://{self.gateway_id}/{str(user_id_for_a2a)}/{a2a_session_id}/{os.path.basename(file_path)}?version={data_version}"\n \n log.info("%s Saved file as artifact: %s", log_id_prefix, artifact_uri)\n\n # Create A2A parts\n file_content_obj = FileContent(\n name=os.path.basename(file_path),\n uri=artifact_uri,\n mimeType=mime_type\n )\n a2a_parts.append(FilePart(file=file_content_obj))\n a2a_parts.append(TextPart(\n text=f"Please analyze and summarize the content of: {os.path.basename(file_path)}"\n ))\n\n log.info("%s Successfully translated file %s into A2A parts", log_id_prefix, file_path)\n return self.target_agent_name, a2a_parts, external_request_context\n\n except Exception as e:\n log.exception("%s Error translating file %s: %s", log_id_prefix, file_path, e)\n return None, [], external_request_context\n\n async def _send_final_response_to_external(\n self, external_request_context: Dict[str, Any], task_data: Task\n ) -> None:\n """Handle final response from A2A agent."""\n log_id_prefix = f"{self.log_identifier}[SendFinalResponse]"\n file_path = external_request_context.get("file_path", "Unknown file")\n task_id = task_data.id\n\n # Extract summary from response\n summary_text = "Summary not available."\n if task_data.status and task_data.status.message and task_data.status.message.parts:\n for part in task_data.status.message.parts:\n if isinstance(part, TextPart):\n summary_text = part.text\n break\n \n log.info("%s Task %s completed for file \'%s\'. Status: %s", \n log_id_prefix, task_id, os.path.basename(file_path), \n task_data.status.state if task_data.status else "Unknown")\n log.info("%s Summary: %s", log_id_prefix, summary_text[:200] + "..." if len(summary_text) > 200 else summary_text)\n\n async def _send_error_to_external(\n self, external_request_context: Dict[str, Any], error_data: JSONRPCError\n ) -> None:\n """Handle errors by moving files to error directory."""\n log_id_prefix = f"{self.log_identifier}[SendError]"\n file_path = external_request_context.get("file_path")\n \n log.error("%s A2A Error for file \'%s\'. Code: %s, Message: %s",\n log_id_prefix, \n os.path.basename(file_path) if file_path else "Unknown file",\n error_data.code, error_data.message)\n\n # Move problematic file to error directory\n if file_path and os.path.exists(file_path):\n try:\n os.makedirs(self.error_directory_path, exist_ok=True)\n base_name = os.path.basename(file_path)\n error_file_path = os.path.join(self.error_directory_path, base_name)\n \n # Handle filename conflicts\n counter = 0\n while os.path.exists(error_file_path):\n counter += 1\n name, ext = os.path.splitext(base_name)\n error_file_path = os.path.join(self.error_directory_path, f"{name}_error_{counter}{ext}")\n\n shutil.move(file_path, error_file_path)\n log.info("%s Moved problematic file %s to %s", log_id_prefix, file_path, error_file_path)\n except Exception as e:\n log.exception("%s Failed to move file %s to error directory: %s",\n log_id_prefix, file_path, e)\n\n async def _send_update_to_external(\n self,\n external_request_context: Dict[str, Any],\n event_data: Union[TaskStatusUpdateEvent, TaskArtifactUpdateEvent],\n is_final_chunk_of_update: bool,\n ) -> None:\n """Handle intermediate updates (optional for this gateway)."""\n log_id_prefix = f"{self.log_identifier}[SendUpdate]"\n task_id = event_data.id\n file_path = external_request_context.get("file_path", "Unknown file")\n \n log.debug("%s Received update for task %s (file %s). Updates not processed by this gateway.",\n log_id_prefix, task_id, os.path.basename(file_path))\n\n def cleanup(self):\n """Clean up resources."""\n log.info("%s Cleaning up Directory Monitor Gateway Component...", self.log_identifier)\n super().cleanup()\n log.info("%s Directory Monitor Gateway Component cleanup finished.", self.log_identifier)\n'})}),"\n",(0,i.jsx)(n.h3,{id:"step-4-configure-the-gateway",children:"Step 4: Configure the Gateway"}),"\n",(0,i.jsxs)(n.p,{children:["Configure the Gateway (",(0,i.jsx)(n.code,{children:"dir_monitor_config.yaml"}),")"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:'# configs/gateways/dir_monitor_config.yaml\nlog:\n stdout_log_level: INFO\n log_file_level: DEBUG\n log_file: "dir_monitor_gateway.log"\n\n!include ../shared_config.yaml\n\napps:\n - name: dir_monitor_gateway_app\n app_base_path: .\n app_module: src.dir_monitor.app\n\n broker:\n <<: *broker_connection\n\n app_config:\n namespace: ${NAMESPACE}\n gateway_id: dir-monitor-gateway\n \n # Artifact service configuration\n artifact_service: *default_artifact_service\n\n # Authorization service\n authorization_service:\n type: "none"\n\n # System purpose for A2A context\n system_purpose: >\n This system monitors directories for new files and processes them automatically.\n Analyze and summarize file contents. Always provide useful insights about the files.\n Your external name is Directory Monitor Agent.\n\n response_format: >\n Responses should be clear, concise, and professionally formatted.\n Provide structured analysis of file contents in Markdown format.\n\n # Gateway-specific configuration\n directory_path: /path/to/monitor/directory\n error_directory_path: /path/to/error/directory\n target_agent_name: "OrchestratorAgent"\n default_user_identity: "dir_monitor_system"\n'})}),"\n",(0,i.jsx)(n.h3,{id:"step-5-install-dependencies",children:"Step 5: Install Dependencies"}),"\n",(0,i.jsx)(n.p,{children:"Add required dependencies to your project:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"pip install watchdog\n"})}),"\n",(0,i.jsx)(n.h3,{id:"step-6-run-your-gateway",children:"Step 6: Run Your Gateway"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"sam run configs/gateways/dir_monitor_config.yaml\n"})}),"\n",(0,i.jsx)(n.h2,{id:"advanced-gateway-patterns",children:"Advanced Gateway Patterns"}),"\n",(0,i.jsx)(n.h3,{id:"authentication-and-authorization",children:"Authentication and Authorization"}),"\n",(0,i.jsx)(n.p,{children:"Gateways can implement sophisticated authentication:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'async def _extract_initial_claims(self, external_event_data: Any) -> Optional[Dict[str, Any]]:\n """Extract user claims with API key validation."""\n request = external_event_data.get("request")\n \n # Validate API key\n api_key = request.headers.get("X-API-Key")\n if not api_key or not self._validate_api_key(api_key):\n return None\n \n # Extract user information\n user_id = request.headers.get("X-User-ID", "anonymous")\n \n return {\n "id": user_id,\n "source": "api_gateway",\n "api_key_hash": hashlib.sha256(api_key.encode()).hexdigest()[:8],\n "roles": self._get_user_roles(user_id)\n }\n'})}),"\n",(0,i.jsx)(n.h3,{id:"file-handling-with-artifacts",children:"File Handling with Artifacts"}),"\n",(0,i.jsx)(n.p,{children:"For gateways that handle files:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'async def _save_file_as_artifact(self, file_content: bytes, filename: str, \n mime_type: str, session_id: str) -> Optional[str]:\n """Save file content as artifact and return URI."""\n if not self.shared_artifact_service:\n return None\n \n try:\n save_result = await save_artifact_with_metadata(\n artifact_service=self.shared_artifact_service,\n app_name=self.gateway_id,\n user_id="system",\n session_id=session_id,\n filename=filename,\n content_bytes=file_content,\n mime_type=mime_type,\n metadata_dict={\n "source": "my_gateway",\n "upload_timestamp": datetime.now(timezone.utc).isoformat()\n },\n timestamp=datetime.now(timezone.utc)\n )\n \n if save_result["status"] in ["success", "partial_success"]:\n version = save_result.get("data_version", 0)\n return f"artifact://{self.gateway_id}/system/{session_id}/{filename}?version={version}"\n \n except Exception as e:\n log.error("Failed to save artifact: %s", e)\n \n return None\n'})}),"\n",(0,i.jsx)(n.h3,{id:"streaming-responses",children:"Streaming Responses"}),"\n",(0,i.jsx)(n.p,{children:"Handle streaming responses from agents:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'async def _send_update_to_external(\n self, external_request_context: Dict[str, Any],\n event_data: Union[TaskStatusUpdateEvent, TaskArtifactUpdateEvent],\n is_final_chunk_of_update: bool\n) -> None:\n """Send streaming updates to external system."""\n if isinstance(event_data, TaskStatusUpdateEvent):\n if event_data.status and event_data.status.message:\n for part in event_data.status.message.parts:\n if isinstance(part, TextPart):\n # Send partial text to external system\n await self._send_partial_response(\n external_request_context,\n part.text,\n is_final=is_final_chunk_of_update\n )\n'})}),"\n",(0,i.jsx)(n.h3,{id:"error-handling-and-retry-logic",children:"Error Handling and Retry Logic"}),"\n",(0,i.jsx)(n.p,{children:"Implement robust error handling:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'async def _process_with_retry(self, data: Any, max_retries: int = 3):\n """Process data with retry logic."""\n for attempt in range(max_retries):\n try:\n return await self._process_data(data)\n except TemporaryError as e:\n if attempt < max_retries - 1:\n wait_time = 2 ** attempt # Exponential backoff\n log.warning("Attempt %d failed, retrying in %ds: %s", \n attempt + 1, wait_time, e)\n await asyncio.sleep(wait_time)\n else:\n raise\n except PermanentError:\n # Don\'t retry permanent errors\n raise\n'})}),"\n",(0,i.jsx)(n.h2,{id:"best-practices",children:"Best Practices"}),"\n",(0,i.jsx)(n.h3,{id:"1-configuration-management",children:"1. Configuration Management"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Use environment variables for sensitive data"}),"\n",(0,i.jsx)(n.li,{children:"Provide sensible defaults"}),"\n",(0,i.jsx)(n.li,{children:"Validate configuration at startup"}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"2-error-handling",children:"2. Error Handling"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Implement comprehensive error handling"}),"\n",(0,i.jsx)(n.li,{children:"Use appropriate HTTP status codes"}),"\n",(0,i.jsx)(n.li,{children:"Log errors with sufficient context"}),"\n",(0,i.jsx)(n.li,{children:"Provide meaningful error messages"}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"3-security",children:"3. Security"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Validate all external inputs"}),"\n",(0,i.jsx)(n.li,{children:"Use secure authentication methods"}),"\n",(0,i.jsx)(n.li,{children:"Implement rate limiting where appropriate"}),"\n",(0,i.jsx)(n.li,{children:"Store secrets securely (use environment variables)"}),"\n",(0,i.jsx)(n.li,{children:"Follow principle of least privilege"}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"4-performance",children:"4. Performance"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Use async/await for I/O operations"}),"\n",(0,i.jsx)(n.li,{children:"Implement connection pooling for external APIs"}),"\n",(0,i.jsx)(n.li,{children:"Monitor resource usage"}),"\n",(0,i.jsx)(n.li,{children:"Handle backpressure appropriately"}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"5-monitoring-and-logging",children:"5. Monitoring and Logging"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Use structured logging"}),"\n",(0,i.jsx)(n.li,{children:"Include correlation IDs"}),"\n",(0,i.jsx)(n.li,{children:"Monitor key metrics (latency, error rates, throughput)"}),"\n",(0,i.jsx)(n.li,{children:"Set up health checks"}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"common-gateway-patterns",children:"Common Gateway Patterns"}),"\n",(0,i.jsx)(n.h3,{id:"httprest-api-gateway",children:"HTTP/REST API Gateway"}),"\n",(0,i.jsx)(n.p,{children:"For HTTP-based integrations:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'from fastapi import FastAPI, HTTPException, Depends\nfrom fastapi.security import HTTPBearer\n\nclass HTTPAPIGatewayComponent(BaseGatewayComponent):\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n self.app = FastAPI()\n self.security = HTTPBearer()\n self._setup_routes()\n \n def _setup_routes(self):\n @self.app.post("/webhook/{endpoint_id}")\n async def webhook_handler(endpoint_id: str, request: Request,\n token: str = Depends(self.security)):\n # Authenticate request\n user_identity = await self.authenticate_and_enrich_user({\n "token": token,\n "endpoint_id": endpoint_id,\n "request": request\n })\n \n if not user_identity:\n raise HTTPException(status_code=401, detail="Unauthorized")\n \n # Process webhook\n body = await request.json()\n target_agent, parts, context = await self._translate_external_input(\n body, user_identity\n )\n \n task_id = await self.submit_a2a_task(\n target_agent_name=target_agent,\n a2a_parts=parts,\n external_request_context=context,\n user_identity=user_identity\n )\n \n return {"task_id": task_id, "status": "accepted"}\n'})}),"\n",(0,i.jsx)(n.h3,{id:"websocket-gateway",children:"WebSocket Gateway"}),"\n",(0,i.jsx)(n.p,{children:"For real-time bidirectional communication:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'import websockets\nimport json\n\nclass WebSocketGatewayComponent(BaseGatewayComponent):\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n self.connections = {}\n \n async def _start_listener(self):\n """Start WebSocket server."""\n self.server = await websockets.serve(\n self.handle_websocket,\n self.get_config("websocket_host", "localhost"),\n self.get_config("websocket_port", 8765)\n )\n log.info("%s WebSocket server started", self.log_identifier)\n \n async def handle_websocket(self, websocket, path):\n """Handle WebSocket connections."""\n connection_id = self.generate_uuid()\n self.connections[connection_id] = websocket\n \n try:\n async for message in websocket:\n data = json.loads(message)\n await self.process_websocket_message(connection_id, data)\n except websockets.exceptions.ConnectionClosed:\n log.info("%s WebSocket connection closed: %s", self.log_identifier, connection_id)\n finally:\n self.connections.pop(connection_id, None)\n \n async def process_websocket_message(self, connection_id: str, data: dict):\n """Process incoming WebSocket message."""\n user_identity = await self.authenticate_and_enrich_user({\n "connection_id": connection_id,\n "data": data\n })\n \n if user_identity:\n target_agent, parts, context = await self._translate_external_input(\n data, user_identity\n )\n context["connection_id"] = connection_id\n \n await self.submit_a2a_task(\n target_agent_name=target_agent,\n a2a_parts=parts,\n external_request_context=context,\n user_identity=user_identity\n )\n \n async def _send_final_response_to_external(self, context: Dict[str, Any], task_data: Task):\n """Send response back via WebSocket."""\n connection_id = context.get("connection_id")\n websocket = self.connections.get(connection_id)\n \n if websocket:\n response = {\n "task_id": task_data.id,\n "status": task_data.status.state.value if task_data.status else "unknown",\n "result": self._extract_text_from_task(task_data)\n }\n await websocket.send(json.dumps(response))\n'})}),"\n",(0,i.jsx)(n.h3,{id:"message-queue-gateway",children:"Message Queue Gateway"}),"\n",(0,i.jsx)(n.p,{children:"For integration with message queues:"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-python",children:'import asyncio\nimport aio_pika\n\nclass MessageQueueGatewayComponent(BaseGatewayComponent):\n def __init__(self, **kwargs):\n super().__init__(**kwargs)\n self.connection = None\n self.channel = None\n \n async def _start_listener(self):\n """Connect to message queue and start consuming."""\n connection_url = self.get_config("rabbitmq_url")\n queue_name = self.get_config("input_queue_name")\n \n self.connection = await aio_pika.connect_robust(connection_url)\n self.channel = await self.connection.channel()\n \n queue = await self.channel.declare_queue(queue_name, durable=True)\n await queue.consume(self.process_message)\n \n log.info("%s Started consuming from queue: %s", self.log_identifier, queue_name)\n \n async def process_message(self, message: aio_pika.IncomingMessage):\n """Process incoming queue message."""\n async with message.process():\n try:\n data = json.loads(message.body.decode())\n \n user_identity = await self.authenticate_and_enrich_user(data)\n if not user_identity:\n log.warning("%s Authentication failed for message", self.log_identifier)\n return\n \n target_agent, parts, context = await self._translate_external_input(\n data, user_identity\n )\n context["message_id"] = message.message_id\n context["reply_to"] = message.reply_to\n \n await self.submit_a2a_task(\n target_agent_name=target_agent,\n a2a_parts=parts,\n external_request_context=context,\n user_identity=user_identity\n )\n \n except Exception as e:\n log.exception("%s Error processing message: %s", self.log_identifier, e)\n \n async def _send_final_response_to_external(self, context: Dict[str, Any], task_data: Task):\n """Send response back to reply queue."""\n reply_to = context.get("reply_to")\n if reply_to and self.channel:\n response = {\n "task_id": task_data.id,\n "status": task_data.status.state.value if task_data.status else "unknown",\n "result": self._extract_text_from_task(task_data)\n }\n \n await self.channel.default_exchange.publish(\n aio_pika.Message(json.dumps(response).encode()),\n routing_key=reply_to\n )\n'})}),"\n",(0,i.jsx)(n.h2,{id:"packaging-as-a-plugin",children:"Packaging as a Plugin"}),"\n",(0,i.jsx)(n.p,{children:"For distribution and reusability, package your gateway as a plugin:"}),"\n",(0,i.jsx)(n.h3,{id:"1-create-plugin-structure",children:"1. Create Plugin Structure"}),"\n",(0,i.jsxs)(n.p,{children:["The following structure is created when running the ",(0,i.jsx)(n.code,{children:"sam plugin create my-gateway-plugin --type gateway"})," command:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{children:"my-gateway-plugin/\n\u251c\u2500\u2500 pyproject.toml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 src/\n\u2502 \u2514\u2500\u2500 sam_my_gateway/\n\u2502 \u251c\u2500\u2500 __init__.py\n\u2502 \u251c\u2500\u2500 app.py\n\u2502 \u251c\u2500\u2500 component.py\n\u251c\u2500\u2500 config.yaml\n\u2514\u2500\u2500 examples/\n \u2514\u2500\u2500 my_gateway_example.yaml\n"})}),"\n",(0,i.jsxs)(n.h3,{id:"2-configure-pyprojecttoml",children:["2. Configure ",(0,i.jsx)(n.code,{children:"pyproject.toml"})]}),"\n",(0,i.jsxs)(n.p,{children:["Update the ",(0,i.jsx)(n.code,{children:"pyproject.toml"})," file to include your gateway dependencies:"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-toml",children:'...\ndependencies = [\n "watchdog>=3.0.0", # Add your specific dependencies\n]\n...\n'})}),"\n",(0,i.jsx)(n.h3,{id:"3-build-and-install",children:"3. Build and Install"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-bash",children:"# Build the plugin\nsam plugin build\n\n# Install plugin from local wheel file\nsam plugin add my-gateway --plugin dist/sam_my_gateway-0.1.0-py3-none-any.whl\n"})}),"\n",(0,i.jsx)(n.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,i.jsx)(n.h3,{id:"common-issues",children:"Common Issues"}),"\n",(0,i.jsx)(n.h4,{id:"gateway-fails-to-start",children:"Gateway Fails to Start"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Check configuration schema validation"}),"\n",(0,i.jsx)(n.li,{children:"Verify all required parameters are provided"}),"\n",(0,i.jsx)(n.li,{children:"Ensure external dependencies are installed"}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"tasks-not-reaching-agents",children:"Tasks Not Reaching Agents"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Verify namespace configuration matches agents"}),"\n",(0,i.jsx)(n.li,{children:"Check Solace broker connectivity"}),"\n",(0,i.jsx)(n.li,{children:"Confirm agent names are correct"}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"authentication-failures",children:"Authentication Failures"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Validate user identity extraction logic"}),"\n",(0,i.jsx)(n.li,{children:"Check authorization service configuration"}),"\n",(0,i.jsx)(n.li,{children:"Verify claims format matches expectations"}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"fileartifact-issues",children:"File/Artifact Issues"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"Ensure artifact service is properly configured"}),"\n",(0,i.jsx)(n.li,{children:"Check file permissions and paths"}),"\n",(0,i.jsx)(n.li,{children:"Verify artifact URI construction"}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"debugging-tips",children:"Debugging Tips"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Enable Debug Logging"}),":"]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-yaml",children:"log:\n stdout_log_level: DEBUG\n log_file_level: DEBUG\n"})}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Use Test Agents"}),":\nCreate simple echo agents for testing gateway integration"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Monitor Solace Topics"}),":\nUse Solace monitoring tools to trace message flow"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"Add Correlation IDs"}),":\nInclude unique identifiers in logs for request tracing"]}),"\n"]}),"\n"]})]})}function _(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>r,x:()=>o});var a=t(6540);const i={},s=a.createContext(i);function r(e){const n=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:r(e.components),a.createElement(s.Provider,{value:n},e.children)}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunksolace_agenitc_mesh_docs=self.webpackChunksolace_agenitc_mesh_docs||[]).push([[1257],{6060:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>l,contentTitle:()=>o,default:()=>h,frontMatter:()=>i,metadata:()=>a,toc:()=>c});const a=JSON.parse('{"id":"documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0","title":"SAM Gateway Migration Guide: Upgrading to the A2A SDK","description":"This guide is for developers who have built or are maintaining a custom Solace Agent Mesh (SAM) gateway. A recent architectural update has aligned SAM with the official Agent-to-Agent (A2A) protocol","source":"@site/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0.md","sourceDirName":"documentation/Migrations/A2A Upgrade To 0.3.0","slug":"/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0","permalink":"/solace-agent-mesh/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0","draft":false,"unlisted":false,"editUrl":"https://github.com/SolaceLabs/solace-agent-mesh/edit/main/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0.md","tags":[],"version":"current","frontMatter":{},"sidebar":"docSidebar","previous":{"title":"RAG Integration","permalink":"/solace-agent-mesh/docs/documentation/tutorials/rag-integration"},"next":{"title":"A2A Technical Migration Map","permalink":"/solace-agent-mesh/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map"}}');var s=n(4848),r=n(8453);const i={},o="SAM Gateway Migration Guide: Upgrading to the A2A SDK",l={},c=[{value:"1. Why the Change?",id:"1-why-the-change",level:2},{value:"2. Core Conceptual Changes",id:"2-core-conceptual-changes",level:2},{value:"The <code>a2a</code> Helper Layer: The New Best Practice",id:"the-a2a-helper-layer-the-new-best-practice",level:3},{value:"Type System Migration",id:"type-system-migration",level:3},{value:"Accessing Object Properties",id:"accessing-object-properties",level:3},{value:"Changes to <code>BaseGatewayComponent</code>",id:"changes-to-basegatewaycomponent",level:3},{value:"3. Practical Migration Checklist",id:"3-practical-migration-checklist",level:2},{value:"4. Code Examples: Before & After",id:"4-code-examples-before--after",level:2},{value:"Example 1: Translating External Input",id:"example-1-translating-external-input",level:3},{value:"Example 2: Sending a Final Response",id:"example-2-sending-a-final-response",level:3}];function d(e){const t={br:"br",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,r.R)(),...e.components},{Details:n}=t;return n||function(e,t){throw new Error("Expected "+(t?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.header,{children:(0,s.jsx)(t.h1,{id:"sam-gateway-migration-guide-upgrading-to-the-a2a-sdk",children:"SAM Gateway Migration Guide: Upgrading to the A2A SDK"})}),"\n",(0,s.jsxs)(t.p,{children:["This guide is for developers who have built or are maintaining a custom Solace Agent Mesh (SAM) gateway. A recent architectural update has aligned SAM with the official Agent-to-Agent (A2A) protocol",(0,s.jsx)(t.br,{}),"\n","specification by adopting the ",(0,s.jsx)(t.code,{children:"a2a-sdk"}),". This migration requires some changes to your gateway code to ensure compatibility."]}),"\n",(0,s.jsx)(t.p,{children:"This document provides a high-level overview of the conceptual changes and a practical checklist to guide you through the upgrade process."}),"\n",(0,s.jsx)(t.h2,{id:"1-why-the-change",children:"1. Why the Change?"}),"\n",(0,s.jsxs)(t.p,{children:["The migration from our legacy A2A implementation to the official ",(0,s.jsx)(t.code,{children:"a2a-sdk"})," is a foundational improvement with several key benefits:"]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"Protocol Compliance:"})," Ensures your gateway is fully interoperable with any A2A-compliant agent or system."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"Standardization:"})," Replaces bespoke code with a community-supported standard, reducing technical debt."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"Improved Maintainability:"})," Insulates your gateway from future A2A specification changes. The ",(0,s.jsx)(t.code,{children:"a2a-sdk"})," will be updated, not your core logic."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"Future-Proofing:"})," Positions your gateway to easily adopt new features as the A2A protocol evolves."]}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"2-core-conceptual-changes",children:"2. Core Conceptual Changes"}),"\n",(0,s.jsx)(t.p,{children:"The upgrade introduces a few key changes in how you interact with A2A objects."}),"\n",(0,s.jsxs)(t.h3,{id:"the-a2a-helper-layer-the-new-best-practice",children:["The ",(0,s.jsx)(t.code,{children:"a2a"})," Helper Layer: The New Best Practice"]}),"\n",(0,s.jsxs)(t.p,{children:["The most significant change is the introduction of a new abstraction layer located at ",(0,s.jsx)(t.code,{children:"solace_agent_mesh.common.a2a"}),"."]}),"\n",(0,s.jsxs)(t.p,{children:["You should ",(0,s.jsxs)(t.strong,{children:["no longer instantiate ",(0,s.jsx)(t.code,{children:"a2a.types"})," models directly"]})," or access their properties by hand. Instead, use the provided helper functions. This layer is designed to simplify development and",(0,s.jsx)(t.br,{}),"\n","protect your code from future SDK changes."]}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Example:"})}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-python",children:'# BEFORE: Direct instantiation and property access \nfrom solace_agent_mesh.common.types import TextPart, Task \nmy_part = TextPart(text="Hello") \ntask_id = my_task.id \n \n# AFTER: Using the a2a helper layer \nfrom solace_agent_mesh.common import a2a \nmy_part = a2a.create_text_part(text="Hello") \ntask_id = a2a.get_task_id(my_task) \n'})}),"\n",(0,s.jsx)(t.h3,{id:"type-system-migration",children:"Type System Migration"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["The legacy types in ",(0,s.jsx)(t.code,{children:"solace_agent_mesh.common.types"})," (like ",(0,s.jsx)(t.code,{children:"A2APart"}),", ",(0,s.jsx)(t.code,{children:"FileContent"}),") are deprecated."]}),"\n",(0,s.jsxs)(t.li,{children:["All A2A models now come from the ",(0,s.jsx)(t.code,{children:"a2a.types"})," library."]}),"\n",(0,s.jsxs)(t.li,{children:["The type hint for a list of message parts has changed from ",(0,s.jsx)(t.code,{children:"List[A2APart]"})," to ",(0,s.jsx)(t.code,{children:"List[ContentPart]"}),". ",(0,s.jsx)(t.code,{children:"ContentPart"})," is a simple alias for the union of ",(0,s.jsx)(t.code,{children:"TextPart"}),", ",(0,s.jsx)(t.code,{children:"DataPart"}),", and ",(0,s.jsx)(t.code,{children:"FilePart"}),"."]}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"accessing-object-properties",children:"Accessing Object Properties"}),"\n",(0,s.jsxs)(t.p,{children:["Field names on many A2A objects have changed. Always use the ",(0,s.jsx)(t.code,{children:"a2a"})," helper functions for safe and future-proof access."]}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Action"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Old Pattern"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"New Pattern (Recommended)"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Get Task ID"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"task.id"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"a2a.get_task_id(task)"})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Get Task Status"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"task.status.state"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"a2a.get_task_status(task)"})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Get Event's Task ID"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"event.id"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"event.task_id"})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Get Message Parts"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"message.parts"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"a2a.get_parts_from_message(message)"})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Get Error Message"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"error.message"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"a2a.get_error_message(error)"})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Get Error Code"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"error.code"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"a2a.get_error_code(error)"})})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Get Error Data"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"error.data"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"a2a.get_error_data(error)"})})]})]})]}),"\n",(0,s.jsxs)(t.h3,{id:"changes-to-basegatewaycomponent",children:["Changes to ",(0,s.jsx)(t.code,{children:"BaseGatewayComponent"})]}),"\n",(0,s.jsxs)(t.p,{children:["The ",(0,s.jsx)(t.code,{children:"BaseGatewayComponent"})," has been significantly improved. It now handles more of the A2A protocol complexity, simplifying gateway implementations."]}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"Artifact Handling:"})," The base class can now automatically handle artifact URIs, converting them to embedded bytes before sending them to your gateway's ",(0,s.jsx)(t.code,{children:"_send_..."})," methods. This is controlled by",(0,s.jsx)(t.br,{}),"\n","the ",(0,s.jsx)(t.code,{children:"resolve_artifact_uris_in_gateway"})," parameter in the constructor."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"Message Publishing:"})," The base class now manages the details of preparing and publishing the A2A request message in ",(0,s.jsx)(t.code,{children:"submit_a2a_task"}),"."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:"Asynchronous Model:"})," The underlying threading model has been removed in favor of a more direct ",(0,s.jsx)(t.code,{children:"asyncio"})," implementation, simplifying the component lifecycle."]}),"\n"]}),"\n",(0,s.jsxs)(t.p,{children:["It is highly recommended to review the latest ",(0,s.jsx)(t.code,{children:"BaseGatewayComponent"})," and re-base your custom gateway on it to inherit these benefits and reduce boilerplate code."]}),"\n",(0,s.jsx)(t.h2,{id:"3-practical-migration-checklist",children:"3. Practical Migration Checklist"}),"\n",(0,s.jsx)(t.p,{children:"Follow these steps to update your gateway code."}),"\n",(0,s.jsxs)(t.ol,{children:["\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Update Imports:"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Remove imports from ",(0,s.jsx)(t.code,{children:"solace_agent_mesh.common.types"}),"."]}),"\n",(0,s.jsxs)(t.li,{children:["Add imports from ",(0,s.jsx)(t.code,{children:"a2a.types"})," for specific models if needed."]}),"\n",(0,s.jsxs)(t.li,{children:["Add ",(0,s.jsx)(t.code,{children:"from solace_agent_mesh.common import a2a"}),"."]}),"\n",(0,s.jsxs)(t.li,{children:["Add ",(0,s.jsx)(t.code,{children:"from solace_agent_mesh.common.a2a import ContentPart"}),"."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Update Type Hints:"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Find all instances of ",(0,s.jsx)(t.code,{children:"List[A2APart]"})," and change them to ",(0,s.jsx)(t.code,{children:"List[ContentPart]"}),"."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Refactor Object Creation:"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Replace direct model instantiation like ",(0,s.jsx)(t.code,{children:"TextPart(...)"})," or ",(0,s.jsx)(t.code,{children:"FilePart(...)"})," with their corresponding helper functions: ",(0,s.jsx)(t.code,{children:"a2a.create_text_part(...)"}),", ",(0,s.jsx)(t.code,{children:"a2a.create_file_part_from_uri(...)"}),", etc."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Refactor Property Access:"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["Replace direct property access (",(0,s.jsx)(t.code,{children:"task.id"}),", ",(0,s.jsx)(t.code,{children:"error.message"}),") with calls to the ",(0,s.jsx)(t.code,{children:"a2a"})," helper functions (",(0,s.jsx)(t.code,{children:"a2a.get_task_id(task)"}),", ",(0,s.jsx)(t.code,{children:"a2a.get_error_message(error)"}),")."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Review Base Class Integration:"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:["If you have a custom gateway, compare it against the latest ",(0,s.jsx)(t.code,{children:"BaseGatewayComponent"}),". Consider refactoring to delegate more responsibility (like artifact handling and message submission) to the",(0,s.jsx)(t.br,{}),"\n","base class."]}),"\n"]}),"\n"]}),"\n",(0,s.jsxs)(t.li,{children:["\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Test Thoroughly:"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"Once refactored, run your integration tests to ensure the gateway correctly translates inputs and processes outputs in the new format."}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,s.jsx)(t.h2,{id:"4-code-examples-before--after",children:"4. Code Examples: Before & After"}),"\n",(0,s.jsx)(t.p,{children:"Here are some common patterns you will encounter during the migration."}),"\n",(0,s.jsx)(t.h3,{id:"example-1-translating-external-input",children:"Example 1: Translating External Input"}),"\n",(0,s.jsx)(t.p,{children:"This example shows how to create A2A parts from an external event."}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:(0,s.jsx)("strong",{children:"_translate_external_input"})}),(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Before:"})}),(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-python",children:'from solace_agent_mesh.common.types import Part as A2APart, TextPart, FilePart, FileContent \n \nasync def _translate_external_input(self, external_event: Any) -> Tuple[str, List[A2APart], Dict[str, Any]]: \n # ... \n a2a_parts: List[A2APart] = [] \n \n # Create a file part with a URI \n uri = "artifact://..." \n a2a_parts.append( \n FilePart( \n file=FileContent(name="report.pdf", uri=uri) \n ) \n ) \n \n # Create a text part \n prompt = "Summarize the attached file." \n a2a_parts.append(TextPart(text=prompt)) \n \n return "summary_agent", a2a_parts, {} \n'})}),(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"After:"})}),(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-python",children:'from solace_agent_mesh.common import a2a \nfrom solace_agent_mesh.common.a2a import ContentPart \n \nasync def _translate_external_input(self, external_event: Any) -> Tuple[str, List[ContentPart], Dict[str, Any]]: \n # ... \n a2a_parts: List[ContentPart] = [] \n \n # Create a file part with a URI using the helper \n uri = "artifact://..." \n file_part = a2a.create_file_part_from_uri(uri=uri, name="report.pdf") \n a2a_parts.append(file_part) \n \n # Create a text part using the helper \n prompt = "Summarize the attached file." \n text_part = a2a.create_text_part(text=prompt) \n a2a_parts.append(text_part) \n \n return "summary_agent", a2a_parts, {} \n'})})]}),"\n",(0,s.jsx)(t.h3,{id:"example-2-sending-a-final-response",children:"Example 2: Sending a Final Response"}),"\n",(0,s.jsxs)(t.p,{children:["This example shows how to process a final ",(0,s.jsx)(t.code,{children:"Task"})," object."]}),"\n",(0,s.jsxs)(n,{children:[(0,s.jsx)("summary",{children:(0,s.jsx)("strong",{children:"_send_final_response_to_external"})}),(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Before:"})}),(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-python",children:'from solace_agent_mesh.common.types import Task, TaskState, TextPart \n \nasync def _send_final_response_to_external(self, context: Dict, task_data: Task) -> None: \n task_id = task_data.id \n final_status_text = ":checkered_flag: Task complete." \n \n if task_data.status.state == TaskState.FAILED: \n error_message_text = "" \n if task_data.status.message and task_data.status.message.parts: \n for part in task_data.status.message.parts: \n if isinstance(part, TextPart): \n error_message_text = part.text \n break \n final_status_text = f":x: Error: Task failed. {error_message_text}".strip() \n \n # ... use final_status_text and task_id \n'})}),(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"After:"})}),(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-python",children:'from solace_agent_mesh.common import a2a \nfrom a2a.types import Task, TaskState \n \nasync def _send_final_response_to_external(self, context: Dict, task_data: Task) -> None: \n # Use helpers to safely access properties \n task_id = a2a.get_task_id(task_data) \n task_status = a2a.get_task_status(task_data) \n \n final_status_text = ":checkered_flag: Task complete." \n \n if task_status == TaskState.failed: \n error_message_text = "" \n if task_data.status and task_data.status.message: \n # Use helper to extract all text from the message \n error_message_text = a2a.get_text_from_message(task_data.status.message) \n final_status_text = f":x: Error: Task failed. {error_message_text}".strip() \n \n # ... use final_status_text and task_id \n'})})]})]})}function h(e={}){const{wrapper:t}={...(0,r.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>i,x:()=>o});var a=n(6540);const s={},r=a.createContext(s);function i(e){const t=a.useContext(r);return a.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:i(e.components),a.createElement(r.Provider,{value:t},e.children)}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunksolace_agenitc_mesh_docs=self.webpackChunksolace_agenitc_mesh_docs||[]).push([[8055],{5343:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>s,default:()=>h,frontMatter:()=>o,metadata:()=>i,toc:()=>c});const i=JSON.parse('{"id":"documentation/tutorials/rag-integration","title":"RAG Integration","description":"This tutorial guides you through setting up and configuring Solace Agent Mesh (SAM) Retrieval Augmented Generation (RAG) plugin. The RAG plugin enables your agents to answer questions by retrieving information from a knowledge base of your documents.","source":"@site/docs/documentation/tutorials/rag-integration.md","sourceDirName":"documentation/tutorials","slug":"/documentation/tutorials/rag-integration","permalink":"/solace-agent-mesh/docs/documentation/tutorials/rag-integration","draft":false,"unlisted":false,"editUrl":"https://github.com/SolaceLabs/solace-agent-mesh/edit/main/docs/docs/documentation/tutorials/rag-integration.md","tags":[],"version":"current","sidebarPosition":70,"frontMatter":{"title":"RAG Integration","sidebar_position":70,"toc_max_heading_level":4},"sidebar":"docSidebar","previous":{"title":"Slack Integration","permalink":"/solace-agent-mesh/docs/documentation/tutorials/slack-integration"},"next":{"title":"SAM Gateway Migration Guide: Upgrading to the A2A SDK","permalink":"/solace-agent-mesh/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0"}}');var a=t(4848),r=t(8453);const o={title:"RAG Integration",sidebar_position:70,toc_max_heading_level:4},s="RAG Integration",l={},c=[{value:"What is SAM RAG?",id:"what-is-sam-rag",level:2},{value:"Prerequisites",id:"prerequisites",level:2},{value:"Adding the RAG Plugin",id:"adding-the-rag-plugin",level:2},{value:"Configuring the RAG Agent",id:"configuring-the-rag-agent",level:2},{value:"Shared Configuration",id:"shared-configuration",level:3},{value:"RAG Pipeline Configuration",id:"rag-pipeline-configuration",level:3},{value:"1. Scanner Configuration",id:"1-scanner-configuration",level:4},{value:"2. Preprocessor Configuration",id:"2-preprocessor-configuration",level:4},{value:"3. Splitter Configuration",id:"3-splitter-configuration",level:4},{value:"4. Embedding Configuration",id:"4-embedding-configuration",level:4},{value:"5. Vector Database Configuration",id:"5-vector-database-configuration",level:4},{value:"6. LLM Configuration",id:"6-llm-configuration",level:4},{value:"7. Retrieval Configuration",id:"7-retrieval-configuration",level:4},{value:"Environment Variables",id:"environment-variables",level:3},{value:"Running the RAG Agent",id:"running-the-rag-agent",level:2},{value:"Testing the RAG Agent",id:"testing-the-rag-agent",level:2},{value:"Ingesting Documents",id:"ingesting-documents",level:3},{value:"Option 1: Automatic Scanning (Batch Ingestion)",id:"option-1-automatic-scanning-batch-ingestion",level:4},{value:"Option 2: Manual Upload via Gateway",id:"option-2-manual-upload-via-gateway",level:4},{value:"Querying the Knowledge Base",id:"querying-the-knowledge-base",level:3},{value:"Troubleshooting",id:"troubleshooting",level:2}];function d(e){const n={a:"a",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,r.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.header,{children:(0,a.jsx)(n.h1,{id:"rag-integration",children:"RAG Integration"})}),"\n",(0,a.jsx)(n.p,{children:"This tutorial guides you through setting up and configuring Solace Agent Mesh (SAM) Retrieval Augmented Generation (RAG) plugin. The RAG plugin enables your agents to answer questions by retrieving information from a knowledge base of your documents."}),"\n",(0,a.jsx)(n.h2,{id:"what-is-sam-rag",children:"What is SAM RAG?"}),"\n",(0,a.jsx)(n.p,{children:"The SAM RAG plugin enhances your agents with the ability to perform retrieval-augmented generation. This means the agent can:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Scan"})," documents from various sources (local filesystem, Google Drive, etc.)."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Preprocess"})," and ",(0,a.jsx)(n.strong,{children:"split"})," the text into manageable chunks."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Embed"})," these chunks into vectors and store them in a vector database."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Retrieve"})," relevant chunks of text based on a user's query."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Generate"})," an answer using a large language model (LLM) augmented with the retrieved information."]}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"This allows you to build agents that can answer questions about your own private data."}),"\n",(0,a.jsx)(n.h2,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,a.jsx)(n.p,{children:"Before you begin, ensure you have:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/getting-started/installation",children:"Installed Solace Agent Mesh and the SAM CLI"}),"."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.a,{href:"/solace-agent-mesh/docs/documentation/getting-started/quick-start",children:"Created a new Solace Agent Mesh project"}),"."]}),"\n",(0,a.jsx)(n.li,{children:"Access to a vector database (for example, Qdrant, Chroma, and Pinecone)."}),"\n",(0,a.jsx)(n.li,{children:"Access to an LLM for generation and an embedding model."}),"\n",(0,a.jsx)(n.li,{children:"A directory with some documents for the agent to ingest."}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"adding-the-rag-plugin",children:"Adding the RAG Plugin"}),"\n",(0,a.jsx)(n.p,{children:"To add the RAG plugin to your SAM project, run the following command:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-sh",children:"sam plugin add my-rag-agent --plugin sam-rag\n"})}),"\n",(0,a.jsxs)(n.p,{children:["Replace ",(0,a.jsx)(n.code,{children:"my-rag-agent"})," with your preferred agent name. This command:"]}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["Installs the ",(0,a.jsx)(n.code,{children:"sam-rag"})," plugin."]}),"\n",(0,a.jsxs)(n.li,{children:["Creates a new agent configuration file at ",(0,a.jsx)(n.code,{children:"configs/agents/my-rag-agent.yaml"}),"."]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"configuring-the-rag-agent",children:"Configuring the RAG Agent"}),"\n",(0,a.jsxs)(n.p,{children:["The RAG agent requires a detailed configuration. Open ",(0,a.jsx)(n.code,{children:"configs/agents/my-rag-agent.yaml"})," to configure the following sections:"]}),"\n",(0,a.jsx)(n.h3,{id:"shared-configuration",children:"Shared Configuration"}),"\n",(0,a.jsxs)(n.p,{children:["Like other agents, the RAG agent needs a connection to the Solace broker and a configured LLM. This is typically done in a ",(0,a.jsx)(n.code,{children:"shared_config.yaml"})," file."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:"# configs/shared_config.yaml\nshared_config:\n - broker_connection: &broker_connection\n dev_mode: ${SOLACE_DEV_MODE, false}\n broker_url: ${SOLACE_BROKER_URL, ws://localhost:8080}\n broker_username: ${SOLACE_BROKER_USERNAME, default}\n broker_password: ${SOLACE_BROKER_PASSWORD, default}\n broker_vpn: ${SOLACE_BROKER_VPN, default}\n temporary_queue: ${USE_TEMPORARY_QUEUES, true}\n\n - models:\n general: &general_model\n model: ${LLM_SERVICE_GENERAL_MODEL_NAME}\n api_base: ${LLM_SERVICE_ENDPOINT}\n api_key: ${LLM_SERVICE_API_KEY}\n"})}),"\n",(0,a.jsx)(n.h3,{id:"rag-pipeline-configuration",children:"RAG Pipeline Configuration"}),"\n",(0,a.jsxs)(n.p,{children:["The RAG pipeline has several stages, each with its own configuration block within the ",(0,a.jsx)(n.code,{children:"app_config"})," section of your ",(0,a.jsx)(n.code,{children:"my-rag-agent.yaml"})," file."]}),"\n",(0,a.jsx)(n.h4,{id:"1-scanner-configuration",children:"1. Scanner Configuration"}),"\n",(0,a.jsx)(n.p,{children:"The scanner discovers documents to be ingested. You can configure it to scan a local filesystem or cloud sources."}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Local Filesystem Example:"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:'scanner:\n batch: true\n use_memory_storage: true\n source:\n type: filesystem\n directories:\n - "/path/to/your/documents" # Important: Replace with your actual document directory path\n filters:\n file_formats:\n - ".txt"\n - ".pdf"\n - ".md"\n'})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Multi-Cloud Source Example:"}),"\nYou can also configure multiple sources, including Google Drive, OneDrive, and S3."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:'scanner:\n batch: true\n use_memory_storage: true\n sources:\n - type: filesystem\n directories: ["${LOCAL_DOCUMENTS_PATH}"]\n - type: google_drive\n credentials_path: "${GOOGLE_DRIVE_CREDENTIALS_PATH}"\n folders:\n - folder_id: "${GOOGLE_DRIVE_FOLDER_ID_1}"\n name: "Documents"\n recursive: true\n'})}),"\n",(0,a.jsx)(n.h4,{id:"2-preprocessor-configuration",children:"2. Preprocessor Configuration"}),"\n",(0,a.jsx)(n.p,{children:"The preprocessor cleans the text extracted from documents."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:"preprocessor:\n default_preprocessor:\n type: enhanced\n params:\n lowercase: true\n normalize_whitespace: true\n remove_urls: true\n preprocessors:\n pdf: \n type: document\n params:\n lowercase: true\n normalize_whitespace: true\n remove_non_ascii: true\n remove_urls: true\n"})}),"\n",(0,a.jsx)(n.h4,{id:"3-splitter-configuration",children:"3. Splitter Configuration"}),"\n",(0,a.jsx)(n.p,{children:"The splitter breaks down large documents into smaller chunks. Different splitters are available for different file types."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:"splitter:\n default_splitter:\n type: recursive_character\n params:\n chunk_size: 2048\n chunk_overlap: 400\n splitters:\n markdown:\n type: markdown\n params:\n chunk_size: 2048\n chunk_overlap: 400\n pdf:\n type: token\n params:\n chunk_size: 500\n chunk_overlap: 100\n"})}),"\n",(0,a.jsx)(n.h4,{id:"4-embedding-configuration",children:"4. Embedding Configuration"}),"\n",(0,a.jsx)(n.p,{children:"This section defines the model used to create vector embeddings from the text chunks."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:'embedding:\n embedder_type: "openai"\n embedder_params:\n model: "${OPENAI_EMBEDDING_MODEL}"\n api_key: "${OPENAI_API_KEY}"\n api_base: "${OPENAI_API_ENDPOINT}"\n normalize_embeddings: true\n'})}),"\n",(0,a.jsx)(n.h4,{id:"5-vector-database-configuration",children:"5. Vector Database Configuration"}),"\n",(0,a.jsx)(n.p,{children:"Configure the connection to your vector database where the embeddings are stored."}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Qdrant Example:"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:'vector_db:\n db_type: "qdrant"\n db_params:\n url: "${QDRANT_URL}"\n api_key: "${QDRANT_API_KEY}"\n collection_name: "${QDRANT_COLLECTION}"\n embedding_dimension: ${QDRANT_EMBEDDING_DIMENSION}\n'})}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Chroma Example:"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:'vector_db:\n db_type: "chroma"\n db_params:\n host: "${CHROMA_HOST}"\n port: "${CHROMA_PORT}"\n collection_name: "${CHROMA_COLLECTION}"\n'})}),"\n",(0,a.jsx)(n.h4,{id:"6-llm-configuration",children:"6. LLM Configuration"}),"\n",(0,a.jsx)(n.p,{children:"Configure the LLM that is used to generate answers based on the retrieved context."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:'llm:\n load_balancer:\n - model_name: "gpt-4o"\n litellm_params:\n model: "openai/${OPENAI_MODEL_NAME}"\n api_key: "${OPENAI_API_KEY}"\n api_base: "${OPENAI_API_ENDPOINT}"\n'})}),"\n",(0,a.jsx)(n.h4,{id:"7-retrieval-configuration",children:"7. Retrieval Configuration"}),"\n",(0,a.jsx)(n.p,{children:"This defines how many document chunks are retrieved to answer a query."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-yaml",children:"retrieval:\n top_k: 7\n"})}),"\n",(0,a.jsx)(n.h3,{id:"environment-variables",children:"Environment Variables"}),"\n",(0,a.jsxs)(n.p,{children:["The RAG agent relies heavily on environment variables. Here are some of the most important ones you'll need to set in your ",(0,a.jsx)(n.code,{children:".env"})," file:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-bash",children:'# Solace Connection\nSOLACE_BROKER_URL=ws://localhost:8080\nSOLACE_BROKER_VPN=default\nSOLACE_BROKER_USERNAME=default\nSOLACE_BROKER_PASSWORD=default\nNAMESPACE=my-org/dev\n\n# LLM and Embedding Models\nOPENAI_API_KEY="your-openai-api-key"\nOPENAI_API_ENDPOINT="your-openai-api-endpoint"\nOPENAI_MODEL_NAME="model name. E.g., gpt-4o"\nOPENAI_EMBEDDING_MODEL="embedding model name. E.g., text-embedding-3-small"\n\n# Vector Database (Qdrant example)\nQDRANT_URL="Qdrant url"\nQDRANT_API_KEY="API key"\nQDRANT_COLLECTION="my-rag-collection"\nQDRANT_EMBEDDING_DIMENSION=1536 # Depends on your embedding model\n\n# Scanner\nLOCAL_DOCUMENTS_PATH="./my_documents" # Relative path to your documents folder\n'})}),"\n",(0,a.jsxs)(n.p,{children:["Create a directory named ",(0,a.jsx)(n.code,{children:"my_documents"})," in your project root and place some text or markdown files inside it."]}),"\n",(0,a.jsx)(n.h2,{id:"running-the-rag-agent",children:"Running the RAG Agent"}),"\n",(0,a.jsx)(n.p,{children:"Once you have configured your agent and set the environment variables, you can run it:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-sh",children:"sam run configs/agents/my-rag-agent.yaml\n"})}),"\n",(0,a.jsx)(n.p,{children:"When the agent starts, it begins scanning the documents in the configured source, processing and ingesting them into your vector database. This process may take some time, depending on the number and size of your documents."}),"\n",(0,a.jsx)(n.h2,{id:"testing-the-rag-agent",children:"Testing the RAG Agent"}),"\n",(0,a.jsx)(n.p,{children:"Once your agent is running, you can test its retrieval capabilities and ingest new documents."}),"\n",(0,a.jsx)(n.h3,{id:"ingesting-documents",children:"Ingesting Documents"}),"\n",(0,a.jsx)(n.p,{children:"There are two primary ways to ingest documents into your RAG agent's knowledge base:"}),"\n",(0,a.jsx)(n.h4,{id:"option-1-automatic-scanning-batch-ingestion",children:"Option 1: Automatic Scanning (Batch Ingestion)"}),"\n",(0,a.jsxs)(n.p,{children:["This method uses the configured ",(0,a.jsx)(n.code,{children:"scanner"})," component. The agent automatically ingests documents from the directories specified in your configuration upon startup."]}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Step 1: Create a Document"})}),"\n",(0,a.jsxs)(n.p,{children:["First, create a simple text file named ",(0,a.jsx)(n.code,{children:"sam_features.txt"})," and add some content to it. For example:"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-text",children:"Solace Agent Mesh (SAM) is a powerful framework for building AI agents.\nKey features of SAM include:\n- A flexible plugin architecture.\n- Integration with various LLMs and vector databases.\n- Scalable gateways for different communication protocols.\n- An event-driven design based on Solace PubSub+.\n"})}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Step 2: Place the Document in the Scanned Directory"})}),"\n",(0,a.jsxs)(n.p,{children:['In the "Environment Variables" section, we configured ',(0,a.jsx)(n.code,{children:"LOCAL_DOCUMENTS_PATH"})," to point to a directory (e.g., ",(0,a.jsx)(n.code,{children:"./my_documents"}),")."]}),"\n",(0,a.jsxs)(n.p,{children:["Create this directory in your project's root folder if you haven't already, and move your ",(0,a.jsx)(n.code,{children:"sam_features.txt"})," file into it."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-sh",children:"mkdir -p my_documents\nmv sam_features.txt my_documents/\n"})}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Step 3: Run the Agent to Trigger Ingestion"})}),"\n",(0,a.jsx)(n.p,{children:"If your agent is already running, you'll need to restart it to trigger the batch scan. If it's not running, start it now:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-sh",children:"sam run configs/agents/my-rag-agent.yaml\n"})}),"\n",(0,a.jsx)(n.p,{children:"You will see logs indicating that the file is being processed. Once the agent is running and the initial scan is complete, the document is successfully ingested and ready for retrieval."}),"\n",(0,a.jsx)(n.h4,{id:"option-2-manual-upload-via-gateway",children:"Option 2: Manual Upload via Gateway"}),"\n",(0,a.jsx)(n.p,{children:"You can also ingest documents dynamically by uploading them directly through a gateway, like the Web UI. This is useful for adding single documents without restarting the agent. The RAG agent exposes a tool for this purpose."}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Step 1: Start the RAG Agent and Web UI"})}),"\n",(0,a.jsx)(n.p,{children:"Ensure both your RAG agent and the Web UI gateway are running."}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Step 2: Upload a Document in the Web UI"})}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsxs)(n.li,{children:["Open the Web UI (usually at ",(0,a.jsx)(n.a,{href:"http://localhost:8000",children:"http://localhost:8000"}),", or check your gateway configuration for the correct URL) and start a chat with your RAG agent."]}),"\n",(0,a.jsx)(n.li,{children:"Use the file attachment button to select a document from your local machine."}),"\n",(0,a.jsxs)(n.li,{children:["Send a prompt along with the file, instructing the agent to ingest it. For example:","\n",(0,a.jsxs)(n.blockquote,{children:["\n",(0,a.jsx)(n.p,{children:'"Please ingest the attached document into your knowledge base."'}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,a.jsxs)(n.p,{children:["The RAG agent uses its ",(0,a.jsx)(n.code,{children:"built-in"})," ingest_document tool to process the file you uploaded. The file goes through the same preprocessing, splitting, and embedding pipeline as the documents from the automatic scan."]}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Step 3: Confirm Ingestion"})}),"\n",(0,a.jsx)(n.p,{children:"After the agent confirms that the document has been ingested, you can immediately ask questions about its content."}),"\n",(0,a.jsx)(n.h3,{id:"querying-the-knowledge-base",children:"Querying the Knowledge Base"}),"\n",(0,a.jsx)(n.p,{children:"You can interact with your RAG agent through any gateway, such as the Web UI gateway."}),"\n",(0,a.jsxs)(n.ol,{children:["\n",(0,a.jsx)(n.li,{children:"Make sure you have a Web UI gateway running (or add one to your project)."}),"\n",(0,a.jsxs)(n.li,{children:["Open the Web UI (usually at ",(0,a.jsx)(n.code,{children:"http://localhost:8000"}),")."]}),"\n",(0,a.jsxs)(n.li,{children:["Start a conversation with ",(0,a.jsx)(n.code,{children:"my-rag-agent"}),"."]}),"\n",(0,a.jsx)(n.li,{children:"Ask a question related to the content of the documents you provided during the initial scan."}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"For example, if you have a document about product features, you could ask:"}),"\n",(0,a.jsxs)(n.blockquote,{children:["\n",(0,a.jsx)(n.p,{children:'"What are the key features of Product X?"'}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"The agent searches its knowledge base, finds the relevant information, and generates an answer based on the content of your documents."}),"\n",(0,a.jsx)(n.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Connection Errors"}),": Double-check all your URLs, API keys, and credentials for your LLM and vector database."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Ingestion Issues"}),": Check the agent logs for errors during the scanning, preprocessing, or embedding stages. Ensure the file formats are supported and the paths are correct."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"No Answers"}),": If the agent can't answer, it might be because the information is not in the documents, or the ",(0,a.jsx)(n.code,{children:"top_k"})," retrieval setting is too low."]}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>s});var i=t(6540);const a={},r=i.createContext(a);function o(e){const n=i.useContext(r);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function s(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:o(e.components),i.createElement(r.Provider,{value:n},e.children)}}}]);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";(self.webpackChunksolace_agenitc_mesh_docs=self.webpackChunksolace_agenitc_mesh_docs||[]).push([[6719],{1786:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>a,default:()=>h,frontMatter:()=>i,metadata:()=>o,toc:()=>c});const o=JSON.parse('{"id":"documentation/user-guide/creating-python-tools","title":"Creating Python Tools","description":"Solace Agent Mesh (SAM) provides a powerful and unified system for creating custom agent tools using Python. This is the primary way to extend an agent\'s capabilities with your own business logic, integrate with proprietary APIs, or perform specialized data processing.","source":"@site/docs/documentation/user-guide/creating-python-tools.md","sourceDirName":"documentation/user-guide","slug":"/documentation/user-guide/creating-python-tools","permalink":"/solace-agent-mesh/docs/documentation/user-guide/creating-python-tools","draft":false,"unlisted":false,"editUrl":"https://github.com/SolaceLabs/solace-agent-mesh/edit/main/docs/docs/documentation/user-guide/creating-python-tools.md","tags":[],"version":"current","sidebarPosition":35,"frontMatter":{"title":"Creating Python Tools","sidebar_position":35},"sidebar":"docSidebar","previous":{"title":"Create Agents","permalink":"/solace-agent-mesh/docs/documentation/user-guide/create-agents"},"next":{"title":"Create Gateways","permalink":"/solace-agent-mesh/docs/documentation/user-guide/create-gateways"}}');var s=t(4848),r=t(8453);const i={title:"Creating Python Tools",sidebar_position:35},a="Creating Python Tools",l={},c=[{value:"Tool Creation Patterns",id:"tool-creation-patterns",level:2},{value:"Pattern 1: Simple Function-Based Tools",id:"pattern-1-simple-function-based-tools",level:2},{value:"Step 1: Write the Tool Function",id:"step-1-write-the-tool-function",level:3},{value:"Step 2: Configure the Tool",id:"step-2-configure-the-tool",level:3},{value:"Pattern 2: Advanced Single-Class Tools",id:"pattern-2-advanced-single-class-tools",level:2},{value:"Step 1: Create the <code>DynamicTool</code> Class",id:"step-1-create-the-dynamictool-class",level:3},{value:"Step 2: Configure the Tool",id:"step-2-configure-the-tool-1",level:3},{value:"Pattern 3: The Tool Provider Factory",id:"pattern-3-the-tool-provider-factory",level:2},{value:"Step 1: Create the Provider and Tools",id:"step-1-create-the-provider-and-tools",level:3},{value:"Step 2: Configure the Provider",id:"step-2-configure-the-provider",level:3}];function d(e){const n={code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,r.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(n.header,{children:(0,s.jsx)(n.h1,{id:"creating-python-tools",children:"Creating Python Tools"})}),"\n",(0,s.jsx)(n.p,{children:"Solace Agent Mesh (SAM) provides a powerful and unified system for creating custom agent tools using Python. This is the primary way to extend an agent's capabilities with your own business logic, integrate with proprietary APIs, or perform specialized data processing."}),"\n",(0,s.jsxs)(n.p,{children:["This guide covers the different patterns for creating custom tools, all of which are configured using the versatile ",(0,s.jsx)(n.code,{children:"tool_type: python"}),"."]}),"\n",(0,s.jsx)(n.h2,{id:"tool-creation-patterns",children:"Tool Creation Patterns"}),"\n",(0,s.jsx)(n.p,{children:"There are three primary patterns for creating Python tools, ranging from simple to advanced. You can choose the best pattern for your needs, and even mix and match them within the same project."}),"\n",(0,s.jsxs)(n.table,{children:[(0,s.jsx)(n.thead,{children:(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.th,{children:"Pattern"}),(0,s.jsx)(n.th,{children:"Best For"}),(0,s.jsx)(n.th,{children:"Key Feature"})]})}),(0,s.jsxs)(n.tbody,{children:[(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsx)(n.strong,{children:"Function-Based"})}),(0,s.jsx)(n.td,{children:"Simple, self-contained tools with static inputs."}),(0,s.jsx)(n.td,{children:"Quick and easy; uses function signature."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsxs)(n.strong,{children:["Single ",(0,s.jsx)(n.code,{children:"DynamicTool"})," Class"]})}),(0,s.jsx)(n.td,{children:"Tools that require complex logic or a programmatically defined interface."}),(0,s.jsx)(n.td,{children:"Full control over the tool's definition."})]}),(0,s.jsxs)(n.tr,{children:[(0,s.jsx)(n.td,{children:(0,s.jsxs)(n.strong,{children:[(0,s.jsx)(n.code,{children:"DynamicToolProvider"})," Class"]})}),(0,s.jsx)(n.td,{children:"Generating multiple related tools from a single, configurable source."}),(0,s.jsx)(n.td,{children:"Maximum scalability and code reuse."})]})]})]}),"\n",(0,s.jsxs)(n.p,{children:["All three patterns are configured in your agent's YAML file under the ",(0,s.jsx)(n.code,{children:"tools"})," list with ",(0,s.jsx)(n.code,{children:"tool_type: python"}),"."]}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsx)(n.h2,{id:"pattern-1-simple-function-based-tools",children:"Pattern 1: Simple Function-Based Tools"}),"\n",(0,s.jsxs)(n.p,{children:["This is the most straightforward way to create a custom tool. You define a standard Python ",(0,s.jsx)(n.code,{children:"async"})," function, and SAM automatically introspects its signature and docstring to create the tool definition for the LLM."]}),"\n",(0,s.jsx)(n.h3,{id:"step-1-write-the-tool-function",children:"Step 1: Write the Tool Function"}),"\n",(0,s.jsxs)(n.p,{children:["Create a Python file (e.g., ",(0,s.jsx)(n.code,{children:"src/my_agent/tools.py"}),") and define your tool."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'# src/my_agent/tools.py\nfrom typing import Any, Dict, Optional\nfrom google.adk.tools import ToolContext\n\nasync def greet_user(\n name: str,\n tool_context: Optional[ToolContext] = None,\n tool_config: Optional[Dict[str, Any]] = None\n) -> Dict[str, Any]:\n """\n Greets a user with a personalized message.\n\n Args:\n name: The name of the person to greet.\n\n Returns:\n A dictionary with the greeting message.\n """\n greeting_prefix = "Hello"\n if tool_config:\n greeting_prefix = tool_config.get("greeting_prefix", "Hello")\n\n greeting_message = f"{greeting_prefix}, {name}! Welcome to Solace Agent Mesh!"\n\n return {\n "status": "success",\n "message": greeting_message\n }\n'})}),"\n",(0,s.jsx)(n.p,{children:(0,s.jsx)(n.strong,{children:"Key Requirements:"})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:["The function must be ",(0,s.jsx)(n.code,{children:"async def"}),"."]}),"\n",(0,s.jsxs)(n.li,{children:["The function's docstring is used as the tool's ",(0,s.jsx)(n.code,{children:"description"})," for the LLM."]}),"\n",(0,s.jsxs)(n.li,{children:["Type hints (",(0,s.jsx)(n.code,{children:"str"}),", ",(0,s.jsx)(n.code,{children:"int"}),", ",(0,s.jsx)(n.code,{children:"bool"}),") are used to generate the parameter schema."]}),"\n",(0,s.jsxs)(n.li,{children:["The function should accept ",(0,s.jsx)(n.code,{children:"tool_context"})," and ",(0,s.jsx)(n.code,{children:"tool_config"})," as optional keyword arguments to receive framework context and YAML configuration."]}),"\n"]}),"\n",(0,s.jsx)(n.h3,{id:"step-2-configure-the-tool",children:"Step 2: Configure the Tool"}),"\n",(0,s.jsxs)(n.p,{children:["In your agent's YAML configuration, add a ",(0,s.jsx)(n.code,{children:"tool_type: python"})," block and point it to your function."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'# In your agent\'s app_config:\ntools:\n - tool_type: python\n component_module: "my_agent.tools"\n function_name: "greet_user"\n tool_config:\n greeting_prefix: "Greetings"\n'})}),"\n",(0,s.jsxs)(n.ul,{children:["\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"component_module"}),": The Python module path to your tools file."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"function_name"}),": The exact name of the function to load."]}),"\n",(0,s.jsxs)(n.li,{children:[(0,s.jsx)(n.code,{children:"tool_config"}),": An optional dictionary passed to your tool at runtime."]}),"\n"]}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsx)(n.h2,{id:"pattern-2-advanced-single-class-tools",children:"Pattern 2: Advanced Single-Class Tools"}),"\n",(0,s.jsxs)(n.p,{children:["For tools that require more complex logic\u2014such as defining their interface programmatically based on configuration\u2014you can use a class that inherits from ",(0,s.jsx)(n.code,{children:"DynamicTool"}),"."]}),"\n",(0,s.jsxs)(n.h3,{id:"step-1-create-the-dynamictool-class",children:["Step 1: Create the ",(0,s.jsx)(n.code,{children:"DynamicTool"})," Class"]}),"\n",(0,s.jsxs)(n.p,{children:["Instead of a function, define a class that implements the ",(0,s.jsx)(n.code,{children:"DynamicTool"})," abstract base class."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'# src/my_agent/tools.py\nfrom typing import Optional, Dict, Any\nfrom google.genai import types as adk_types\nfrom solace_agent_mesh.agent.tools.dynamic_tool import DynamicTool\n\nclass WeatherTool(DynamicTool):\n """A dynamic tool that fetches current weather information."""\n\n @property\n def tool_name(self) -> str:\n return "get_current_weather"\n\n @property\n def tool_description(self) -> str:\n return "Get the current weather for a specified location."\n\n @property\n def parameters_schema(self) -> adk_types.Schema:\n # Programmatically define the tool\'s parameters\n return adk_types.Schema(\n type=adk_types.Type.OBJECT,\n properties={\n "location": adk_types.Schema(type=adk_types.Type.STRING, description="The city and state/country."),\n "units": adk_types.Schema(type=adk_types.Type.STRING, enum=["celsius", "fahrenheit"], nullable=True),\n },\n required=["location"],\n )\n\n async def _run_async_impl(self, args: Dict[str, Any], **kwargs) -> Dict[str, Any]:\n location = args["location"]\n # Access config via self.tool_config\n api_key = self.tool_config.get("api_key")\n if not api_key:\n return {"status": "error", "message": "API key not configured"}\n # ... implementation to call weather API ...\n return {"status": "success", "weather": "Sunny"}\n'})}),"\n",(0,s.jsx)(n.h3,{id:"step-2-configure-the-tool-1",children:"Step 2: Configure the Tool"}),"\n",(0,s.jsxs)(n.p,{children:["The YAML configuration is very similar. You can either specify the ",(0,s.jsx)(n.code,{children:"class_name"})," or let SAM auto-discover it if it's the only ",(0,s.jsx)(n.code,{children:"DynamicTool"})," in the module."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:"# In your agent's app_config:\ntools:\n - tool_type: python\n component_module: \"my_agent.tools\"\n # class_name: WeatherTool # Optional if it's the only one\n tool_config:\n api_key: ${WEATHER_API_KEY}\n"})}),"\n",(0,s.jsx)(n.hr,{}),"\n",(0,s.jsx)(n.h2,{id:"pattern-3-the-tool-provider-factory",children:"Pattern 3: The Tool Provider Factory"}),"\n",(0,s.jsx)(n.p,{children:"This is the most powerful pattern, designed for generating multiple, related tools from a single module and configuration block. It's perfect for creating toolsets based on external schemas, database tables, or other dynamic sources."}),"\n",(0,s.jsx)(n.h3,{id:"step-1-create-the-provider-and-tools",children:"Step 1: Create the Provider and Tools"}),"\n",(0,s.jsxs)(n.p,{children:["In your tools module, you define your ",(0,s.jsx)(n.code,{children:"DynamicTool"})," classes as before, but you also create a ",(0,s.jsx)(n.strong,{children:"provider"})," class that inherits from ",(0,s.jsx)(n.code,{children:"DynamicToolProvider"}),". This provider acts as a factory."]}),"\n",(0,s.jsxs)(n.p,{children:["You can also use the ",(0,s.jsx)(n.code,{children:"@register_tool"})," decorator on simple functions to have them automatically included by the provider."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-python",children:'# src/my_agent/database_tools.py\nfrom typing import Optional, Dict, Any, List\nfrom google.genai import types as adk_types\nfrom solace_agent_mesh.agent.tools.dynamic_tool import DynamicTool, DynamicToolProvider\n\n# --- Tool Implementations ---\nclass DatabaseQueryTool(DynamicTool):\n # ... (implementation as in previous examples) ...\n pass\n\nclass DatabaseSchemaTool(DynamicTool):\n # ... (implementation as in previous examples) ...\n pass\n\n# --- Tool Provider Implementation ---\nclass DatabaseToolProvider(DynamicToolProvider):\n """A factory that creates all database-related tools."""\n\n # Use a decorator for a simple, function-based tool\n\n def create_tools(self, tool_config: Optional[dict] = None) -> List[DynamicTool]:\n """\n Generates a list of all database tools, passing the shared\n configuration to each one.\n """\n # 1. Create tools from any decorated functions in this module\n tools = self._create_tools_from_decorators(tool_config)\n\n # 2. Programmatically create and add more complex tools\n if tool_config and tool_config.get("connection_string"):\n tools.append(DatabaseQueryTool(tool_config=tool_config))\n tools.append(DatabaseSchemaTool(tool_config=tool_config))\n\n return tools\n\n# NOTE that you must use the decorator outside of any class with the provider\'s class name.\n@DatabaseToolProvider.register_tool\nasync def get_database_server_version(tool_config: dict, **kwargs) -> dict:\n """Returns the version of the connected PostgreSQL server."""\n # ... implementation ...\n return {"version": "PostgreSQL 15.3"}\n\n'})}),"\n",(0,s.jsx)(n.h3,{id:"step-2-configure-the-provider",children:"Step 2: Configure the Provider"}),"\n",(0,s.jsxs)(n.p,{children:["You only need a single YAML block. SAM will automatically detect the ",(0,s.jsx)(n.code,{children:"DynamicToolProvider"})," and use it to load all the tools it generates."]}),"\n",(0,s.jsx)(n.pre,{children:(0,s.jsx)(n.code,{className:"language-yaml",children:'# In your agent\'s app_config:\ntools:\n # This single block loads get_database_server_version,\n # execute_database_query, and get_database_schema.\n - tool_type: python\n component_module: "my_agent.database_tools"\n tool_config:\n connection_string: ${DB_CONNECTION_STRING}\n max_rows: 1000\n'})}),"\n",(0,s.jsx)(n.p,{children:"This approach is incredibly scalable, as one configuration entry can bootstrap an entire suite of dynamically generated tools."})]})}function h(e={}){const{wrapper:n}={...(0,r.R)(),...e.components};return n?(0,s.jsx)(n,{...e,children:(0,s.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>i,x:()=>a});var o=t(6540);const s={},r=o.createContext(s);function i(e){const n=o.useContext(r);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function a(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:i(e.components),o.createElement(r.Provider,{value:n},e.children)}}}]);
|