solace-agent-mesh 1.6.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/app_llm_agent.py +26 -0
- solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +165 -1
- solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +163 -0
- solace_agent_mesh/agent/adk/callbacks.py +852 -109
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +234 -36
- 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 +77 -21
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +24 -137
- solace_agent_mesh/agent/adk/runner.py +85 -20
- solace_agent_mesh/agent/adk/schema_migration.py +88 -0
- solace_agent_mesh/agent/adk/services.py +94 -18
- solace_agent_mesh/agent/adk/setup.py +281 -65
- solace_agent_mesh/agent/adk/stream_parser.py +231 -37
- solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +472 -137
- solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
- 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/app.py +3 -2
- 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 +91 -3
- solace_agent_mesh/agent/sac/component.py +591 -157
- solace_agent_mesh/agent/sac/patch_adk.py +8 -16
- solace_agent_mesh/agent/sac/task_execution_context.py +146 -4
- 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 +710 -171
- solace_agent_mesh/agent/tools/deep_research_tools.py +2161 -0
- solace_agent_mesh/agent/tools/dynamic_tool.py +2 -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 +57 -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 +248 -6
- 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/2987107d.a80604f9.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/631738c7.fa471607.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/6a520c9d.b6e3f2ce.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.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/8b032486.91a91afc.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/ad87452a.9d73dad6.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/da0b5bad.b62f7b08.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.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/ff4d71f2.74710fc1.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 +82 -68
- 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 +182 -0
- 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 -49
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +29 -30
- 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 +90 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +17 -16
- 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 +162 -171
- 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 +86 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +23 -19
- 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 +112 -87
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +87 -64
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
- 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 -37
- 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 +311 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +39 -42
- 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 +251 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
- 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/docs_cmd.py +4 -1
- 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-BTf6dqwp.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-CGk8Suyh.js +565 -0
- 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/events.py +2 -1
- solace_agent_mesh/common/a2a/protocol.py +5 -0
- solace_agent_mesh/common/a2a/types.py +2 -1
- solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +23 -6
- solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
- solace_agent_mesh/common/agent_registry.py +38 -11
- solace_agent_mesh/common/data_parts.py +144 -4
- 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 +97 -19
- solace_agent_mesh/common/sam_events/event_service.py +2 -2
- solace_agent_mesh/common/services/employee_service.py +1 -1
- solace_agent_mesh/common/utils/embeds/constants.py +1 -0
- solace_agent_mesh/common/utils/embeds/converter.py +1 -8
- solace_agent_mesh/common/utils/embeds/modifiers.py +4 -28
- solace_agent_mesh/common/utils/embeds/resolver.py +152 -31
- solace_agent_mesh/common/utils/embeds/types.py +9 -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/pydantic_utils.py +90 -3
- 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/__init__.py +1 -0
- solace_agent_mesh/gateway/adapter/base.py +170 -0
- solace_agent_mesh/gateway/adapter/types.py +230 -0
- solace_agent_mesh/gateway/base/app.py +39 -2
- solace_agent_mesh/gateway/base/auth_interface.py +103 -0
- solace_agent_mesh/gateway/base/component.py +1027 -151
- solace_agent_mesh/gateway/generic/__init__.py +1 -0
- solace_agent_mesh/gateway/generic/app.py +50 -0
- solace_agent_mesh/gateway/generic/component.py +894 -0
- solace_agent_mesh/gateway/http_sse/alembic/env.py +0 -7
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +109 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
- 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 +40 -11
- solace_agent_mesh/gateway/http_sse/component.py +285 -160
- solace_agent_mesh/gateway/http_sse/dependencies.py +149 -114
- solace_agent_mesh/gateway/http_sse/main.py +68 -450
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +2 -2
- solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +26 -3
- solace_agent_mesh/gateway/http_sse/repository/entities/task.py +7 -0
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +114 -6
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +13 -0
- solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
- solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -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 +8 -2
- solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +8 -1
- solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
- solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +177 -11
- 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 +256 -58
- solace_agent_mesh/gateway/http_sse/routers/auth.py +168 -134
- solace_agent_mesh/gateway/http_sse/routers/config.py +302 -8
- 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/project_requests.py +48 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +14 -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 +31 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +5 -2
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
- solace_agent_mesh/gateway/http_sse/routers/people.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/projects.py +768 -0
- solace_agent_mesh/gateway/http_sse/routers/prompts.py +1416 -0
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +167 -7
- solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
- solace_agent_mesh/gateway/http_sse/routers/sse.py +131 -8
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +670 -18
- 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 +92 -9
- 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 +930 -0
- solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
- solace_agent_mesh/gateway/http_sse/services/session_service.py +361 -12
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +354 -4
- solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
- solace_agent_mesh/gateway/http_sse/sse_manager.py +286 -166
- 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 +19 -5
- 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.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/METADATA +31 -12
- solace_agent_mesh-1.13.2.dist-info/RECORD +591 -0
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/WHEEL +1 -1
- solace_agent_mesh/agent/adk/adk_llm.txt +0 -232
- 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 -142
- solace_agent_mesh/agent/agent_llm.txt +0 -378
- 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/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 -57
- solace_agent_mesh/agent/testing/testing_llm_detail.txt +0 -68
- solace_agent_mesh/agent/tools/tools_llm.txt +0 -263
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +0 -274
- solace_agent_mesh/agent/utils/utils_llm.txt +0 -138
- solace_agent_mesh/agent/utils/utils_llm_detail.txt +0 -149
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.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.76654dd9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/55b7b518.f2b1d1ba.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.4a5fbf39.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.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.b5ddb7a1.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.3e6dd091.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.d08a9466.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.6b9493d0.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1761248203150.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-CaY59VuC.js +0 -25
- solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
- solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-BEmvJSYz.js +0 -405
- solace_agent_mesh/common/a2a/a2a_llm.txt +0 -182
- solace_agent_mesh/common/a2a/a2a_llm_detail.txt +0 -193
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +0 -407
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +0 -736
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +0 -313
- solace_agent_mesh/common/common_llm.txt +0 -251
- 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 -80
- solace_agent_mesh/common/services/services_llm.txt +0 -363
- 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 -336
- 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 -224
- solace_agent_mesh/gateway/base/base_llm_detail.txt +0 -235
- solace_agent_mesh/gateway/gateway_llm.txt +0 -373
- solace_agent_mesh/gateway/gateway_llm_detail.txt +0 -3885
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +0 -295
- solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +0 -155
- 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 -263
- solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +0 -266
- solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +0 -340
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +0 -346
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +0 -83
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +0 -107
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +0 -314
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +0 -297
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +0 -146
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +0 -285
- 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-1.6.1.dist-info/RECORD +0 -525
- /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.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.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.6.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,18 +238,49 @@ 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")
|
|
245
|
+
if filename == "unknown_artifact":
|
|
246
|
+
log.warning(
|
|
247
|
+
"%s Fenced artifact block started without a 'filename' parameter.",
|
|
248
|
+
log_identifier,
|
|
249
|
+
)
|
|
250
|
+
description = event.params.get("description")
|
|
251
|
+
if filename == "unknown_artifact":
|
|
252
|
+
log.warning(
|
|
253
|
+
"%s Fenced artifact block started without a 'filename' parameter.",
|
|
254
|
+
log_identifier,
|
|
255
|
+
)
|
|
173
256
|
if a2a_context:
|
|
257
|
+
status_text = f"Receiving artifact `{filename}`..."
|
|
258
|
+
if description:
|
|
259
|
+
status_text = (
|
|
260
|
+
f"Receiving artifact `{filename}`: {description}"
|
|
261
|
+
)
|
|
174
262
|
progress_data = AgentProgressUpdateData(
|
|
175
|
-
status_text=
|
|
263
|
+
status_text=status_text
|
|
176
264
|
)
|
|
177
265
|
await _publish_data_part_status_update(
|
|
178
266
|
host_component, a2a_context, progress_data
|
|
179
267
|
)
|
|
268
|
+
# Also send an initial in-progress event to create the UI bubble
|
|
269
|
+
artifact_progress_data = ArtifactCreationProgressData(
|
|
270
|
+
filename=filename,
|
|
271
|
+
description=description,
|
|
272
|
+
status="in-progress",
|
|
273
|
+
bytes_transferred=0,
|
|
274
|
+
artifact_chunk=None,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
await _publish_data_part_status_update(
|
|
278
|
+
host_component, a2a_context, artifact_progress_data
|
|
279
|
+
)
|
|
180
280
|
params_str = " ".join(
|
|
181
281
|
[f'{k}="{v}"' for k, v in event.params.items()]
|
|
182
282
|
)
|
|
183
|
-
original_text = f"
|
|
283
|
+
original_text = f"{ARTIFACT_BLOCK_DELIMITER_OPEN}save_artifact: {params_str}\n"
|
|
184
284
|
session.state["artifact_block_original_text"] = original_text
|
|
185
285
|
|
|
186
286
|
elif isinstance(event, BlockProgressedEvent):
|
|
@@ -189,20 +289,44 @@ async def process_artifact_blocks_callback(
|
|
|
189
289
|
log_identifier,
|
|
190
290
|
event.buffered_size,
|
|
191
291
|
)
|
|
192
|
-
params =
|
|
292
|
+
params = event.params
|
|
193
293
|
filename = params.get("filename", "unknown_artifact")
|
|
294
|
+
if filename == "unknown_artifact":
|
|
295
|
+
log.warning(
|
|
296
|
+
"%s Fenced artifact block progressed without a 'filename' parameter.",
|
|
297
|
+
log_identifier,
|
|
298
|
+
)
|
|
194
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
|
+
|
|
195
308
|
progress_data = ArtifactCreationProgressData(
|
|
196
309
|
filename=filename,
|
|
197
|
-
|
|
198
|
-
|
|
310
|
+
description=params.get("description"),
|
|
311
|
+
status="in-progress",
|
|
312
|
+
bytes_transferred=event.buffered_size,
|
|
313
|
+
artifact_chunk=resolved_chunk, # Resolved chunk
|
|
199
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
|
|
320
|
+
)
|
|
321
|
+
new_char_count = previous_char_count + len(event.chunk)
|
|
322
|
+
session.state["artifact_chars_sent"] = new_char_count
|
|
323
|
+
|
|
200
324
|
await _publish_data_part_status_update(
|
|
201
325
|
host_component, a2a_context, progress_data
|
|
202
326
|
)
|
|
203
327
|
|
|
204
328
|
elif isinstance(event, BlockCompletedEvent):
|
|
205
|
-
log.
|
|
329
|
+
log.debug(
|
|
206
330
|
"%s Event: BlockCompleted. Content length: %d",
|
|
207
331
|
log_identifier,
|
|
208
332
|
len(event.content),
|
|
@@ -236,6 +360,18 @@ async def process_artifact_blocks_callback(
|
|
|
236
360
|
"original_text": original_text,
|
|
237
361
|
}
|
|
238
362
|
)
|
|
363
|
+
if a2a_context:
|
|
364
|
+
if not filename or not filename.strip():
|
|
365
|
+
filename = "unknown_artifact"
|
|
366
|
+
progress_data = ArtifactCreationProgressData(
|
|
367
|
+
filename=filename or "unknown_artifact",
|
|
368
|
+
description=params.get("description"),
|
|
369
|
+
status="failed",
|
|
370
|
+
bytes_transferred=0,
|
|
371
|
+
)
|
|
372
|
+
await _publish_data_part_status_update(
|
|
373
|
+
host_component, a2a_context, progress_data
|
|
374
|
+
)
|
|
239
375
|
continue
|
|
240
376
|
|
|
241
377
|
kwargs_for_call = {
|
|
@@ -257,7 +393,6 @@ async def process_artifact_blocks_callback(
|
|
|
257
393
|
log_identifier,
|
|
258
394
|
params["schema_max_keys"],
|
|
259
395
|
)
|
|
260
|
-
|
|
261
396
|
wrapped_creator = ADKToolWrapper(
|
|
262
397
|
original_func=_internal_create_artifact,
|
|
263
398
|
tool_config=None, # No specific config for this internal tool
|
|
@@ -288,6 +423,13 @@ async def process_artifact_blocks_callback(
|
|
|
288
423
|
version_for_tool,
|
|
289
424
|
logical_task_id,
|
|
290
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
|
+
)
|
|
291
433
|
else:
|
|
292
434
|
log.warning(
|
|
293
435
|
"%s No logical_task_id, cannot register inline artifact.",
|
|
@@ -299,19 +441,172 @@ async def process_artifact_blocks_callback(
|
|
|
299
441
|
log_identifier,
|
|
300
442
|
e_track,
|
|
301
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
|
+
|
|
477
|
+
# Publish completion status immediately via SSE
|
|
478
|
+
if a2a_context:
|
|
479
|
+
progress_data = ArtifactCreationProgressData(
|
|
480
|
+
filename=filename,
|
|
481
|
+
description=params.get("description"),
|
|
482
|
+
status="completed",
|
|
483
|
+
bytes_transferred=len(event.content),
|
|
484
|
+
mime_type=params.get("mime_type"),
|
|
485
|
+
version=version_for_tool,
|
|
486
|
+
)
|
|
487
|
+
await _publish_data_part_status_update(
|
|
488
|
+
host_component, a2a_context, progress_data
|
|
489
|
+
)
|
|
302
490
|
else:
|
|
303
491
|
status_for_tool = "error"
|
|
304
492
|
version_for_tool = 0
|
|
493
|
+
# Publish failure status immediately via SSE
|
|
494
|
+
if a2a_context:
|
|
495
|
+
progress_data = ArtifactCreationProgressData(
|
|
496
|
+
filename=filename,
|
|
497
|
+
description=params.get("description"),
|
|
498
|
+
status="failed",
|
|
499
|
+
bytes_transferred=len(event.content),
|
|
500
|
+
)
|
|
501
|
+
await _publish_data_part_status_update(
|
|
502
|
+
host_component, a2a_context, progress_data
|
|
503
|
+
)
|
|
305
504
|
|
|
306
505
|
session.state["completed_artifact_blocks_list"].append(
|
|
307
506
|
{
|
|
308
507
|
"filename": filename,
|
|
309
508
|
"version": version_for_tool,
|
|
310
509
|
"status": status_for_tool,
|
|
510
|
+
"description": params.get("description"),
|
|
511
|
+
"mime_type": params.get("mime_type"),
|
|
512
|
+
"bytes_transferred": len(event.content),
|
|
311
513
|
"original_text": original_text,
|
|
312
514
|
}
|
|
313
515
|
)
|
|
314
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
|
+
|
|
315
610
|
elif isinstance(event, BlockInvalidatedEvent):
|
|
316
611
|
log.debug(
|
|
317
612
|
"%s Event: BlockInvalidated. Rolled back: '%s'",
|
|
@@ -347,6 +642,11 @@ async def process_artifact_blocks_callback(
|
|
|
347
642
|
)
|
|
348
643
|
params = event.params
|
|
349
644
|
filename = params.get("filename", "unknown_artifact")
|
|
645
|
+
if filename == "unknown_artifact":
|
|
646
|
+
log.warning(
|
|
647
|
+
"%s Unterminated fenced artifact block is missing a valid 'filename'. Failing operation.",
|
|
648
|
+
log_identifier,
|
|
649
|
+
)
|
|
350
650
|
if (
|
|
351
651
|
"completed_artifact_blocks_list" not in session.state
|
|
352
652
|
or session.state["completed_artifact_blocks_list"] is None
|
|
@@ -392,8 +692,12 @@ async def process_artifact_blocks_callback(
|
|
|
392
692
|
len(completed_blocks_list),
|
|
393
693
|
)
|
|
394
694
|
|
|
695
|
+
# Get a2a_context for sending signals
|
|
696
|
+
a2a_context = callback_context.state.get("a2a_context")
|
|
697
|
+
|
|
395
698
|
tool_call_parts = []
|
|
396
699
|
for block_info in completed_blocks_list:
|
|
700
|
+
function_call_id = f"host-notify-{uuid.uuid4()}"
|
|
397
701
|
notify_tool_call = adk_types.FunctionCall(
|
|
398
702
|
name="_notify_artifact_save",
|
|
399
703
|
args={
|
|
@@ -401,10 +705,41 @@ async def process_artifact_blocks_callback(
|
|
|
401
705
|
"version": block_info["version"],
|
|
402
706
|
"status": block_info["status"],
|
|
403
707
|
},
|
|
404
|
-
id=
|
|
708
|
+
id=function_call_id,
|
|
405
709
|
)
|
|
406
710
|
tool_call_parts.append(adk_types.Part(function_call=notify_tool_call))
|
|
407
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
|
+
|
|
408
743
|
existing_parts = llm_response.content.parts if llm_response.content else []
|
|
409
744
|
final_existing_parts = existing_parts
|
|
410
745
|
|
|
@@ -419,6 +754,7 @@ async def process_artifact_blocks_callback(
|
|
|
419
754
|
session.state[parser_state_key] = None
|
|
420
755
|
session.state["completed_artifact_blocks_list"] = None
|
|
421
756
|
session.state["artifact_block_original_text"] = None
|
|
757
|
+
session.state["completed_template_blocks_list"] = None
|
|
422
758
|
log.debug("%s Cleaned up parser session state.", log_identifier)
|
|
423
759
|
|
|
424
760
|
return None
|
|
@@ -671,26 +1007,24 @@ async def manage_large_mcp_tool_responses_callback(
|
|
|
671
1007
|
message_parts_for_llm: list[str] = []
|
|
672
1008
|
|
|
673
1009
|
if needs_truncation_for_llm:
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
truncated_bytes = serialized_original_response_str.encode("utf-8")[
|
|
680
|
-
:adjusted_max_bytes
|
|
681
|
-
]
|
|
682
|
-
truncated_preview_str = (
|
|
683
|
-
truncated_bytes.decode("utf-8", "ignore") + truncation_suffix
|
|
684
|
-
)
|
|
685
|
-
|
|
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.
|
|
686
1014
|
final_llm_response_dict["mcp_tool_output"] = {
|
|
687
|
-
"type": "
|
|
688
|
-
"
|
|
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.",
|
|
689
1017
|
}
|
|
690
1018
|
message_parts_for_llm.append(
|
|
691
|
-
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,
|
|
692
1027
|
)
|
|
693
|
-
log.debug("%s MCP tool output truncated for LLM.", log_identifier)
|
|
694
1028
|
|
|
695
1029
|
if needs_saving_as_artifact:
|
|
696
1030
|
if save_result and save_result.status in [
|
|
@@ -707,19 +1041,27 @@ async def manage_large_mcp_tool_responses_callback(
|
|
|
707
1041
|
filename = first_artifact.data_filename
|
|
708
1042
|
version = first_artifact.data_version
|
|
709
1043
|
if total_artifacts > 1:
|
|
710
|
-
|
|
711
|
-
f"The full response has been saved as {total_artifacts} artifacts, starting with '{filename}' (version {version})."
|
|
712
|
-
)
|
|
1044
|
+
artifact_msg = f"The full response has been saved as {total_artifacts} artifacts, starting with '{filename}' (version {version})."
|
|
713
1045
|
else:
|
|
714
|
-
|
|
715
|
-
|
|
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}").'
|
|
716
1053
|
)
|
|
1054
|
+
message_parts_for_llm.append(artifact_msg)
|
|
717
1055
|
elif save_result.fallback_artifact:
|
|
718
1056
|
filename = save_result.fallback_artifact.data_filename
|
|
719
1057
|
version = save_result.fallback_artifact.data_version
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
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)
|
|
723
1065
|
|
|
724
1066
|
log.debug(
|
|
725
1067
|
"%s Added saved artifact details to LLM response.", log_identifier
|
|
@@ -744,16 +1086,18 @@ async def manage_large_mcp_tool_responses_callback(
|
|
|
744
1086
|
and save_result.status in [McpSaveStatus.SUCCESS, McpSaveStatus.PARTIAL_SUCCESS]
|
|
745
1087
|
):
|
|
746
1088
|
if needs_truncation_for_llm:
|
|
747
|
-
|
|
1089
|
+
# Data was too large - withheld from LLM, only available via artifact
|
|
1090
|
+
final_llm_response_dict["status"] = "processed_saved_artifact_only"
|
|
748
1091
|
else:
|
|
749
1092
|
final_llm_response_dict["status"] = "processed_and_saved"
|
|
750
1093
|
elif needs_saving_as_artifact:
|
|
751
1094
|
if needs_truncation_for_llm:
|
|
752
|
-
final_llm_response_dict["status"] = "
|
|
1095
|
+
final_llm_response_dict["status"] = "processed_artifact_only_save_failed"
|
|
753
1096
|
else:
|
|
754
1097
|
final_llm_response_dict["status"] = "processed_save_failed"
|
|
755
1098
|
elif needs_truncation_for_llm:
|
|
756
|
-
|
|
1099
|
+
# This case shouldn't happen (truncation implies saving), but handle it
|
|
1100
|
+
final_llm_response_dict["status"] = "processed_data_withheld"
|
|
757
1101
|
else:
|
|
758
1102
|
final_llm_response_dict["status"] = "processed"
|
|
759
1103
|
|
|
@@ -769,34 +1113,121 @@ async def manage_large_mcp_tool_responses_callback(
|
|
|
769
1113
|
return final_llm_response_dict
|
|
770
1114
|
|
|
771
1115
|
|
|
1116
|
+
def _generate_fenced_block_syntax_rules() -> str:
|
|
1117
|
+
"""Generates the shared syntax rules for all fenced blocks."""
|
|
1118
|
+
open_delim = ARTIFACT_BLOCK_DELIMITER_OPEN
|
|
1119
|
+
close_delim = ARTIFACT_BLOCK_DELIMITER_CLOSE
|
|
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.
|
|
1123
|
+
|
|
1124
|
+
**EXACT SYNTAX (copy this pattern exactly):**
|
|
1125
|
+
{open_delim}keyword: parameter="value" ...
|
|
1126
|
+
The content for the block goes here.
|
|
1127
|
+
It can span multiple lines.
|
|
1128
|
+
{close_delim}
|
|
1129
|
+
|
|
1130
|
+
**CRITICAL FORMATTING RULES:**
|
|
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.
|
|
1139
|
+
|
|
1140
|
+
**COMMON ERRORS TO AVOID:**
|
|
1141
|
+
❌ WRONG: `{open_delim[0:1]}template_liquid:` (only 1 angle brackets)
|
|
1142
|
+
❌ WRONG: `{open_delim[0:2]}save_artifact:` (only 2 angle brackets)
|
|
1143
|
+
❌ WRONG: `{open_delim}save_artifact` (missing colon)
|
|
1144
|
+
✅ CORRECT: `{open_delim}save_artifact: filename="test.txt" mime_type="text/plain"`
|
|
1145
|
+
"""
|
|
1146
|
+
|
|
1147
|
+
|
|
772
1148
|
def _generate_fenced_artifact_instruction() -> str:
|
|
773
1149
|
"""Generates the instruction text for using fenced artifact blocks."""
|
|
774
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
|
|
775
1181
|
close_delim = ARTIFACT_BLOCK_DELIMITER_CLOSE
|
|
776
1182
|
return f"""\
|
|
777
|
-
**
|
|
778
|
-
To create an artifact from content you generate (like code, a report, or a document), you MUST use a special `save_artifact` block. This is the only reliable way to ensure your content is saved correctly.
|
|
1183
|
+
**Inline Liquid Templates (`{open_delim}template_liquid: ...`):**
|
|
779
1184
|
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
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 %}}
|
|
784
1209
|
{close_delim}
|
|
785
1210
|
|
|
786
|
-
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
- Do not surround a save_artifact block with '```' (triple backticks). This will create rendering issues.
|
|
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.
|
|
791
1215
|
|
|
792
|
-
|
|
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.
|
|
1223
|
+
"""
|
|
793
1224
|
|
|
794
1225
|
|
|
795
1226
|
def _generate_artifact_creation_instruction() -> str:
|
|
796
1227
|
return """
|
|
797
1228
|
**Creating Text-Based Artifacts:**
|
|
798
1229
|
|
|
799
|
-
|
|
1230
|
+
When to Create Text-based Artifacts:
|
|
800
1231
|
Create an artifact when the content provides value as a standalone file:
|
|
801
1232
|
- Content with special formatting (HTML, Markdown, CSS, structured markup) that requires proper rendering
|
|
802
1233
|
- Content explicitly intended for use outside this conversation (reports, emails, presentations, reference documents)
|
|
@@ -805,15 +1236,123 @@ def _generate_artifact_creation_instruction() -> str:
|
|
|
805
1236
|
- Substantial text documents
|
|
806
1237
|
- Technical documentation meant as reference material
|
|
807
1238
|
|
|
808
|
-
|
|
1239
|
+
When NOT to Create Text-based Artifacts:
|
|
809
1240
|
- Simple answers, explanations, or conversational responses
|
|
810
1241
|
- Brief advice, opinions, or quick information
|
|
811
|
-
- Short lists, summaries, or single paragraphs
|
|
1242
|
+
- Short lists, summaries, or single paragraphs
|
|
812
1243
|
- Temporary content only relevant to the immediate conversation
|
|
813
1244
|
- Basic explanations that don't require reference material
|
|
814
1245
|
"""
|
|
815
1246
|
|
|
816
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
|
+
|
|
817
1356
|
def _generate_embed_instruction(
|
|
818
1357
|
include_artifact_content: bool,
|
|
819
1358
|
log_identifier: str,
|
|
@@ -823,12 +1362,18 @@ def _generate_embed_instruction(
|
|
|
823
1362
|
close_delim = EMBED_DELIMITER_CLOSE
|
|
824
1363
|
chain_delim = EMBED_CHAIN_DELIMITER
|
|
825
1364
|
early_types = "`math`, `datetime`, `uuid`, `artifact_meta`"
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
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])
|
|
829
1372
|
|
|
830
1373
|
base_instruction = f"""\
|
|
831
|
-
|
|
1374
|
+
**Using Dynamic Embeds in Responses:**
|
|
1375
|
+
|
|
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
|
|
832
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.
|
|
833
1378
|
Use HTML entities to escape the delimiters.
|
|
834
1379
|
This host resolves the following embed types *early* (before sending to the LLM or tool): {early_types}. This means the embed is replaced with its resolved value.
|
|
@@ -836,31 +1381,59 @@ This host resolves the following embed types *early* (before sending to the LLM
|
|
|
836
1381
|
- `{open_delim}datetime:format_or_keyword{close_delim}`: Inserts current date/time. Use Python strftime format (e.g., `%Y-%m-%d`) or keywords (`iso`, `timestamp`, `date`, `time`, `now`).
|
|
837
1382
|
- `{open_delim}uuid:{close_delim}`: Inserts a random UUID.
|
|
838
1383
|
- `{open_delim}artifact_meta:filename[:version]{close_delim}`: Inserts a summary of the artifact's metadata (latest version if unspecified).
|
|
839
|
-
- `{open_delim}status_update:Your message here{close_delim}`: Generates an immediate, distinct status message event that is displayed to the user (e.g., 'Thinking...', 'Searching database...'). This message appears in a status area, not as part of the main chat conversation. Use this to provide interim feedback during processing.
|
|
1384
|
+
- `{open_delim}status_update:Your message here{close_delim}`: Generates an immediate, distinct status message event that is displayed to the user (e.g., 'Thinking...', 'Searching database...'). This message appears in a status area, not as part of the main chat conversation. Use this to provide interim feedback during processing.
|
|
1385
|
+
|
|
1386
|
+
Examples:
|
|
1387
|
+
- `{open_delim}status_update:Analyzing data...{close_delim}` (Shows 'Analyzing data...' as a status update)
|
|
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)
|
|
1389
|
+
|
|
1390
|
+
The following embeds are resolved *late* (by the gateway before final display):
|
|
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
|
+
"""
|
|
840
1416
|
|
|
841
1417
|
artifact_content_instruction = f"""
|
|
842
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).
|
|
1419
|
+
- If this embed resolves to binary content (like an image), it will be automatically converted into an attached file, similar to `artifact_return`.
|
|
843
1420
|
- Use `{chain_delim}` to separate the artifact identifier from the modifier steps and the final format step.
|
|
844
1421
|
- Available modifiers: {modifier_list}.
|
|
845
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.
|
|
846
1423
|
- Use `artifact_meta` first to check size; embedding large files may fail.
|
|
847
|
-
-
|
|
848
|
-
-
|
|
849
|
-
-
|
|
850
|
-
|
|
851
|
-
- If the input data is a **list** (e.g., from `jsonpath` or a JSON array), it's available under `items`.
|
|
852
|
-
- If the input data is a **dictionary** (e.g., from a JSON object), its keys are directly available (e.g., `{{{{key1}}}}`).
|
|
853
|
-
- If the input data is a **plain string** (and not auto-parsed as CSV), it's available under `text`.
|
|
854
|
-
- The template filename can include a version (e.g., `template.mustache:2`). Defaults to latest.
|
|
855
|
-
- 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.
|
|
856
1428
|
- Examples:
|
|
857
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.)
|
|
858
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)
|
|
859
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)
|
|
860
|
-
- `{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)
|
|
861
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)
|
|
862
|
-
- `{open_delim}artifact_content:
|
|
863
|
-
- `{open_delim}artifact_content:
|
|
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)`
|
|
1436
|
+
"""
|
|
864
1437
|
|
|
865
1438
|
final_instruction = base_instruction
|
|
866
1439
|
if include_artifact_content:
|
|
@@ -872,6 +1445,64 @@ Ensure the syntax is exactly `{open_delim}type:expression{close_delim}` or `{ope
|
|
|
872
1445
|
return final_instruction
|
|
873
1446
|
|
|
874
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
|
+
|
|
875
1506
|
def _generate_tool_instructions_from_registry(
|
|
876
1507
|
active_tools: List[BuiltinTool],
|
|
877
1508
|
log_identifier: str,
|
|
@@ -882,6 +1513,10 @@ def _generate_tool_instructions_from_registry(
|
|
|
882
1513
|
|
|
883
1514
|
instructions_by_category = defaultdict(list)
|
|
884
1515
|
for tool in sorted(active_tools, key=lambda t: (t.category, t.name)):
|
|
1516
|
+
# Skip internal tools (those starting with underscore)
|
|
1517
|
+
if tool.name.startswith("_"):
|
|
1518
|
+
continue
|
|
1519
|
+
|
|
885
1520
|
param_parts = []
|
|
886
1521
|
if tool.parameters and tool.parameters.properties:
|
|
887
1522
|
for name, schema in tool.parameters.properties.items():
|
|
@@ -937,27 +1572,51 @@ def inject_dynamic_instructions_callback(
|
|
|
937
1572
|
Parallel Tool Calling:
|
|
938
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.
|
|
939
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
|
+
|
|
940
1597
|
Embeds in responses from agents:
|
|
941
|
-
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
|
|
942
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
|
|
943
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.
|
|
944
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
|
+
|
|
945
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.
|
|
946
1605
|
Simple, direct requests like 'create an image of a dog' or 'write an email to thank my boss' do not require a plan.
|
|
947
1606
|
|
|
948
1607
|
If a plan is created:
|
|
949
1608
|
1. It should be a terse, hierarchical list describing the steps needed, with each checkbox item on its own line.
|
|
950
|
-
2. Use '
|
|
1609
|
+
2. Use '⬜' for pending items, '✅' for completed items, and '❌' for cancelled items.
|
|
951
1610
|
3. If the plan changes significantly during execution, restate the updated plan.
|
|
952
1611
|
4. As items are completed, update the plan to check them off.
|
|
953
1612
|
|
|
954
1613
|
"""
|
|
955
1614
|
injected_instructions.append(planning_instruction)
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
injected_instructions.append(
|
|
959
|
-
|
|
960
|
-
injected_instructions.append(
|
|
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())
|
|
961
1620
|
|
|
962
1621
|
agent_instruction_str: Optional[str] = None
|
|
963
1622
|
if host_component._agent_system_instruction_callback:
|
|
@@ -1025,6 +1684,11 @@ If a plan is created:
|
|
|
1025
1684
|
include_artifact_content_instr,
|
|
1026
1685
|
)
|
|
1027
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
|
+
|
|
1028
1692
|
if active_builtin_tools:
|
|
1029
1693
|
instruction = _generate_tool_instructions_from_registry(
|
|
1030
1694
|
active_builtin_tools, log_identifier
|
|
@@ -1094,6 +1758,8 @@ If a plan is created:
|
|
|
1094
1758
|
e_last_call,
|
|
1095
1759
|
)
|
|
1096
1760
|
|
|
1761
|
+
injected_instructions.append(_generate_examples_instruction())
|
|
1762
|
+
|
|
1097
1763
|
if injected_instructions:
|
|
1098
1764
|
combined_instructions = "\n\n---\n\n".join(injected_instructions)
|
|
1099
1765
|
if llm_request.config is None:
|
|
@@ -1612,9 +2278,11 @@ def notify_tool_invocation_start_callback(
|
|
|
1612
2278
|
tool_args=serializable_args,
|
|
1613
2279
|
function_call_id=tool_context.function_call_id,
|
|
1614
2280
|
)
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
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,
|
|
1618
2286
|
)
|
|
1619
2287
|
log.debug(
|
|
1620
2288
|
"%s Scheduled tool_invocation_start notification.",
|
|
@@ -1685,9 +2353,11 @@ def notify_tool_execution_result_callback(
|
|
|
1685
2353
|
result_data=serializable_response,
|
|
1686
2354
|
function_call_id=tool_context.function_call_id,
|
|
1687
2355
|
)
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
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,
|
|
1691
2361
|
)
|
|
1692
2362
|
log.debug(
|
|
1693
2363
|
"%s Scheduled tool_result notification for function call ID %s.",
|
|
@@ -1785,3 +2455,76 @@ def auto_continue_on_max_tokens_callback(
|
|
|
1785
2455
|
)
|
|
1786
2456
|
|
|
1787
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
|