solace-agent-mesh 1.7.1__py3-none-any.whl → 1.13.2__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/alembic/README +74 -0
- solace_agent_mesh/agent/adk/alembic/env.py +77 -0
- solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
- solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
- solace_agent_mesh/agent/adk/alembic.ini +112 -0
- solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +164 -0
- solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +163 -0
- solace_agent_mesh/agent/adk/callbacks.py +752 -127
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +99 -7
- solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +52 -5
- solace_agent_mesh/agent/adk/mcp_content_processor.py +1 -1
- solace_agent_mesh/agent/adk/models/lite_llm.py +34 -16
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +24 -137
- solace_agent_mesh/agent/adk/runner.py +66 -8
- solace_agent_mesh/agent/adk/schema_migration.py +88 -0
- solace_agent_mesh/agent/adk/services.py +41 -1
- solace_agent_mesh/agent/adk/setup.py +220 -32
- solace_agent_mesh/agent/adk/stream_parser.py +229 -40
- solace_agent_mesh/agent/protocol/event_handlers.py +219 -33
- solace_agent_mesh/agent/proxies/a2a/component.py +572 -75
- solace_agent_mesh/agent/proxies/a2a/config.py +80 -4
- solace_agent_mesh/agent/proxies/base/component.py +188 -22
- solace_agent_mesh/agent/proxies/base/proxy_task_context.py +3 -1
- solace_agent_mesh/agent/sac/app.py +37 -12
- solace_agent_mesh/agent/sac/component.py +322 -52
- solace_agent_mesh/agent/sac/patch_adk.py +8 -16
- solace_agent_mesh/agent/sac/task_execution_context.py +90 -0
- solace_agent_mesh/agent/tools/__init__.py +3 -0
- solace_agent_mesh/agent/tools/audio_tools.py +3 -3
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +698 -24
- solace_agent_mesh/agent/tools/deep_research_tools.py +2161 -0
- solace_agent_mesh/agent/tools/peer_agent_tool.py +82 -15
- solace_agent_mesh/agent/tools/time_tools.py +126 -0
- solace_agent_mesh/agent/tools/tool_config_types.py +54 -2
- solace_agent_mesh/agent/tools/web_search_tools.py +279 -0
- solace_agent_mesh/agent/tools/web_tools.py +125 -17
- solace_agent_mesh/agent/utils/artifact_helpers.py +243 -5
- solace_agent_mesh/agent/utils/context_helpers.py +17 -0
- solace_agent_mesh/assets/docs/404.html +6 -6
- solace_agent_mesh/assets/docs/assets/css/{styles.906a1503.css → styles.8162edfb.css} +1 -1
- solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.e186750d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/{17896441.a5e82f9b.js.LICENSE.txt → 2279.550aa580.js.LICENSE.txt} +6 -0
- solace_agent_mesh/assets/docs/assets/js/240a0364.83e37aa8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.2f0db237.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.7e61915d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.7f7ab1c1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.e53c9b78.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/41adc471.0e95b87c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4667dc50.bf2ad456.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/49eed117.493d6f99.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{509e993c.4c7a1a6d.js → 509e993c.a1fbf45a.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/547e15cc.8e6da617.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5b8d9c11.d4eb37b8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.1ee87753.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/64195356.09dbd087.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/66d4869e.30340bd3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/729898df.7249e9fd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7e294c01.7c5f6906.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8024126c.e3467286.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/81a99df0.7ed65d45.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9bb13469.4523ae20.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a7d42657.a956689d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e563275.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e04b235d.06d23db6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.deb2b62e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.acc800d3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e92d0134.c147a429.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ee0c2fe7.94d0a351.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.cc97854c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.d634009f.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.27bb82a7.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +68 -68
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +50 -50
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +42 -42
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +55 -55
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +75 -75
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/image-tools/index.html +81 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +67 -50
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/research-tools/index.html +136 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +178 -144
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +43 -42
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +20 -18
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +23 -23
- solace_agent_mesh/assets/docs/docs/documentation/components/platform-service/index.html +33 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +45 -45
- solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +98 -112
- solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +208 -125
- solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +28 -28
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +29 -29
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +14 -14
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/index.html +47 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/kubernetes-deployment-guide/index.html +197 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +67 -53
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +17 -17
- solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +38 -38
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +87 -87
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +67 -49
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +17 -17
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +22 -22
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +27 -27
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +135 -135
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +66 -66
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +50 -38
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +86 -86
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +24 -24
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +30 -30
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +44 -44
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +50 -23
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +29 -24
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +21 -21
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +40 -37
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +96 -66
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +181 -181
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +75 -75
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +27 -27
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +44 -44
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +39 -38
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +30 -30
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +18 -18
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/vibe_coding/index.html +62 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +135 -114
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +37 -37
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +14 -14
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +27 -25
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +69 -69
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +72 -72
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +112 -112
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +28 -28
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +42 -42
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +20 -20
- solace_agent_mesh/assets/docs/docs/documentation/migrations/platform-service-split/index.html +85 -0
- solace_agent_mesh/assets/docs/lunr-index-1768329217460.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1768329217460.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/cli/commands/add_cmd/__init__.py +3 -1
- solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +6 -1
- solace_agent_mesh/cli/commands/add_cmd/proxy_cmd.py +100 -0
- solace_agent_mesh/cli/commands/eval_cmd.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +15 -0
- solace_agent_mesh/cli/commands/init_cmd/directory_step.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +30 -3
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +3 -4
- solace_agent_mesh/cli/commands/init_cmd/platform_service_step.py +85 -0
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +16 -3
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +2 -1
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +1 -0
- solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +3 -3
- solace_agent_mesh/cli/commands/run_cmd.py +64 -49
- solace_agent_mesh/cli/commands/tools_cmd.py +315 -0
- solace_agent_mesh/cli/main.py +15 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-tcIFZLis.js → authCallback-KnKMP_vb.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/client-DpBL2stg.js +25 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-Cd498TV2.js +435 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-rSf8Vu29.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CINwxvwV.js → vendor-CGk8Suyh.js} +189 -94
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
- solace_agent_mesh/client/webui/frontend/static/mockServiceWorker.js +336 -0
- solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
- solace_agent_mesh/common/a2a/types.py +1 -1
- solace_agent_mesh/common/agent_registry.py +38 -11
- solace_agent_mesh/common/data_parts.py +124 -0
- solace_agent_mesh/common/error_handlers.py +83 -0
- solace_agent_mesh/common/exceptions.py +24 -0
- solace_agent_mesh/common/oauth/__init__.py +17 -0
- solace_agent_mesh/common/oauth/oauth_client.py +408 -0
- solace_agent_mesh/common/oauth/utils.py +50 -0
- solace_agent_mesh/common/rag_dto.py +156 -0
- solace_agent_mesh/common/sac/sam_component_base.py +73 -1
- solace_agent_mesh/common/sam_events/event_service.py +2 -2
- solace_agent_mesh/common/utils/embeds/converter.py +1 -8
- solace_agent_mesh/common/utils/embeds/modifiers.py +2 -27
- solace_agent_mesh/common/utils/embeds/resolver.py +94 -25
- solace_agent_mesh/common/utils/embeds/types.py +1 -0
- solace_agent_mesh/common/utils/log_formatters.py +20 -0
- solace_agent_mesh/common/utils/mime_helpers.py +12 -5
- solace_agent_mesh/common/utils/rbac_utils.py +69 -0
- solace_agent_mesh/common/utils/templates/__init__.py +8 -0
- solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
- solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
- solace_agent_mesh/config_portal/backend/common.py +12 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-CljP4_mv.js +103 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{components-Rk0n-9cK.js → components-CaC6hG8d.js} +22 -22
- solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-mvZjNKiz.js → entry.client-H_TM0YBt.js} +3 -3
- solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DzNKzXrc.js → index-CnFykb2v.js} +16 -16
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-f8439d40.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-BIMqslJB.css +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-mJmTIdIk.js +10 -0
- solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
- solace_agent_mesh/core_a2a/service.py +3 -2
- solace_agent_mesh/gateway/adapter/base.py +28 -1
- solace_agent_mesh/gateway/adapter/types.py +9 -0
- solace_agent_mesh/gateway/base/app.py +10 -0
- solace_agent_mesh/gateway/base/auth_interface.py +103 -0
- solace_agent_mesh/gateway/base/component.py +451 -10
- solace_agent_mesh/gateway/generic/component.py +274 -30
- solace_agent_mesh/gateway/http_sse/alembic/env.py +0 -7
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +2 -43
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +2 -2
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251108_create_prompt_tables_with_sharing.py +154 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
- solace_agent_mesh/gateway/http_sse/alembic.ini +0 -36
- solace_agent_mesh/gateway/http_sse/app.py +23 -6
- solace_agent_mesh/gateway/http_sse/component.py +158 -73
- solace_agent_mesh/gateway/http_sse/dependencies.py +50 -57
- solace_agent_mesh/gateway/http_sse/main.py +58 -482
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +2 -2
- solace_agent_mesh/gateway/http_sse/repository/entities/project.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +3 -2
- solace_agent_mesh/gateway/http_sse/repository/entities/task.py +7 -0
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +2 -2
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +2 -2
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +5 -0
- solace_agent_mesh/gateway/http_sse/repository/models/prompt_model.py +159 -0
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +8 -1
- solace_agent_mesh/gateway/http_sse/repository/project_repository.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +1 -1
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +12 -107
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +86 -2
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +38 -7
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +113 -7
- solace_agent_mesh/gateway/http_sse/routers/auth.py +69 -132
- solace_agent_mesh/gateway/http_sse/routers/config.py +235 -10
- solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +1 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +3 -2
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/people.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/projects.py +250 -24
- solace_agent_mesh/gateway/http_sse/routers/prompts.py +1416 -0
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +14 -5
- solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
- solace_agent_mesh/gateway/http_sse/routers/sse.py +117 -4
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +509 -149
- solace_agent_mesh/gateway/http_sse/routers/users.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +2 -1
- solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
- solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
- solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/project_service.py +539 -12
- solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
- solace_agent_mesh/gateway/http_sse/services/session_service.py +198 -21
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +354 -4
- solace_agent_mesh/gateway/http_sse/sse_manager.py +280 -169
- solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
- solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +41 -1
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/platform/__init__.py +29 -0
- solace_agent_mesh/services/platform/alembic/env.py +85 -0
- solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
- solace_agent_mesh/services/platform/alembic.ini +109 -0
- solace_agent_mesh/services/platform/api/__init__.py +3 -0
- solace_agent_mesh/services/platform/api/dependencies.py +154 -0
- solace_agent_mesh/services/platform/api/main.py +314 -0
- solace_agent_mesh/services/platform/api/middleware.py +51 -0
- solace_agent_mesh/services/platform/api/routers/__init__.py +33 -0
- solace_agent_mesh/services/platform/api/routers/health_router.py +31 -0
- solace_agent_mesh/services/platform/app.py +215 -0
- solace_agent_mesh/services/platform/component.py +777 -0
- solace_agent_mesh/shared/__init__.py +14 -0
- solace_agent_mesh/shared/api/__init__.py +42 -0
- solace_agent_mesh/shared/auth/__init__.py +26 -0
- solace_agent_mesh/shared/auth/dependencies.py +204 -0
- solace_agent_mesh/shared/auth/middleware.py +347 -0
- solace_agent_mesh/shared/database/__init__.py +20 -0
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/base_repository.py +1 -1
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_exceptions.py +1 -1
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_helpers.py +1 -1
- solace_agent_mesh/shared/exceptions/__init__.py +36 -0
- solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exception_handlers.py +1 -1
- solace_agent_mesh/shared/utils/__init__.py +21 -0
- solace_agent_mesh/templates/logging_config_template.yaml +48 -0
- solace_agent_mesh/templates/main_orchestrator.yaml +12 -1
- solace_agent_mesh/templates/platform.yaml +49 -0
- solace_agent_mesh/templates/plugin_readme_template.md +3 -25
- solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
- solace_agent_mesh/templates/proxy_template.yaml +62 -0
- solace_agent_mesh/templates/webui.yaml +148 -6
- solace_agent_mesh/tools/web_search/__init__.py +18 -0
- solace_agent_mesh/tools/web_search/base.py +84 -0
- solace_agent_mesh/tools/web_search/google_search.py +247 -0
- solace_agent_mesh/tools/web_search/models.py +99 -0
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/METADATA +29 -8
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/RECORD +334 -313
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/WHEEL +1 -1
- solace_agent_mesh/agent/adk/adk_llm.txt +0 -226
- solace_agent_mesh/agent/adk/adk_llm_detail.txt +0 -566
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +0 -171
- solace_agent_mesh/agent/adk/models/models_llm.txt +0 -189
- solace_agent_mesh/agent/agent_llm.txt +0 -369
- solace_agent_mesh/agent/agent_llm_detail.txt +0 -1702
- solace_agent_mesh/agent/protocol/protocol_llm.txt +0 -81
- solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +0 -92
- solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +0 -190
- solace_agent_mesh/agent/proxies/base/base_llm.txt +0 -148
- solace_agent_mesh/agent/proxies/proxies_llm.txt +0 -283
- solace_agent_mesh/agent/sac/sac_llm.txt +0 -189
- solace_agent_mesh/agent/sac/sac_llm_detail.txt +0 -200
- solace_agent_mesh/agent/testing/testing_llm.txt +0 -58
- solace_agent_mesh/agent/testing/testing_llm_detail.txt +0 -68
- solace_agent_mesh/agent/tools/tools_llm.txt +0 -276
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +0 -275
- solace_agent_mesh/agent/utils/utils_llm.txt +0 -152
- solace_agent_mesh/agent/utils/utils_llm_detail.txt +0 -149
- solace_agent_mesh/assets/docs/assets/js/05749d90.c70b2be9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/15e40e79.36003774.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/240a0364.c39f8388.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.e4870a49.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.b63ee53a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/55b7b518.f2b1d1ba.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.45b32c2b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/66d4869e.830d443f.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.4a5fbf39.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/81a99df0.07034dd9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/94e8668d.09ed9234.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9bb13469.dd1c9b54.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.245ae0ef.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.f902fad8.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e04b235d.c9c50c7b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.d11c67a7.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.045d0fa1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.5099c51e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.f213fe0c.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.d9606d6a.js +0 -1
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +0 -47
- solace_agent_mesh/assets/docs/lunr-index-1762283454666.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1762283454666.json +0 -1
- solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +0 -250
- solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +0 -365
- solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +0 -305
- solace_agent_mesh/client/webui/frontend/static/assets/client-CRYdKo2Q.js +0 -25
- solace_agent_mesh/client/webui/frontend/static/assets/main-CojeY_1w.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-ILja9MCG.js +0 -353
- solace_agent_mesh/common/a2a/a2a_llm.txt +0 -175
- solace_agent_mesh/common/a2a/a2a_llm_detail.txt +0 -193
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +0 -445
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +0 -736
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +0 -330
- solace_agent_mesh/common/common_llm.txt +0 -230
- solace_agent_mesh/common/common_llm_detail.txt +0 -2562
- solace_agent_mesh/common/middleware/middleware_llm.txt +0 -174
- solace_agent_mesh/common/middleware/middleware_llm_detail.txt +0 -185
- solace_agent_mesh/common/sac/sac_llm.txt +0 -71
- solace_agent_mesh/common/sac/sac_llm_detail.txt +0 -82
- solace_agent_mesh/common/sam_events/sam_events_llm.txt +0 -104
- solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +0 -115
- solace_agent_mesh/common/services/providers/providers_llm.txt +0 -81
- solace_agent_mesh/common/services/services_llm.txt +0 -368
- solace_agent_mesh/common/services/services_llm_detail.txt +0 -459
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +0 -220
- solace_agent_mesh/common/utils/utils_llm.txt +0 -335
- solace_agent_mesh/common/utils/utils_llm_detail.txt +0 -572
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +0 -98
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-61038fc6.js +0 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-BWvk5-gF.js +0 -10
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +0 -1
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +0 -90
- solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +0 -101
- solace_agent_mesh/gateway/base/base_llm.txt +0 -226
- solace_agent_mesh/gateway/base/base_llm_detail.txt +0 -235
- solace_agent_mesh/gateway/gateway_llm.txt +0 -369
- solace_agent_mesh/gateway/gateway_llm_detail.txt +0 -3885
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +0 -345
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_fulltext_search_indexes.py +0 -92
- solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +0 -161
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +0 -105
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +0 -299
- solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +0 -3278
- solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +0 -221
- solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +0 -257
- solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +0 -308
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +0 -450
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +0 -133
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +0 -123
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +0 -312
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +0 -303
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +0 -146
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +0 -319
- solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +0 -47
- solace_agent_mesh/llm.txt +0 -228
- solace_agent_mesh/llm_detail.txt +0 -2835
- solace_agent_mesh/solace_agent_mesh_llm.txt +0 -362
- solace_agent_mesh/solace_agent_mesh_llm_detail.txt +0 -8599
- solace_agent_mesh/templates/logging_config_template.ini +0 -45
- solace_agent_mesh/templates/templates_llm.txt +0 -147
- /solace_agent_mesh/assets/docs/assets/js/{main.f213fe0c.js.LICENSE.txt → main.d634009f.js.LICENSE.txt} +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/auth_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/pagination.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/response_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/error_dto.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exceptions.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/enums.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/timestamp_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/types.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/utils.py +0 -0
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.7.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,7 +10,6 @@ import asyncio
|
|
|
10
10
|
import uuid
|
|
11
11
|
from typing import Any, Dict, Optional, TYPE_CHECKING, List
|
|
12
12
|
from collections import defaultdict
|
|
13
|
-
from datetime import datetime, timezone
|
|
14
13
|
|
|
15
14
|
from google.adk.tools import BaseTool, ToolContext
|
|
16
15
|
from google.adk.artifacts import BaseArtifactService
|
|
@@ -34,32 +33,31 @@ from ...agent.utils.context_helpers import (
|
|
|
34
33
|
get_session_from_callback_context,
|
|
35
34
|
)
|
|
36
35
|
from ..tools.tool_definition import BuiltinTool
|
|
36
|
+
from ..tools.peer_agent_tool import PEER_TOOL_PREFIX
|
|
37
37
|
|
|
38
38
|
from ...common.utils.embeds import (
|
|
39
39
|
EMBED_DELIMITER_OPEN,
|
|
40
40
|
EMBED_DELIMITER_CLOSE,
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
from ...common.utils.embeds import (
|
|
44
41
|
EMBED_CHAIN_DELIMITER,
|
|
42
|
+
EARLY_EMBED_TYPES,
|
|
43
|
+
evaluate_embed,
|
|
44
|
+
resolve_embeds_in_string,
|
|
45
45
|
)
|
|
46
|
+
from ...common.utils.embeds.types import ResolutionMode
|
|
46
47
|
|
|
47
48
|
from ...common.utils.embeds.modifiers import MODIFIER_IMPLEMENTATIONS
|
|
48
49
|
|
|
49
50
|
from ...common import a2a
|
|
50
|
-
from ...common.a2a.types import
|
|
51
|
+
from ...common.a2a.types import ArtifactInfo
|
|
51
52
|
from ...common.data_parts import (
|
|
52
53
|
AgentProgressUpdateData,
|
|
53
54
|
ArtifactCreationProgressData,
|
|
54
55
|
LlmInvocationData,
|
|
55
56
|
ToolInvocationStartData,
|
|
56
57
|
ToolResultData,
|
|
58
|
+
TemplateBlockData,
|
|
57
59
|
)
|
|
58
60
|
|
|
59
|
-
from ...agent.utils.artifact_helpers import (
|
|
60
|
-
save_artifact_with_metadata,
|
|
61
|
-
DEFAULT_SCHEMA_MAX_KEYS,
|
|
62
|
-
)
|
|
63
61
|
|
|
64
62
|
METADATA_RESPONSE_KEY = "appended_artifact_metadata"
|
|
65
63
|
from ..tools.builtin_artifact_tools import _internal_create_artifact
|
|
@@ -73,10 +71,14 @@ from ...agent.adk.stream_parser import (
|
|
|
73
71
|
BlockProgressedEvent,
|
|
74
72
|
BlockCompletedEvent,
|
|
75
73
|
BlockInvalidatedEvent,
|
|
74
|
+
TemplateBlockStartedEvent,
|
|
75
|
+
TemplateBlockCompletedEvent,
|
|
76
76
|
ARTIFACT_BLOCK_DELIMITER_OPEN,
|
|
77
77
|
ARTIFACT_BLOCK_DELIMITER_CLOSE,
|
|
78
|
+
TEMPLATE_LIQUID_START_SEQUENCE,
|
|
78
79
|
)
|
|
79
80
|
|
|
81
|
+
|
|
80
82
|
log = logging.getLogger(__name__)
|
|
81
83
|
|
|
82
84
|
A2A_LLM_STREAM_CHUNKS_PROCESSED_KEY = "temp:llm_stream_chunks_processed"
|
|
@@ -90,32 +92,95 @@ async def _publish_data_part_status_update(
|
|
|
90
92
|
a2a_context: Dict[str, Any],
|
|
91
93
|
data_part_model: BaseModel,
|
|
92
94
|
):
|
|
93
|
-
"""Helper to construct and publish a TaskStatusUpdateEvent with a DataPart.
|
|
94
|
-
logical_task_id = a2a_context.get("logical_task_id")
|
|
95
|
-
context_id = a2a_context.get("contextId")
|
|
95
|
+
"""Helper to construct and publish a TaskStatusUpdateEvent with a DataPart.
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
This function delegates to the host component's publish_data_signal_from_thread method,
|
|
98
|
+
which handles the async loop check and scheduling internally.
|
|
99
|
+
"""
|
|
100
|
+
host_component.publish_data_signal_from_thread(
|
|
101
|
+
a2a_context=a2a_context,
|
|
100
102
|
signal_data=data_part_model,
|
|
101
|
-
|
|
103
|
+
skip_buffer_flush=False,
|
|
104
|
+
log_identifier=host_component.log_identifier,
|
|
102
105
|
)
|
|
103
106
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
|
|
108
|
+
async def _resolve_early_embeds_in_chunk(
|
|
109
|
+
chunk: str,
|
|
110
|
+
callback_context: CallbackContext,
|
|
111
|
+
host_component: "SamAgentComponent",
|
|
112
|
+
log_identifier: str,
|
|
113
|
+
) -> str:
|
|
114
|
+
"""
|
|
115
|
+
Resolves early embeds in an artifact chunk before streaming to the browser.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
chunk: The text chunk containing potential embeds
|
|
119
|
+
callback_context: The ADK callback context with services
|
|
120
|
+
host_component: The host component instance
|
|
121
|
+
log_identifier: Identifier for logging
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
The chunk with early embeds resolved
|
|
125
|
+
"""
|
|
126
|
+
if not chunk or EMBED_DELIMITER_OPEN not in chunk:
|
|
127
|
+
return chunk
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
# Build resolution context from callback_context (pattern from EmbedResolvingMCPToolset)
|
|
131
|
+
invocation_context = callback_context._invocation_context
|
|
132
|
+
if not invocation_context:
|
|
133
|
+
log.warning(
|
|
134
|
+
"%s No invocation context available for embed resolution",
|
|
135
|
+
log_identifier,
|
|
136
|
+
)
|
|
137
|
+
return chunk
|
|
138
|
+
|
|
139
|
+
session_context = invocation_context.session
|
|
140
|
+
if not session_context:
|
|
141
|
+
log.warning(
|
|
142
|
+
"%s No session context available for embed resolution", log_identifier
|
|
143
|
+
)
|
|
144
|
+
return chunk
|
|
145
|
+
|
|
146
|
+
resolution_context = {
|
|
147
|
+
"artifact_service": invocation_context.artifact_service,
|
|
148
|
+
"session_context": {
|
|
149
|
+
"session_id": get_original_session_id(invocation_context),
|
|
150
|
+
"user_id": session_context.user_id,
|
|
151
|
+
"app_name": session_context.app_name,
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
# Resolve only early embeds (math, datetime, uuid, artifact_meta)
|
|
156
|
+
resolved_text, processed_until, _ = await resolve_embeds_in_string(
|
|
157
|
+
text=chunk,
|
|
158
|
+
context=resolution_context,
|
|
159
|
+
resolver_func=evaluate_embed,
|
|
160
|
+
types_to_resolve=EARLY_EMBED_TYPES, # Only resolve early embeds
|
|
161
|
+
resolution_mode=ResolutionMode.ARTIFACT_STREAMING, # New mode
|
|
162
|
+
log_identifier=log_identifier,
|
|
163
|
+
config=None, # Could pass host_component config if needed
|
|
113
164
|
)
|
|
114
|
-
|
|
165
|
+
|
|
166
|
+
# SAFETY CHECK: If resolver buffered something, parser has a bug
|
|
167
|
+
if processed_until < len(chunk):
|
|
168
|
+
log.error(
|
|
169
|
+
"%s PARSER BUG DETECTED: Resolver buffered partial embed. "
|
|
170
|
+
"Chunk ends with: %r. Returning unresolved chunk to avoid corruption.",
|
|
171
|
+
log_identifier,
|
|
172
|
+
chunk[-50:] if len(chunk) > 50 else chunk,
|
|
173
|
+
)
|
|
174
|
+
# Fallback: return original unresolved chunk (degraded but not corrupted)
|
|
175
|
+
return chunk
|
|
176
|
+
|
|
177
|
+
return resolved_text
|
|
178
|
+
|
|
179
|
+
except Exception as e:
|
|
115
180
|
log.error(
|
|
116
|
-
"%s
|
|
117
|
-
host_component.log_identifier,
|
|
181
|
+
"%s Error resolving embeds in chunk: %s", log_identifier, e, exc_info=True
|
|
118
182
|
)
|
|
183
|
+
return chunk # Return original chunk on error
|
|
119
184
|
|
|
120
185
|
|
|
121
186
|
async def process_artifact_blocks_callback(
|
|
@@ -135,9 +200,13 @@ async def process_artifact_blocks_callback(
|
|
|
135
200
|
parser: FencedBlockStreamParser = session.state.get(parser_state_key)
|
|
136
201
|
if parser is None:
|
|
137
202
|
log.debug("%s New turn. Creating new FencedBlockStreamParser.", log_identifier)
|
|
138
|
-
parser = FencedBlockStreamParser(progress_update_interval_bytes=
|
|
203
|
+
parser = FencedBlockStreamParser(progress_update_interval_bytes=50)
|
|
139
204
|
session.state[parser_state_key] = parser
|
|
140
205
|
session.state["completed_artifact_blocks_list"] = []
|
|
206
|
+
session.state["completed_template_blocks_list"] = []
|
|
207
|
+
session.state["artifact_chars_sent"] = (
|
|
208
|
+
0 # Reset character tracking for new turn
|
|
209
|
+
)
|
|
141
210
|
|
|
142
211
|
stream_chunks_were_processed = callback_context.state.get(
|
|
143
212
|
A2A_LLM_STREAM_CHUNKS_PROCESSED_KEY, False
|
|
@@ -169,6 +238,9 @@ async def process_artifact_blocks_callback(
|
|
|
169
238
|
log_identifier,
|
|
170
239
|
event.params,
|
|
171
240
|
)
|
|
241
|
+
# Reset character tracking for this new artifact block
|
|
242
|
+
session.state["artifact_chars_sent"] = 0
|
|
243
|
+
|
|
172
244
|
filename = event.params.get("filename", "unknown_artifact")
|
|
173
245
|
if filename == "unknown_artifact":
|
|
174
246
|
log.warning(
|
|
@@ -201,13 +273,14 @@ async def process_artifact_blocks_callback(
|
|
|
201
273
|
bytes_transferred=0,
|
|
202
274
|
artifact_chunk=None,
|
|
203
275
|
)
|
|
276
|
+
|
|
204
277
|
await _publish_data_part_status_update(
|
|
205
278
|
host_component, a2a_context, artifact_progress_data
|
|
206
279
|
)
|
|
207
280
|
params_str = " ".join(
|
|
208
281
|
[f'{k}="{v}"' for k, v in event.params.items()]
|
|
209
282
|
)
|
|
210
|
-
original_text = f"
|
|
283
|
+
original_text = f"{ARTIFACT_BLOCK_DELIMITER_OPEN}save_artifact: {params_str}\n"
|
|
211
284
|
session.state["artifact_block_original_text"] = original_text
|
|
212
285
|
|
|
213
286
|
elif isinstance(event, BlockProgressedEvent):
|
|
@@ -224,19 +297,36 @@ async def process_artifact_blocks_callback(
|
|
|
224
297
|
log_identifier,
|
|
225
298
|
)
|
|
226
299
|
if a2a_context:
|
|
300
|
+
# Resolve early embeds in the chunk before streaming
|
|
301
|
+
resolved_chunk = await _resolve_early_embeds_in_chunk(
|
|
302
|
+
chunk=event.chunk,
|
|
303
|
+
callback_context=callback_context,
|
|
304
|
+
host_component=host_component,
|
|
305
|
+
log_identifier=f"{log_identifier}[ResolveChunk]",
|
|
306
|
+
)
|
|
307
|
+
|
|
227
308
|
progress_data = ArtifactCreationProgressData(
|
|
228
309
|
filename=filename,
|
|
229
310
|
description=params.get("description"),
|
|
230
311
|
status="in-progress",
|
|
231
312
|
bytes_transferred=event.buffered_size,
|
|
232
|
-
artifact_chunk=
|
|
313
|
+
artifact_chunk=resolved_chunk, # Resolved chunk
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Track the cumulative character count of what we've sent
|
|
317
|
+
# We need character count (not bytes) to slice correctly later
|
|
318
|
+
previous_char_count = session.state.get(
|
|
319
|
+
"artifact_chars_sent", 0
|
|
233
320
|
)
|
|
321
|
+
new_char_count = previous_char_count + len(event.chunk)
|
|
322
|
+
session.state["artifact_chars_sent"] = new_char_count
|
|
323
|
+
|
|
234
324
|
await _publish_data_part_status_update(
|
|
235
325
|
host_component, a2a_context, progress_data
|
|
236
326
|
)
|
|
237
327
|
|
|
238
328
|
elif isinstance(event, BlockCompletedEvent):
|
|
239
|
-
log.
|
|
329
|
+
log.debug(
|
|
240
330
|
"%s Event: BlockCompleted. Content length: %d",
|
|
241
331
|
log_identifier,
|
|
242
332
|
len(event.content),
|
|
@@ -333,6 +423,13 @@ async def process_artifact_blocks_callback(
|
|
|
333
423
|
version_for_tool,
|
|
334
424
|
logical_task_id,
|
|
335
425
|
)
|
|
426
|
+
else:
|
|
427
|
+
log.warning(
|
|
428
|
+
"%s TaskExecutionContext not found for task %s, cannot register inline artifact '%s'.",
|
|
429
|
+
log_identifier,
|
|
430
|
+
logical_task_id,
|
|
431
|
+
filename,
|
|
432
|
+
)
|
|
336
433
|
else:
|
|
337
434
|
log.warning(
|
|
338
435
|
"%s No logical_task_id, cannot register inline artifact.",
|
|
@@ -344,6 +441,39 @@ async def process_artifact_blocks_callback(
|
|
|
344
441
|
log_identifier,
|
|
345
442
|
e_track,
|
|
346
443
|
)
|
|
444
|
+
|
|
445
|
+
# Send final progress update with any remaining content not yet sent
|
|
446
|
+
if a2a_context:
|
|
447
|
+
# Check if there's unsent content (content after last progress event)
|
|
448
|
+
total_bytes = len(event.content.encode("utf-8"))
|
|
449
|
+
chars_already_sent = session.state.get(
|
|
450
|
+
"artifact_chars_sent", 0
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
if chars_already_sent < len(event.content):
|
|
454
|
+
# There's unsent content - send it as a final progress update
|
|
455
|
+
final_chunk = event.content[chars_already_sent:]
|
|
456
|
+
|
|
457
|
+
# Resolve embeds in final chunk
|
|
458
|
+
resolved_final_chunk = await _resolve_early_embeds_in_chunk(
|
|
459
|
+
chunk=final_chunk,
|
|
460
|
+
callback_context=callback_context,
|
|
461
|
+
host_component=host_component,
|
|
462
|
+
log_identifier=f"{log_identifier}[ResolveFinalChunk]",
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
final_progress_data = ArtifactCreationProgressData(
|
|
466
|
+
filename=filename,
|
|
467
|
+
description=params.get("description"),
|
|
468
|
+
status="in-progress",
|
|
469
|
+
bytes_transferred=total_bytes,
|
|
470
|
+
artifact_chunk=resolved_final_chunk, # Resolved final chunk
|
|
471
|
+
)
|
|
472
|
+
|
|
473
|
+
await _publish_data_part_status_update(
|
|
474
|
+
host_component, a2a_context, final_progress_data
|
|
475
|
+
)
|
|
476
|
+
|
|
347
477
|
# Publish completion status immediately via SSE
|
|
348
478
|
if a2a_context:
|
|
349
479
|
progress_data = ArtifactCreationProgressData(
|
|
@@ -377,10 +507,106 @@ async def process_artifact_blocks_callback(
|
|
|
377
507
|
"filename": filename,
|
|
378
508
|
"version": version_for_tool,
|
|
379
509
|
"status": status_for_tool,
|
|
510
|
+
"description": params.get("description"),
|
|
511
|
+
"mime_type": params.get("mime_type"),
|
|
512
|
+
"bytes_transferred": len(event.content),
|
|
380
513
|
"original_text": original_text,
|
|
381
514
|
}
|
|
382
515
|
)
|
|
383
516
|
|
|
517
|
+
elif isinstance(event, TemplateBlockStartedEvent):
|
|
518
|
+
log.debug(
|
|
519
|
+
"%s Event: TemplateBlockStarted. Params: %s",
|
|
520
|
+
log_identifier,
|
|
521
|
+
event.params,
|
|
522
|
+
)
|
|
523
|
+
|
|
524
|
+
elif isinstance(event, TemplateBlockCompletedEvent):
|
|
525
|
+
log.debug(
|
|
526
|
+
"%s Event: TemplateBlockCompleted. Template length: %d",
|
|
527
|
+
log_identifier,
|
|
528
|
+
len(event.template_content),
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
# Create a TemplateBlockData message to send to the gateway
|
|
532
|
+
template_id = str(uuid.uuid4())
|
|
533
|
+
params = event.params
|
|
534
|
+
|
|
535
|
+
data_artifact = params.get("data")
|
|
536
|
+
if not data_artifact:
|
|
537
|
+
log.warning(
|
|
538
|
+
"%s Template block is missing 'data' parameter. Skipping.",
|
|
539
|
+
log_identifier,
|
|
540
|
+
)
|
|
541
|
+
continue
|
|
542
|
+
|
|
543
|
+
template_data = TemplateBlockData(
|
|
544
|
+
template_id=template_id,
|
|
545
|
+
data_artifact=data_artifact,
|
|
546
|
+
jsonpath=params.get("jsonpath"),
|
|
547
|
+
limit=(
|
|
548
|
+
int(params.get("limit"))
|
|
549
|
+
if params.get("limit")
|
|
550
|
+
else None
|
|
551
|
+
),
|
|
552
|
+
template_content=event.template_content,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
# Publish A2A status update with template metadata
|
|
556
|
+
if a2a_context:
|
|
557
|
+
await _publish_data_part_status_update(
|
|
558
|
+
host_component, a2a_context, template_data
|
|
559
|
+
)
|
|
560
|
+
log.info(
|
|
561
|
+
"%s Published TemplateBlockData with ID: %s",
|
|
562
|
+
log_identifier,
|
|
563
|
+
template_id,
|
|
564
|
+
)
|
|
565
|
+
|
|
566
|
+
# Reconstruct the original template block text for peer-to-peer responses
|
|
567
|
+
# Peer agents don't receive TemplateBlockData signals, so they need
|
|
568
|
+
# the original block text to pass templates through to the gateway
|
|
569
|
+
params_str = " ".join([f'{k}="{v}"' for k, v in params.items()])
|
|
570
|
+
original_template_text = (
|
|
571
|
+
f"{TEMPLATE_LIQUID_START_SEQUENCE} {params_str}\n"
|
|
572
|
+
f"{event.template_content}"
|
|
573
|
+
f"{ARTIFACT_BLOCK_DELIMITER_CLOSE}"
|
|
574
|
+
)
|
|
575
|
+
|
|
576
|
+
# For RUN_BASED sessions (peer-to-peer agent requests), preserve the
|
|
577
|
+
# template block in the response text at its original position.
|
|
578
|
+
# This allows the calling agent to forward it to the gateway.
|
|
579
|
+
# Gateway requests use streaming sessions and receive TemplateBlockData
|
|
580
|
+
# signals instead.
|
|
581
|
+
is_run_based = a2a_context and a2a_context.get(
|
|
582
|
+
"is_run_based_session", False
|
|
583
|
+
)
|
|
584
|
+
if is_run_based and llm_response.partial:
|
|
585
|
+
processed_parts.append(
|
|
586
|
+
adk_types.Part(text=original_template_text)
|
|
587
|
+
)
|
|
588
|
+
log.debug(
|
|
589
|
+
"%s Preserved template block in RUN_BASED peer response. Template ID: %s",
|
|
590
|
+
log_identifier,
|
|
591
|
+
template_id,
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
# Store template_id and original text in session for potential future use
|
|
595
|
+
# (Gateway will handle the actual resolution via signals,
|
|
596
|
+
# but peer agents need the original text in their responses)
|
|
597
|
+
if (
|
|
598
|
+
"completed_template_blocks_list" not in session.state
|
|
599
|
+
or session.state["completed_template_blocks_list"] is None
|
|
600
|
+
):
|
|
601
|
+
session.state["completed_template_blocks_list"] = []
|
|
602
|
+
session.state["completed_template_blocks_list"].append(
|
|
603
|
+
{
|
|
604
|
+
"template_id": template_id,
|
|
605
|
+
"data_artifact": data_artifact,
|
|
606
|
+
"original_text": original_template_text,
|
|
607
|
+
}
|
|
608
|
+
)
|
|
609
|
+
|
|
384
610
|
elif isinstance(event, BlockInvalidatedEvent):
|
|
385
611
|
log.debug(
|
|
386
612
|
"%s Event: BlockInvalidated. Rolled back: '%s'",
|
|
@@ -466,8 +692,12 @@ async def process_artifact_blocks_callback(
|
|
|
466
692
|
len(completed_blocks_list),
|
|
467
693
|
)
|
|
468
694
|
|
|
695
|
+
# Get a2a_context for sending signals
|
|
696
|
+
a2a_context = callback_context.state.get("a2a_context")
|
|
697
|
+
|
|
469
698
|
tool_call_parts = []
|
|
470
699
|
for block_info in completed_blocks_list:
|
|
700
|
+
function_call_id = f"host-notify-{uuid.uuid4()}"
|
|
471
701
|
notify_tool_call = adk_types.FunctionCall(
|
|
472
702
|
name="_notify_artifact_save",
|
|
473
703
|
args={
|
|
@@ -475,10 +705,41 @@ async def process_artifact_blocks_callback(
|
|
|
475
705
|
"version": block_info["version"],
|
|
476
706
|
"status": block_info["status"],
|
|
477
707
|
},
|
|
478
|
-
id=
|
|
708
|
+
id=function_call_id,
|
|
479
709
|
)
|
|
480
710
|
tool_call_parts.append(adk_types.Part(function_call=notify_tool_call))
|
|
481
711
|
|
|
712
|
+
# Send artifact saved notification now that we have the function_call_id
|
|
713
|
+
# This ensures the signal and tool call arrive together
|
|
714
|
+
if block_info["status"] == "success" and a2a_context:
|
|
715
|
+
try:
|
|
716
|
+
artifact_info = ArtifactInfo(
|
|
717
|
+
filename=block_info["filename"],
|
|
718
|
+
version=block_info["version"],
|
|
719
|
+
mime_type=block_info.get("mime_type")
|
|
720
|
+
or "application/octet-stream",
|
|
721
|
+
size=block_info.get("bytes_transferred", 0),
|
|
722
|
+
description=block_info.get("description"),
|
|
723
|
+
version_count=None, # Count not available in save context
|
|
724
|
+
)
|
|
725
|
+
await host_component.notify_artifact_saved(
|
|
726
|
+
artifact_info=artifact_info,
|
|
727
|
+
a2a_context=a2a_context,
|
|
728
|
+
function_call_id=function_call_id,
|
|
729
|
+
)
|
|
730
|
+
log.debug(
|
|
731
|
+
"%s Published artifact saved notification for fenced block: %s (function_call_id=%s)",
|
|
732
|
+
log_identifier,
|
|
733
|
+
block_info["filename"],
|
|
734
|
+
function_call_id,
|
|
735
|
+
)
|
|
736
|
+
except Exception as signal_err:
|
|
737
|
+
log.warning(
|
|
738
|
+
"%s Failed to publish artifact saved notification: %s",
|
|
739
|
+
log_identifier,
|
|
740
|
+
signal_err,
|
|
741
|
+
)
|
|
742
|
+
|
|
482
743
|
existing_parts = llm_response.content.parts if llm_response.content else []
|
|
483
744
|
final_existing_parts = existing_parts
|
|
484
745
|
|
|
@@ -493,6 +754,7 @@ async def process_artifact_blocks_callback(
|
|
|
493
754
|
session.state[parser_state_key] = None
|
|
494
755
|
session.state["completed_artifact_blocks_list"] = None
|
|
495
756
|
session.state["artifact_block_original_text"] = None
|
|
757
|
+
session.state["completed_template_blocks_list"] = None
|
|
496
758
|
log.debug("%s Cleaned up parser session state.", log_identifier)
|
|
497
759
|
|
|
498
760
|
return None
|
|
@@ -745,26 +1007,24 @@ async def manage_large_mcp_tool_responses_callback(
|
|
|
745
1007
|
message_parts_for_llm: list[str] = []
|
|
746
1008
|
|
|
747
1009
|
if needs_truncation_for_llm:
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
truncated_bytes = serialized_original_response_str.encode("utf-8")[
|
|
754
|
-
:adjusted_max_bytes
|
|
755
|
-
]
|
|
756
|
-
truncated_preview_str = (
|
|
757
|
-
truncated_bytes.decode("utf-8", "ignore") + truncation_suffix
|
|
758
|
-
)
|
|
759
|
-
|
|
1010
|
+
# ALL-OR-NOTHING APPROACH: Do not include truncated data to prevent LLM hallucination.
|
|
1011
|
+
# When LLMs receive partial data, they tend to confidently fill in gaps with
|
|
1012
|
+
# hallucinated information. By withholding partial data entirely, we force the LLM
|
|
1013
|
+
# to use reliable mechanisms (template_liquid, load_artifact) to access the full data.
|
|
760
1014
|
final_llm_response_dict["mcp_tool_output"] = {
|
|
761
|
-
"type": "
|
|
762
|
-
"
|
|
1015
|
+
"type": "data_in_artifact_only",
|
|
1016
|
+
"message": "Data exceeds size limit. Full data saved as artifact - use template_liquid, load_artifact or other artifact analysis tools to process and access.",
|
|
763
1017
|
}
|
|
764
1018
|
message_parts_for_llm.append(
|
|
765
|
-
f"The response from tool '{tool.name}' was too large ({original_response_bytes} bytes)
|
|
1019
|
+
f"The response from tool '{tool.name}' was too large ({original_response_bytes} bytes) to display directly. "
|
|
1020
|
+
"The data has NOT been included here to prevent incomplete information. "
|
|
1021
|
+
"You MUST use template_liquid (for displaying to users) or load_artifact or other "
|
|
1022
|
+
"artifact analysis tools (for processing) to access the full data."
|
|
1023
|
+
)
|
|
1024
|
+
log.debug(
|
|
1025
|
+
"%s MCP tool output withheld from LLM (all-or-nothing approach).",
|
|
1026
|
+
log_identifier,
|
|
766
1027
|
)
|
|
767
|
-
log.debug("%s MCP tool output truncated for LLM.", log_identifier)
|
|
768
1028
|
|
|
769
1029
|
if needs_saving_as_artifact:
|
|
770
1030
|
if save_result and save_result.status in [
|
|
@@ -781,19 +1041,27 @@ async def manage_large_mcp_tool_responses_callback(
|
|
|
781
1041
|
filename = first_artifact.data_filename
|
|
782
1042
|
version = first_artifact.data_version
|
|
783
1043
|
if total_artifacts > 1:
|
|
784
|
-
|
|
785
|
-
f"The full response has been saved as {total_artifacts} artifacts, starting with '{filename}' (version {version})."
|
|
786
|
-
)
|
|
1044
|
+
artifact_msg = f"The full response has been saved as {total_artifacts} artifacts, starting with '{filename}' (version {version})."
|
|
787
1045
|
else:
|
|
788
|
-
|
|
789
|
-
|
|
1046
|
+
artifact_msg = f"The full response has been saved as artifact '{filename}' (version {version})."
|
|
1047
|
+
|
|
1048
|
+
# When data was too large and truncated, provide explicit guidance
|
|
1049
|
+
if needs_truncation_for_llm:
|
|
1050
|
+
artifact_msg += (
|
|
1051
|
+
f' To display this data to the user, use template_liquid with data="{filename}". '
|
|
1052
|
+
f'To process the data yourself, use load_artifact("{filename}").'
|
|
790
1053
|
)
|
|
1054
|
+
message_parts_for_llm.append(artifact_msg)
|
|
791
1055
|
elif save_result.fallback_artifact:
|
|
792
1056
|
filename = save_result.fallback_artifact.data_filename
|
|
793
1057
|
version = save_result.fallback_artifact.data_version
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1058
|
+
artifact_msg = f"The full response has been saved as artifact '{filename}' (version {version})."
|
|
1059
|
+
if needs_truncation_for_llm:
|
|
1060
|
+
artifact_msg += (
|
|
1061
|
+
f' To display this data to the user, use template_liquid with data="{filename}". '
|
|
1062
|
+
f'To process the data yourself, use load_artifact("{filename}").'
|
|
1063
|
+
)
|
|
1064
|
+
message_parts_for_llm.append(artifact_msg)
|
|
797
1065
|
|
|
798
1066
|
log.debug(
|
|
799
1067
|
"%s Added saved artifact details to LLM response.", log_identifier
|
|
@@ -818,16 +1086,18 @@ async def manage_large_mcp_tool_responses_callback(
|
|
|
818
1086
|
and save_result.status in [McpSaveStatus.SUCCESS, McpSaveStatus.PARTIAL_SUCCESS]
|
|
819
1087
|
):
|
|
820
1088
|
if needs_truncation_for_llm:
|
|
821
|
-
|
|
1089
|
+
# Data was too large - withheld from LLM, only available via artifact
|
|
1090
|
+
final_llm_response_dict["status"] = "processed_saved_artifact_only"
|
|
822
1091
|
else:
|
|
823
1092
|
final_llm_response_dict["status"] = "processed_and_saved"
|
|
824
1093
|
elif needs_saving_as_artifact:
|
|
825
1094
|
if needs_truncation_for_llm:
|
|
826
|
-
final_llm_response_dict["status"] = "
|
|
1095
|
+
final_llm_response_dict["status"] = "processed_artifact_only_save_failed"
|
|
827
1096
|
else:
|
|
828
1097
|
final_llm_response_dict["status"] = "processed_save_failed"
|
|
829
1098
|
elif needs_truncation_for_llm:
|
|
830
|
-
|
|
1099
|
+
# This case shouldn't happen (truncation implies saving), but handle it
|
|
1100
|
+
final_llm_response_dict["status"] = "processed_data_withheld"
|
|
831
1101
|
else:
|
|
832
1102
|
final_llm_response_dict["status"] = "processed"
|
|
833
1103
|
|
|
@@ -843,60 +1113,113 @@ async def manage_large_mcp_tool_responses_callback(
|
|
|
843
1113
|
return final_llm_response_dict
|
|
844
1114
|
|
|
845
1115
|
|
|
846
|
-
def
|
|
847
|
-
"""Generates the
|
|
1116
|
+
def _generate_fenced_block_syntax_rules() -> str:
|
|
1117
|
+
"""Generates the shared syntax rules for all fenced blocks."""
|
|
848
1118
|
open_delim = ARTIFACT_BLOCK_DELIMITER_OPEN
|
|
849
1119
|
close_delim = ARTIFACT_BLOCK_DELIMITER_CLOSE
|
|
850
|
-
return f"""
|
|
851
|
-
**
|
|
852
|
-
|
|
853
|
-
**When to Create Text-based Artifacts:**
|
|
854
|
-
Create an artifact when the content provides value as a standalone file:
|
|
855
|
-
- Content with special formatting (HTML, Markdown, CSS, structured markup) that requires proper rendering
|
|
856
|
-
- Content explicitly intended for use outside this conversation (reports, emails, presentations, reference documents)
|
|
857
|
-
- Structured reference content users will save or follow (schedules, guides, templates)
|
|
858
|
-
- Content that will be edited, expanded, or reused
|
|
859
|
-
- Substantial text documents
|
|
860
|
-
- Technical documentation meant as reference material
|
|
861
|
-
|
|
862
|
-
**When NOT to Create Text-based Artifacts:**
|
|
863
|
-
- Simple answers, explanations, or conversational responses
|
|
864
|
-
- Brief advice, opinions, or quick information
|
|
865
|
-
- Short lists, summaries, or single paragraphs
|
|
866
|
-
- Temporary content only relevant to the immediate conversation
|
|
867
|
-
- Basic explanations that don't require reference material
|
|
868
|
-
|
|
869
|
-
**Behaviour of created artifacts:**
|
|
870
|
-
- they are sent back to the UI inline with the text and show up as an interactive file component
|
|
871
|
-
- the user can easily see the content so there is no need to return or embed it again.
|
|
872
|
-
- do not embed the same artifact again, since the user already has it to expand and view
|
|
873
|
-
|
|
874
|
-
**How to create artifacts:**
|
|
875
|
-
To create an artifact from content you generate (like code, a report, or a document), you MUST use a fenced artifact block with the EXACT syntax shown below. This is the only reliable way to ensure your content is saved correctly.
|
|
1120
|
+
return f"""
|
|
1121
|
+
**Fenced Block Syntax Rules (Applies to `save_artifact` and `template_liquid`):**
|
|
1122
|
+
To create content blocks, you MUST use the EXACT syntax shown below.
|
|
876
1123
|
|
|
877
1124
|
**EXACT SYNTAX (copy this pattern exactly):**
|
|
878
|
-
{open_delim}
|
|
879
|
-
The
|
|
1125
|
+
{open_delim}keyword: parameter="value" ...
|
|
1126
|
+
The content for the block goes here.
|
|
880
1127
|
It can span multiple lines.
|
|
881
1128
|
{close_delim}
|
|
882
1129
|
|
|
883
1130
|
**CRITICAL FORMATTING RULES:**
|
|
884
|
-
1. The opening delimiter MUST be EXACTLY
|
|
885
|
-
2. Immediately after the
|
|
886
|
-
3.
|
|
887
|
-
4. All parameter values **MUST** be enclosed in double quotes
|
|
888
|
-
5. You **MUST NOT** use double quotes `"` inside parameter values. Use single quotes or rephrase instead
|
|
889
|
-
6.
|
|
890
|
-
7. Close the block with EXACTLY three angle brackets
|
|
891
|
-
8. Do NOT surround the block with triple backticks (```). The
|
|
1131
|
+
1. The opening delimiter MUST be EXACTLY `{open_delim}`.
|
|
1132
|
+
2. Immediately after the delimiter, write the keyword (`save_artifact` or `template_liquid`) followed by a colon, with NO space before the colon (e.g., `{open_delim}save_artifact:`).
|
|
1133
|
+
3. All parameters (like `filename`, `data`, `mime_type`) must be on the SAME line as the opening delimiter.
|
|
1134
|
+
4. All parameter values **MUST** be enclosed in double quotes (e.g., `filename="example.txt"`).
|
|
1135
|
+
5. You **MUST NOT** use double quotes `"` inside parameter values. Use single quotes or rephrase instead.
|
|
1136
|
+
6. The block's content begins on the line immediately following the parameters.
|
|
1137
|
+
7. Close the block with EXACTLY `{close_delim}` (three angle brackets) on its own line.
|
|
1138
|
+
8. Do NOT surround the block with triple backticks (```). The `{open_delim}` and `{close_delim}` delimiters are sufficient.
|
|
892
1139
|
|
|
893
1140
|
**COMMON ERRORS TO AVOID:**
|
|
1141
|
+
❌ WRONG: `{open_delim[0:1]}template_liquid:` (only 1 angle brackets)
|
|
894
1142
|
❌ WRONG: `{open_delim[0:2]}save_artifact:` (only 2 angle brackets)
|
|
895
|
-
❌ WRONG: `{open_delim[0:1]}save_artifact:` (only 1 angle bracket)
|
|
896
1143
|
❌ WRONG: `{open_delim}save_artifact` (missing colon)
|
|
897
1144
|
✅ CORRECT: `{open_delim}save_artifact: filename="test.txt" mime_type="text/plain"`
|
|
1145
|
+
"""
|
|
1146
|
+
|
|
1147
|
+
|
|
1148
|
+
def _generate_fenced_artifact_instruction() -> str:
|
|
1149
|
+
"""Generates the instruction text for using fenced artifact blocks."""
|
|
1150
|
+
open_delim = ARTIFACT_BLOCK_DELIMITER_OPEN
|
|
1151
|
+
return f"""\
|
|
1152
|
+
**Creating Text-Based Artifacts (`{open_delim}save_artifact: ...`):**
|
|
1153
|
+
|
|
1154
|
+
When to Create Artifacts:
|
|
1155
|
+
Create an artifact when the content provides value as a standalone file, such as:
|
|
1156
|
+
- Content with special formatting (HTML, Markdown, CSS).
|
|
1157
|
+
- Documents intended for use outside the conversation (reports, emails).
|
|
1158
|
+
- Structured reference content (schedules, guides, templates).
|
|
1159
|
+
- Substantial text documents or technical documentation.
|
|
1160
|
+
|
|
1161
|
+
When NOT to Create Artifacts:
|
|
1162
|
+
- Simple answers, explanations, or conversational responses.
|
|
1163
|
+
- Brief advice, opinions, or short lists.
|
|
1164
|
+
|
|
1165
|
+
Behavior of Created Artifacts:
|
|
1166
|
+
- They are sent to the user as an interactive file component.
|
|
1167
|
+
- The user can see the content, so there is no need to return or embed it again.
|
|
1168
|
+
|
|
1169
|
+
Parameters for `{open_delim}save_artifact: ...`:
|
|
1170
|
+
- `filename="your_filename.ext"` (REQUIRED)
|
|
1171
|
+
- `mime_type="text/plain"` (optional, defaults to text/plain)
|
|
1172
|
+
- `description="A brief description."` (optional)
|
|
1173
|
+
|
|
1174
|
+
The system will automatically save the content and confirm it in the next turn.
|
|
1175
|
+
"""
|
|
1176
|
+
|
|
1177
|
+
|
|
1178
|
+
def _generate_inline_template_instruction() -> str:
|
|
1179
|
+
"""Generates the instruction text for using inline Liquid templates."""
|
|
1180
|
+
open_delim = ARTIFACT_BLOCK_DELIMITER_OPEN
|
|
1181
|
+
close_delim = ARTIFACT_BLOCK_DELIMITER_CLOSE
|
|
1182
|
+
return f"""\
|
|
1183
|
+
**Inline Liquid Templates (`{open_delim}template_liquid: ...`):**
|
|
1184
|
+
|
|
1185
|
+
Use inline Liquid templates to dynamically render data from artifacts for user-friendly display. This is faster and more accurate than reading the artifact and reformatting it yourself.
|
|
1186
|
+
|
|
1187
|
+
IMPORTANT: Template Format
|
|
1188
|
+
- Templates use Liquid template syntax (same as Shopify templates - NOTE that Jekyll extensions are NOT supported).
|
|
1189
|
+
|
|
1190
|
+
When to Use Inline Templates:
|
|
1191
|
+
- Formatting CSV, JSON, or YAML data into tables or lists.
|
|
1192
|
+
- Applying simple transformations (filtering, limiting rows).
|
|
1193
|
+
|
|
1194
|
+
Parameters for `{open_delim}template_liquid: ...`:
|
|
1195
|
+
- `data="filename.ext"` (REQUIRED): The data artifact to render. Can include version: `data="file.csv:2"`.
|
|
1196
|
+
- `jsonpath="$.expression"` (optional): JSONPath to extract a subset of JSON/YAML data.
|
|
1197
|
+
- `limit="N"` (optional): Limit to the first N rows (CSV) or items (JSON/YAML arrays).
|
|
1198
|
+
|
|
1199
|
+
Data Context for Liquid Templates:
|
|
1200
|
+
- CSV data: Available as `headers` (array of column names) and `data_rows` (array of row arrays).
|
|
1201
|
+
- JSON/YAML arrays: Available as `items`.
|
|
1202
|
+
- JSON/YAML objects: Keys are directly available (e.g., `name`, `email`).
|
|
1203
|
+
|
|
1204
|
+
Example - CSV Table:
|
|
1205
|
+
{open_delim}template_liquid: data="sales_data.csv" limit="5"
|
|
1206
|
+
| {{% for h in headers %}}{{{{ h }}}} | {{% endfor %}}
|
|
1207
|
+
|{{% for h in headers %}}---|{{% endfor %}}
|
|
1208
|
+
{{% for row in data_rows %}}| {{% for cell in row %}}{{{{ cell }}}} | {{% endfor %}}{{% endfor %}}
|
|
1209
|
+
{close_delim}
|
|
898
1210
|
|
|
899
|
-
|
|
1211
|
+
**IMPORTANT - Pipe Characters in Markdown Tables:**
|
|
1212
|
+
Text data may contain "|" characters which will break markdown table rendering by pushing data into wrong columns. For text fields that might contain pipes, use the `replace` filter to escape them:
|
|
1213
|
+
`{{{{ item.summary | replace: "|", "|" }}}}`
|
|
1214
|
+
Only apply this to text fields that might contain pipes - numerical columns don't need it.
|
|
1215
|
+
|
|
1216
|
+
Negative Examples
|
|
1217
|
+
Use {{ issues.size }} instead of {{ issues|length }}
|
|
1218
|
+
Use {{ forloop.index }} instead of {{ loop.index }} (Liquid uses forloop not loop)
|
|
1219
|
+
Use {{ issue.fields.description | truncate: 200 }} instead of slicing with [:200]
|
|
1220
|
+
Do not use Jekyll-specific tags or filters (e.g., `{{% assign %}}`, `{{% capture %}}`, `where`, `sort`, `where_exp`, etc.)
|
|
1221
|
+
|
|
1222
|
+
The rendered output will appear inline in your response automatically.
|
|
900
1223
|
"""
|
|
901
1224
|
|
|
902
1225
|
|
|
@@ -904,7 +1227,7 @@ def _generate_artifact_creation_instruction() -> str:
|
|
|
904
1227
|
return """
|
|
905
1228
|
**Creating Text-Based Artifacts:**
|
|
906
1229
|
|
|
907
|
-
|
|
1230
|
+
When to Create Text-based Artifacts:
|
|
908
1231
|
Create an artifact when the content provides value as a standalone file:
|
|
909
1232
|
- Content with special formatting (HTML, Markdown, CSS, structured markup) that requires proper rendering
|
|
910
1233
|
- Content explicitly intended for use outside this conversation (reports, emails, presentations, reference documents)
|
|
@@ -913,15 +1236,123 @@ def _generate_artifact_creation_instruction() -> str:
|
|
|
913
1236
|
- Substantial text documents
|
|
914
1237
|
- Technical documentation meant as reference material
|
|
915
1238
|
|
|
916
|
-
|
|
1239
|
+
When NOT to Create Text-based Artifacts:
|
|
917
1240
|
- Simple answers, explanations, or conversational responses
|
|
918
1241
|
- Brief advice, opinions, or quick information
|
|
919
|
-
- Short lists, summaries, or single paragraphs
|
|
1242
|
+
- Short lists, summaries, or single paragraphs
|
|
920
1243
|
- Temporary content only relevant to the immediate conversation
|
|
921
1244
|
- Basic explanations that don't require reference material
|
|
922
1245
|
"""
|
|
923
1246
|
|
|
924
1247
|
|
|
1248
|
+
def _generate_examples_instruction() -> str:
|
|
1249
|
+
open_delim = ARTIFACT_BLOCK_DELIMITER_OPEN
|
|
1250
|
+
close_delim = ARTIFACT_BLOCK_DELIMITER_CLOSE
|
|
1251
|
+
embed_open_delim = EMBED_DELIMITER_OPEN
|
|
1252
|
+
embed_close_delim = EMBED_DELIMITER_CLOSE
|
|
1253
|
+
|
|
1254
|
+
return (
|
|
1255
|
+
f"""\
|
|
1256
|
+
Example 1:
|
|
1257
|
+
- User: "Create a markdown file with your two csv files as tables."
|
|
1258
|
+
<note>There are two csv files already uploaded: data1.csv and data2.csv</note>
|
|
1259
|
+
- OrchestratorAgent:
|
|
1260
|
+
{embed_open_delim}status_update:Creating Markdown tables from CSV files...{embed_close_delim}
|
|
1261
|
+
{open_delim}save_artifact: filename="data_tables.md" mime_type="text/markdown" description="Markdown tables from CSV files"
|
|
1262
|
+
# Data Tables
|
|
1263
|
+
## Data 1
|
|
1264
|
+
{open_delim}template_liquid: data="data1.csv"
|
|
1265
|
+
"""
|
|
1266
|
+
+ """| {% for h in headers %}{{ h }} | {% endfor %}
|
|
1267
|
+
|{% for h in headers %}---|{% endfor %}
|
|
1268
|
+
{% for row in data_rows %}| {% for cell in row %}{{ cell }} | {% endfor %}{% endfor %}
|
|
1269
|
+
"""
|
|
1270
|
+
+ f"""{close_delim}
|
|
1271
|
+
## Data 2
|
|
1272
|
+
{open_delim}template_liquid: data="data2.csv"
|
|
1273
|
+
"""
|
|
1274
|
+
+ """| {% for h in headers %}{{ h }} | {% endfor %}
|
|
1275
|
+
|{% for h in headers %}---|{% endfor %}
|
|
1276
|
+
{% for row in data_rows %}| {% for cell in row %}{{ cell }} | {% endfor %}{% endfor %}
|
|
1277
|
+
"""
|
|
1278
|
+
+ f"""{close_delim}
|
|
1279
|
+
{close_delim}
|
|
1280
|
+
Example 2:
|
|
1281
|
+
- User: "Create a text file with the result of sqrt(12345) + sqrt(67890) + sqrt(13579) + sqrt(24680)."
|
|
1282
|
+
- OrchestratorAgent:
|
|
1283
|
+
{embed_open_delim}status_update:Calculating and creating text file...{embed_close_delim}
|
|
1284
|
+
{open_delim}save_artifact: filename="math.txt" mime_type="text/plain" description="Result of sqrt(12345) + sqrt(67890) + sqrt(13579) + sqrt(24680)"
|
|
1285
|
+
result = {embed_open_delim}math: sqrt(12345) + sqrt(67890) + sqrt(13579) + sqrt(24680) | .2f{embed_close_delim}
|
|
1286
|
+
{close_delim}
|
|
1287
|
+
|
|
1288
|
+
Example 3:
|
|
1289
|
+
- User: "Show me the first 10 entries from data1.csv"
|
|
1290
|
+
- OrchestratorAgent:
|
|
1291
|
+
{embed_open_delim}status_update:Loading CSV data...{embed_close_delim}
|
|
1292
|
+
{open_delim}template_liquid: data="data1.csv" limit="10"
|
|
1293
|
+
"""
|
|
1294
|
+
+ """| {% for h in headers %}{{ h }} | {% endfor %}
|
|
1295
|
+
|{% for h in headers %}---|{% endfor %}
|
|
1296
|
+
{% for row in data_rows %}| {% for cell in row %}{{ cell }} | {% endfor %}{% endfor %}
|
|
1297
|
+
"""
|
|
1298
|
+
+ f"""{close_delim}
|
|
1299
|
+
|
|
1300
|
+
Example 4:
|
|
1301
|
+
- User: "Show me the Jira issues as a table"
|
|
1302
|
+
<note>There is a JSON artifact jira_issues.json with items containing key, summary, status, type, assignee, updated fields</note>
|
|
1303
|
+
- OrchestratorAgent:
|
|
1304
|
+
{embed_open_delim}status_update:Rendering Jira issues table...{embed_close_delim}
|
|
1305
|
+
{open_delim}template_liquid: data="jira_issues.json" limit="10"
|
|
1306
|
+
"""
|
|
1307
|
+
+ """| Key | Summary | Status | Type | Assignee | Updated |
|
|
1308
|
+
|-----|---------|--------|------|----------|---------|
|
|
1309
|
+
{% for item in items %}| [{{ item.key }}](https://jira.example.com/browse/{{ item.key }}) | {{ item.summary | replace: "|", "|" }} | {{ item.status }} | {{ item.type }} | {{ item.assignee }} | {{ item.updated }} |
|
|
1310
|
+
{% endfor %}"""
|
|
1311
|
+
+ f"""
|
|
1312
|
+
{close_delim}
|
|
1313
|
+
<note>The replace filter on item.summary escapes any pipe characters that would break the markdown table. Only apply to text fields that might contain pipes.</note>
|
|
1314
|
+
|
|
1315
|
+
Example 5:
|
|
1316
|
+
- User: "Search the database for all orders from last month"
|
|
1317
|
+
- OrchestratorAgent:
|
|
1318
|
+
{embed_open_delim}status_update:Querying order database...{embed_close_delim}
|
|
1319
|
+
[calls search_database tool with no visible text]
|
|
1320
|
+
[After getting results:]
|
|
1321
|
+
Found 247 orders from last month totaling $45,231.
|
|
1322
|
+
|
|
1323
|
+
Example 6:
|
|
1324
|
+
- User: "Create an HTML with the chart image you just generated with the customer data."
|
|
1325
|
+
- OrchestratorAgent:
|
|
1326
|
+
{embed_open_delim}status_update:Generating HTML report with chart...{embed_close_delim}
|
|
1327
|
+
{open_delim}save_artifact: filename="customer_analysis.html" mime_type="text/html" description="Interactive customer analysis dashboard"
|
|
1328
|
+
<!DOCTYPE html>
|
|
1329
|
+
<html>
|
|
1330
|
+
<head>
|
|
1331
|
+
<title>Customer Chart - {embed_open_delim}datetime:%Y-%m-%d{embed_close_delim}</title>
|
|
1332
|
+
"""
|
|
1333
|
+
+ """
|
|
1334
|
+
<style>
|
|
1335
|
+
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
1336
|
+
.metric { background: #f0f0f0; padding: 10px; margin: 10px 0; }
|
|
1337
|
+
img { max-width: 100%; height: auto; }
|
|
1338
|
+
"""
|
|
1339
|
+
+ f""" </style>
|
|
1340
|
+
</head>
|
|
1341
|
+
<body>
|
|
1342
|
+
<h1>Customer Analysis Report</h1>
|
|
1343
|
+
<p>Generated: {embed_open_delim}datetime:iso{embed_close_delim}</p>
|
|
1344
|
+
|
|
1345
|
+
<h2>Customer Distribution Chart</h2>
|
|
1346
|
+
<img src="{embed_open_delim}artifact_content:customer_chart.png >>> format:datauri{embed_close_delim}" alt="Customer Distribution">
|
|
1347
|
+
|
|
1348
|
+
</body>
|
|
1349
|
+
</html>
|
|
1350
|
+
{close_delim}
|
|
1351
|
+
|
|
1352
|
+
"""
|
|
1353
|
+
)
|
|
1354
|
+
|
|
1355
|
+
|
|
925
1356
|
def _generate_embed_instruction(
|
|
926
1357
|
include_artifact_content: bool,
|
|
927
1358
|
log_identifier: str,
|
|
@@ -931,11 +1362,17 @@ def _generate_embed_instruction(
|
|
|
931
1362
|
close_delim = EMBED_DELIMITER_CLOSE
|
|
932
1363
|
chain_delim = EMBED_CHAIN_DELIMITER
|
|
933
1364
|
early_types = "`math`, `datetime`, `uuid`, `artifact_meta`"
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
1365
|
+
|
|
1366
|
+
modifier_list = MODIFIER_IMPLEMENTATIONS.keys()
|
|
1367
|
+
# Remove apply_to_template from the modifier list as it's been deprecated
|
|
1368
|
+
if "apply_to_template" in modifier_list:
|
|
1369
|
+
modifier_list = list(modifier_list)
|
|
1370
|
+
modifier_list.remove("apply_to_template")
|
|
1371
|
+
modifier_list = ", ".join([f"`{prefix}`" for prefix in modifier_list])
|
|
937
1372
|
|
|
938
1373
|
base_instruction = f"""\
|
|
1374
|
+
**Using Dynamic Embeds in Responses:**
|
|
1375
|
+
|
|
939
1376
|
You can use dynamic embeds in your text responses and tool parameters using the syntax {open_delim}type:expression {chain_delim} format{close_delim}. NOTE that this differs from 'save_artifact', which has different delimiters. This allows you to
|
|
940
1377
|
always have correct information in your output. Specifically, make sure you always use embeds for math, even if it is simple. You will make mistakes if you try to do math yourself.
|
|
941
1378
|
Use HTML entities to escape the delimiters.
|
|
@@ -951,7 +1388,31 @@ Examples:
|
|
|
951
1388
|
- `The result of 23.5 * 4.2 is {open_delim}math:23.5 * 4.2 | .2f{close_delim}` (Embeds calculated result with 2 decimal places)
|
|
952
1389
|
|
|
953
1390
|
The following embeds are resolved *late* (by the gateway before final display):
|
|
954
|
-
- `{open_delim}artifact_return:filename[:version]{close_delim}`:
|
|
1391
|
+
- `{open_delim}artifact_return:filename[:version]{close_delim}`: Attaches an artifact to your message so the user receives the file. The embed itself is removed from the text.
|
|
1392
|
+
|
|
1393
|
+
**CRITICAL - Returning Artifacts to Users:**
|
|
1394
|
+
Only artifacts created with the `{open_delim}save_artifact:...{close_delim}` fenced block syntax are automatically sent to the user.
|
|
1395
|
+
|
|
1396
|
+
**You MUST use artifact_return for:**
|
|
1397
|
+
- Artifacts created by tools (e.g., image generation, chart creation, file conversion)
|
|
1398
|
+
- Artifacts created by other agents you called
|
|
1399
|
+
- Artifacts from MCP servers
|
|
1400
|
+
|
|
1401
|
+
**When deciding whether to return an artifact:**
|
|
1402
|
+
- Return artifacts the user explicitly requested or that answer their question
|
|
1403
|
+
- Return final outputs (charts, reports, images, documents)
|
|
1404
|
+
- Do NOT return intermediate/temporary artifacts (e.g., temp files, internal data)
|
|
1405
|
+
|
|
1406
|
+
**Example - Tool creates an image:**
|
|
1407
|
+
User: "Create a chart of sales data"
|
|
1408
|
+
[You call a charting tool that creates sales_chart.png]
|
|
1409
|
+
Your response: "Here's the sales chart. {open_delim}artifact_return:sales_chart.png{close_delim}"
|
|
1410
|
+
|
|
1411
|
+
**Example - Agent creates a report:**
|
|
1412
|
+
User: "Generate a quarterly report"
|
|
1413
|
+
[You call ReportAgent which creates quarterly_report.pdf]
|
|
1414
|
+
Your response: "The quarterly report is ready. {open_delim}artifact_return:quarterly_report.pdf{close_delim}"
|
|
1415
|
+
"""
|
|
955
1416
|
|
|
956
1417
|
artifact_content_instruction = f"""
|
|
957
1418
|
- `{open_delim}artifact_content:filename[:version] {chain_delim} modifier1:value1 {chain_delim} ... {chain_delim} format:output_format{close_delim}`: Embeds artifact content after applying a chain of modifiers. This is resolved *late* (typically by a gateway before final display).
|
|
@@ -960,23 +1421,18 @@ The following embeds are resolved *late* (by the gateway before final display):
|
|
|
960
1421
|
- Available modifiers: {modifier_list}.
|
|
961
1422
|
- The `format:output_format` step *must* be the last step in the chain. Supported formats include `text`, `datauri`, `json`, `json_pretty`, `csv`. Formatting as datauri, will include the data URI prefix, so do not add it yourself.
|
|
962
1423
|
- Use `artifact_meta` first to check size; embedding large files may fail.
|
|
963
|
-
-
|
|
964
|
-
-
|
|
965
|
-
-
|
|
966
|
-
|
|
967
|
-
- If the input data is a **list** (e.g., from `jsonpath` or a JSON array), it's available under `items`.
|
|
968
|
-
- If the input data is a **dictionary** (e.g., from a JSON object), its keys are directly available (e.g., `{{{{key1}}}}`).
|
|
969
|
-
- If the input data is a **plain string** (and not auto-parsed as CSV), it's available under `text`.
|
|
970
|
-
- The template filename can include a version (e.g., `template.mustache:2`). Defaults to latest.
|
|
971
|
-
- The template itself can contain `«artifact_content:...»` embeds, which will be resolved before rendering.
|
|
1424
|
+
- Efficient workflows for large artifacts:
|
|
1425
|
+
- To extract specific line ranges: `load_artifact(filename, version, include_line_numbers=True)` to identify lines, then use `slice_lines:start:end` modifier to extract that range.
|
|
1426
|
+
- To fill templates with many placeholders: use `artifact_search_and_replace_regex` with `replacements` array (single atomic operation instead of multiple calls).
|
|
1427
|
+
- Line numbers are display-only; `slice_lines` always operates on original content.
|
|
972
1428
|
- Examples:
|
|
973
1429
|
- `<img src="{open_delim}artifact_content:image.png {chain_delim} format:datauri{close_delim}`"> (Embed image as data URI - NOTE that this includes the datauri prefix. Do not add it yourself.)
|
|
974
1430
|
- `{open_delim}artifact_content:data.json {chain_delim} jsonpath:$.items[*] {chain_delim} select_fields:name,status {chain_delim} format:json_pretty{close_delim}` (Extract and format JSON fields)
|
|
975
1431
|
- `{open_delim}artifact_content:logs.txt {chain_delim} grep:ERROR {chain_delim} head:10 {chain_delim} format:text{close_delim}` (Get first 10 error lines)
|
|
976
|
-
- `{open_delim}artifact_content:products.csv {chain_delim} apply_to_template:product_table.html.mustache {chain_delim} format:text{close_delim}` (CSV is auto-parsed to `headers` and `data_rows` for the HTML template)
|
|
977
1432
|
- `{open_delim}artifact_content:config.json {chain_delim} jsonpath:$.userPreferences.theme {chain_delim} format:text{close_delim}` (Extract a single value from a JSON artifact)
|
|
978
|
-
- `{open_delim}artifact_content:sensor_readings.csv {chain_delim} filter_rows_eq:status:critical {chain_delim} select_cols:timestamp,sensor_id,value {chain_delim} format:csv{close_delim}` (Filter critical sensor readings and select specific columns, output as CSV)
|
|
979
1433
|
- `{open_delim}artifact_content:server.log {chain_delim} tail:100 {chain_delim} grep:WARN {chain_delim} format:text{close_delim}` (Get warning lines from the last 100 lines of a log file)
|
|
1434
|
+
- `{open_delim}artifact_content:template.html {chain_delim} slice_lines:10:50 {chain_delim} format:text{close_delim}` (Extract lines 10-50 from a large file)
|
|
1435
|
+
- `<img src="{open_delim}artifact_content:diagram.png {chain_delim} format:datauri{close_delim}`"> (Embed an PNG diagram as a data URI)`
|
|
980
1436
|
"""
|
|
981
1437
|
|
|
982
1438
|
final_instruction = base_instruction
|
|
@@ -989,6 +1445,64 @@ Ensure the syntax is exactly `{open_delim}type:expression{close_delim}` or `{ope
|
|
|
989
1445
|
return final_instruction
|
|
990
1446
|
|
|
991
1447
|
|
|
1448
|
+
def _generate_conversation_flow_instruction() -> str:
|
|
1449
|
+
"""Generates instruction text for conversation flow and response formatting."""
|
|
1450
|
+
open_delim = EMBED_DELIMITER_OPEN
|
|
1451
|
+
close_delim = EMBED_DELIMITER_CLOSE
|
|
1452
|
+
return f"""\
|
|
1453
|
+
**Conversation Flow and Response Formatting:**
|
|
1454
|
+
|
|
1455
|
+
**CRITICAL: Minimize Narration - Maximize Results**
|
|
1456
|
+
|
|
1457
|
+
You do NOT need to produce visible text on every turn. Many turns should contain ONLY status updates and tool calls, with NO visible text at all.
|
|
1458
|
+
Only produce visible text when you have actual results, answers, or insights to share with the user.
|
|
1459
|
+
|
|
1460
|
+
Response Content Rules:
|
|
1461
|
+
1. Visible responses should contain ONLY:
|
|
1462
|
+
- Direct answers to the user's question
|
|
1463
|
+
- Analysis and insights derived from tool results
|
|
1464
|
+
- Final results and data
|
|
1465
|
+
- Follow-up questions when needed
|
|
1466
|
+
- Plans for complex multi-step tasks
|
|
1467
|
+
|
|
1468
|
+
2. DO NOT include visible text for:
|
|
1469
|
+
- Process narration ("Let me...", "I'll...", "Now I will...")
|
|
1470
|
+
- Acknowledgments of tool calls ("I'm calling...", "Searching...")
|
|
1471
|
+
- Descriptions of what you're about to do
|
|
1472
|
+
- Play-by-play commentary on your actions
|
|
1473
|
+
- Transitional phrases between tool calls
|
|
1474
|
+
|
|
1475
|
+
3. Use invisible status_update embeds for ALL process updates:
|
|
1476
|
+
- "Searching for..."
|
|
1477
|
+
- "Analyzing..."
|
|
1478
|
+
- "Creating..."
|
|
1479
|
+
- "Querying..."
|
|
1480
|
+
- "Calling agent X..."
|
|
1481
|
+
|
|
1482
|
+
4. NEVER mix process narration with status updates - if you use a status_update embed, do NOT repeat that information in visible text.
|
|
1483
|
+
|
|
1484
|
+
Examples:
|
|
1485
|
+
|
|
1486
|
+
**Excellent (no visible text, just status and tools):**
|
|
1487
|
+
"{open_delim}status_update:Retrieving sales data...{close_delim}" [then calls tool, no visible text]
|
|
1488
|
+
|
|
1489
|
+
**Good (visible text only contains results):**
|
|
1490
|
+
"{open_delim}status_update:Analyzing Q4 sales...{close_delim}" [calls tool]
|
|
1491
|
+
"Sales increased 23% in Q4, driven primarily by enterprise accounts."
|
|
1492
|
+
|
|
1493
|
+
**Bad (unnecessary narration):**
|
|
1494
|
+
"Let me retrieve the sales data for you." [then calls tool]
|
|
1495
|
+
|
|
1496
|
+
**Bad (narration mixed with results):**
|
|
1497
|
+
"I've analyzed the data and found that sales increased 23% in Q4."
|
|
1498
|
+
|
|
1499
|
+
**Bad (play-by-play commentary):**
|
|
1500
|
+
"Now I'll search for the information. After that I'll analyze it."
|
|
1501
|
+
|
|
1502
|
+
Remember: The user can see status updates and tool calls. You don't need to announce them in visible text.
|
|
1503
|
+
"""
|
|
1504
|
+
|
|
1505
|
+
|
|
992
1506
|
def _generate_tool_instructions_from_registry(
|
|
993
1507
|
active_tools: List[BuiltinTool],
|
|
994
1508
|
log_identifier: str,
|
|
@@ -1058,11 +1572,35 @@ def inject_dynamic_instructions_callback(
|
|
|
1058
1572
|
Parallel Tool Calling:
|
|
1059
1573
|
The system is capable of calling multiple tools in parallel to speed up processing. Please try to run tools in parallel when they don't depend on each other. This saves money and time, providing faster results to the user.
|
|
1060
1574
|
|
|
1575
|
+
**Response Formatting - CRITICAL**:
|
|
1576
|
+
In most cases when calling tools, you should produce NO visible text at all - only status_update embeds and the tool calls themselves.
|
|
1577
|
+
The user can see your tool calls and status updates, so narrating your actions is redundant and creates noise.
|
|
1578
|
+
|
|
1579
|
+
If you do include visible text:
|
|
1580
|
+
- It must contain actual results, insights, or answers - NOT process narration
|
|
1581
|
+
- Do NOT end with a colon (":") before tool calls, as this leaves it hanging
|
|
1582
|
+
- Prefer ending with a period (".") if you must include visible text
|
|
1583
|
+
|
|
1584
|
+
Examples:
|
|
1585
|
+
- BEST: "{open_delim}status_update:Searching database...{close_delim}" [then calls tool, NO visible text]
|
|
1586
|
+
- BAD: "Let me search for that information." [then calls tool]
|
|
1587
|
+
- BAD: "Searching for information..." [then calls tool]
|
|
1588
|
+
|
|
1589
|
+
**CRITICAL - No Links From Training Data**:
|
|
1590
|
+
- DO NOT include URLs, links, or markdown links from your training data in responses
|
|
1591
|
+
- NEVER include markdown links like [text](url) or raw URLs like https://example.com unless they came from a tool result
|
|
1592
|
+
- If a delegated agent's response contains [[cite:searchN]] citations, those are properly formatted - preserve them exactly
|
|
1593
|
+
- If a delegated agent's response has no links, do NOT add any links yourself
|
|
1594
|
+
- The ONLY acceptable links are those returned by tools (web search, deep research, etc.) with proper citation format
|
|
1595
|
+
- Your role is to coordinate and present results, not to augment them with links from your training data
|
|
1596
|
+
|
|
1061
1597
|
Embeds in responses from agents:
|
|
1062
|
-
To be efficient, agents may
|
|
1598
|
+
To be efficient, peer agents may respond with artifact_content in their responses. These will not be resolved until they are sent back to a gateway. If it makes
|
|
1063
1599
|
sense, just carry that embed forward to your response to the user. For example, if you ask for an org chart from another agent and its response contains an embed like
|
|
1064
1600
|
`{open_delim}artifact_content:org_chart.md{close_delim}`, you can just include that embed in your response to the user. The gateway will resolve it and display the org chart.
|
|
1065
1601
|
|
|
1602
|
+
Similarly, template_liquid blocks in peer agent responses can be carried forward to your response to the user for resolution by the gateway.
|
|
1603
|
+
|
|
1066
1604
|
When faced with a complex goal or request that involves multiple steps, data retrieval, or artifact summarization to produce a new report or document, you MUST first create a plan.
|
|
1067
1605
|
Simple, direct requests like 'create an image of a dog' or 'write an email to thank my boss' do not require a plan.
|
|
1068
1606
|
|
|
@@ -1074,8 +1612,11 @@ If a plan is created:
|
|
|
1074
1612
|
|
|
1075
1613
|
"""
|
|
1076
1614
|
injected_instructions.append(planning_instruction)
|
|
1077
|
-
|
|
1078
|
-
|
|
1615
|
+
|
|
1616
|
+
# Add the consolidated block instructions
|
|
1617
|
+
injected_instructions.append(_generate_fenced_artifact_instruction())
|
|
1618
|
+
injected_instructions.append(_generate_inline_template_instruction())
|
|
1619
|
+
injected_instructions.append(_generate_fenced_block_syntax_rules())
|
|
1079
1620
|
|
|
1080
1621
|
agent_instruction_str: Optional[str] = None
|
|
1081
1622
|
if host_component._agent_system_instruction_callback:
|
|
@@ -1143,6 +1684,11 @@ If a plan is created:
|
|
|
1143
1684
|
include_artifact_content_instr,
|
|
1144
1685
|
)
|
|
1145
1686
|
|
|
1687
|
+
instruction = _generate_conversation_flow_instruction()
|
|
1688
|
+
if instruction:
|
|
1689
|
+
injected_instructions.append(instruction)
|
|
1690
|
+
log.debug("%s Prepared conversation flow instructions.", log_identifier)
|
|
1691
|
+
|
|
1146
1692
|
if active_builtin_tools:
|
|
1147
1693
|
instruction = _generate_tool_instructions_from_registry(
|
|
1148
1694
|
active_builtin_tools, log_identifier
|
|
@@ -1212,6 +1758,8 @@ If a plan is created:
|
|
|
1212
1758
|
e_last_call,
|
|
1213
1759
|
)
|
|
1214
1760
|
|
|
1761
|
+
injected_instructions.append(_generate_examples_instruction())
|
|
1762
|
+
|
|
1215
1763
|
if injected_instructions:
|
|
1216
1764
|
combined_instructions = "\n\n---\n\n".join(injected_instructions)
|
|
1217
1765
|
if llm_request.config is None:
|
|
@@ -1730,9 +2278,11 @@ def notify_tool_invocation_start_callback(
|
|
|
1730
2278
|
tool_args=serializable_args,
|
|
1731
2279
|
function_call_id=tool_context.function_call_id,
|
|
1732
2280
|
)
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2281
|
+
host_component.publish_data_signal_from_thread(
|
|
2282
|
+
a2a_context=a2a_context,
|
|
2283
|
+
signal_data=tool_data,
|
|
2284
|
+
skip_buffer_flush=False,
|
|
2285
|
+
log_identifier=log_identifier,
|
|
1736
2286
|
)
|
|
1737
2287
|
log.debug(
|
|
1738
2288
|
"%s Scheduled tool_invocation_start notification.",
|
|
@@ -1803,9 +2353,11 @@ def notify_tool_execution_result_callback(
|
|
|
1803
2353
|
result_data=serializable_response,
|
|
1804
2354
|
function_call_id=tool_context.function_call_id,
|
|
1805
2355
|
)
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
2356
|
+
host_component.publish_data_signal_from_thread(
|
|
2357
|
+
a2a_context=a2a_context,
|
|
2358
|
+
signal_data=tool_data,
|
|
2359
|
+
skip_buffer_flush=False,
|
|
2360
|
+
log_identifier=log_identifier,
|
|
1809
2361
|
)
|
|
1810
2362
|
log.debug(
|
|
1811
2363
|
"%s Scheduled tool_result notification for function call ID %s.",
|
|
@@ -1903,3 +2455,76 @@ def auto_continue_on_max_tokens_callback(
|
|
|
1903
2455
|
)
|
|
1904
2456
|
|
|
1905
2457
|
return hijacked_response
|
|
2458
|
+
|
|
2459
|
+
|
|
2460
|
+
def preregister_long_running_tools_callback(
|
|
2461
|
+
callback_context: CallbackContext,
|
|
2462
|
+
llm_response: LlmResponse,
|
|
2463
|
+
host_component: "SamAgentComponent",
|
|
2464
|
+
) -> Optional[LlmResponse]:
|
|
2465
|
+
"""
|
|
2466
|
+
ADK after_model_callback to pre-register all long-running tool calls
|
|
2467
|
+
before any tool execution begins. This prevents race conditions where
|
|
2468
|
+
one tool completes before another has registered.
|
|
2469
|
+
|
|
2470
|
+
The race condition occurs because tools are executed via asyncio.gather
|
|
2471
|
+
(non-deterministic order) and each tool calls register_parallel_call_sent()
|
|
2472
|
+
inside its run_async(). If Tool A completes before Tool B even registers,
|
|
2473
|
+
the system thinks all calls are done (completed=1, total=1).
|
|
2474
|
+
|
|
2475
|
+
By pre-registering all long-running tools in this callback (which runs
|
|
2476
|
+
BEFORE tool execution), we ensure the total count is set correctly upfront.
|
|
2477
|
+
"""
|
|
2478
|
+
log_identifier = "[Callback:PreregisterLongRunning]"
|
|
2479
|
+
|
|
2480
|
+
# Only process non-partial responses with function calls
|
|
2481
|
+
if llm_response.partial:
|
|
2482
|
+
return None
|
|
2483
|
+
|
|
2484
|
+
if not llm_response.content or not llm_response.content.parts:
|
|
2485
|
+
return None
|
|
2486
|
+
|
|
2487
|
+
# Find all long-running tool calls (identified by peer_ prefix)
|
|
2488
|
+
long_running_calls = []
|
|
2489
|
+
for part in llm_response.content.parts:
|
|
2490
|
+
if part.function_call:
|
|
2491
|
+
tool_name = part.function_call.name
|
|
2492
|
+
if tool_name.startswith(PEER_TOOL_PREFIX):
|
|
2493
|
+
long_running_calls.append(part.function_call)
|
|
2494
|
+
|
|
2495
|
+
if not long_running_calls:
|
|
2496
|
+
return None
|
|
2497
|
+
|
|
2498
|
+
# Get task context
|
|
2499
|
+
a2a_context = callback_context.state.get("a2a_context")
|
|
2500
|
+
if not a2a_context:
|
|
2501
|
+
log.warning("%s No a2a_context, cannot pre-register tools", log_identifier)
|
|
2502
|
+
return None
|
|
2503
|
+
|
|
2504
|
+
logical_task_id = a2a_context.get("logical_task_id")
|
|
2505
|
+
invocation_id = callback_context._invocation_context.invocation_id
|
|
2506
|
+
|
|
2507
|
+
with host_component.active_tasks_lock:
|
|
2508
|
+
task_context = host_component.active_tasks.get(logical_task_id)
|
|
2509
|
+
|
|
2510
|
+
if not task_context:
|
|
2511
|
+
log.warning(
|
|
2512
|
+
"%s TaskContext not found for %s, cannot pre-register",
|
|
2513
|
+
log_identifier,
|
|
2514
|
+
logical_task_id,
|
|
2515
|
+
)
|
|
2516
|
+
return None
|
|
2517
|
+
|
|
2518
|
+
# Pre-register ALL long-running calls atomically
|
|
2519
|
+
for fc in long_running_calls:
|
|
2520
|
+
task_context.register_parallel_call_sent(invocation_id)
|
|
2521
|
+
|
|
2522
|
+
log.info(
|
|
2523
|
+
"%s Pre-registered %d long-running tool call(s) for invocation %s (task %s)",
|
|
2524
|
+
log_identifier,
|
|
2525
|
+
len(long_running_calls),
|
|
2526
|
+
invocation_id,
|
|
2527
|
+
logical_task_id,
|
|
2528
|
+
)
|
|
2529
|
+
|
|
2530
|
+
return None # Don't alter the response
|