solace-agent-mesh 1.11.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.
- solace_agent_mesh/__init__.py +0 -0
- solace_agent_mesh/agent/__init__.py +0 -0
- solace_agent_mesh/agent/adk/__init__.py +0 -0
- solace_agent_mesh/agent/adk/adk_llm.txt +226 -0
- solace_agent_mesh/agent/adk/adk_llm_detail.txt +566 -0
- 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 +52 -0
- solace_agent_mesh/agent/adk/artifacts/__init__.py +1 -0
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
- solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +545 -0
- solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +609 -0
- solace_agent_mesh/agent/adk/callbacks.py +2318 -0
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +406 -0
- solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +415 -0
- solace_agent_mesh/agent/adk/mcp_content_processor.py +666 -0
- solace_agent_mesh/agent/adk/models/lite_llm.py +1026 -0
- solace_agent_mesh/agent/adk/models/models_llm.txt +189 -0
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +132 -0
- solace_agent_mesh/agent/adk/runner.py +390 -0
- solace_agent_mesh/agent/adk/schema_migration.py +88 -0
- solace_agent_mesh/agent/adk/services.py +468 -0
- solace_agent_mesh/agent/adk/setup.py +1325 -0
- solace_agent_mesh/agent/adk/stream_parser.py +415 -0
- solace_agent_mesh/agent/adk/tool_wrapper.py +165 -0
- solace_agent_mesh/agent/agent_llm.txt +369 -0
- solace_agent_mesh/agent/agent_llm_detail.txt +1702 -0
- solace_agent_mesh/agent/protocol/__init__.py +0 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +2041 -0
- solace_agent_mesh/agent/protocol/protocol_llm.txt +81 -0
- solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +92 -0
- solace_agent_mesh/agent/proxies/__init__.py +0 -0
- solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +190 -0
- solace_agent_mesh/agent/proxies/a2a/app.py +56 -0
- solace_agent_mesh/agent/proxies/a2a/component.py +1585 -0
- solace_agent_mesh/agent/proxies/a2a/config.py +216 -0
- solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
- solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/base/app.py +100 -0
- solace_agent_mesh/agent/proxies/base/base_llm.txt +148 -0
- solace_agent_mesh/agent/proxies/base/component.py +816 -0
- solace_agent_mesh/agent/proxies/base/config.py +85 -0
- solace_agent_mesh/agent/proxies/base/proxy_task_context.py +19 -0
- solace_agent_mesh/agent/proxies/proxies_llm.txt +283 -0
- solace_agent_mesh/agent/sac/__init__.py +0 -0
- solace_agent_mesh/agent/sac/app.py +595 -0
- solace_agent_mesh/agent/sac/component.py +3668 -0
- solace_agent_mesh/agent/sac/patch_adk.py +103 -0
- solace_agent_mesh/agent/sac/sac_llm.txt +189 -0
- solace_agent_mesh/agent/sac/sac_llm_detail.txt +200 -0
- solace_agent_mesh/agent/sac/task_execution_context.py +415 -0
- solace_agent_mesh/agent/testing/__init__.py +3 -0
- solace_agent_mesh/agent/testing/debug_utils.py +135 -0
- solace_agent_mesh/agent/testing/testing_llm.txt +58 -0
- solace_agent_mesh/agent/testing/testing_llm_detail.txt +68 -0
- solace_agent_mesh/agent/tools/__init__.py +16 -0
- solace_agent_mesh/agent/tools/audio_tools.py +1740 -0
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +2500 -0
- solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +244 -0
- solace_agent_mesh/agent/tools/dynamic_tool.py +396 -0
- solace_agent_mesh/agent/tools/general_agent_tools.py +572 -0
- solace_agent_mesh/agent/tools/image_tools.py +1185 -0
- solace_agent_mesh/agent/tools/peer_agent_tool.py +363 -0
- solace_agent_mesh/agent/tools/registry.py +38 -0
- solace_agent_mesh/agent/tools/test_tools.py +136 -0
- solace_agent_mesh/agent/tools/time_tools.py +126 -0
- solace_agent_mesh/agent/tools/tool_config_types.py +93 -0
- solace_agent_mesh/agent/tools/tool_definition.py +53 -0
- solace_agent_mesh/agent/tools/tools_llm.txt +276 -0
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +275 -0
- solace_agent_mesh/agent/tools/web_tools.py +392 -0
- solace_agent_mesh/agent/utils/__init__.py +0 -0
- solace_agent_mesh/agent/utils/artifact_helpers.py +1353 -0
- solace_agent_mesh/agent/utils/config_parser.py +49 -0
- solace_agent_mesh/agent/utils/context_helpers.py +77 -0
- solace_agent_mesh/agent/utils/utils_llm.txt +152 -0
- solace_agent_mesh/agent/utils/utils_llm_detail.txt +149 -0
- solace_agent_mesh/assets/docs/404.html +16 -0
- solace_agent_mesh/assets/docs/assets/css/styles.8162edfb.css +1 -0
- solace_agent_mesh/assets/docs/assets/images/Solace_AI_Framework_With_Broker-85f0a306a9bcdd20b390b7a949f6d862.png +0 -0
- solace_agent_mesh/assets/docs/assets/images/sam-enterprise-credentials-b269f095349473118b2b33bdfcc40122.png +0 -0
- solace_agent_mesh/assets/docs/assets/js/032c2d61.f3d37824.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/0bcf40b7.c019ad46.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1001.0182a8bd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/1039.0bd46aa1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/149.b797a808.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/165.6a39807d.js.LICENSE.txt +9 -0
- solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2130.ab9fd314.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2131ec11.5c7a1f6e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2237.5e477fc6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js.LICENSE.txt +13 -0
- solace_agent_mesh/assets/docs/assets/js/2334.1cf50a20.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/240a0364.9ad94d1b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3219.adc1d663.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/341393d4.0fac2613.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3624.0eaa1fd0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/375.708d48db.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3834.b6cd790e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.28b7c67b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.2ddc75c0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/41adc471.48b12a4e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4250.95455b28.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4356.d169ab5b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4458.518e66fa.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4488.c7cc3442.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4494.6ee23046.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4855.fc4444b6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4866.22daefc0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4950.ca4caeda.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/509e993c.a1fbf45a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5388.7a136447.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5607.081356f8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5864.b0d0e9de.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.90a87880.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5e95c892.558d5167.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6063ff4c.ef84f702.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6143.0a1464c9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6395.e9c73649.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/66d4869e.b77431fc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6796.51d2c9b7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6976.379be23b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6978.ee0b945c.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/6fdfefc7.99de744e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7040.cb436723.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7195.412f418a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/722f809d.965da774.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7280.3fb73bdb.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/742f027b.46c07808.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7845.e33e7c4c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7900.69516146.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/81a99df0.2484b8d9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8356.8a379c04.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8567.4732c6b7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8573.cb04eda5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8577.1d54e766.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/8591.5d015485.js.LICENSE.txt +61 -0
- solace_agent_mesh/assets/docs/assets/js/8709.7ecd4047.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8731.6c1dbf0c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8908.f9d1b506.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9157.b4093d07.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9278.a4fd875d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/945fb41e.6f4cdffd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9616.b75c2f6d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9793.c6d16376.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9bb13469.b2333011.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a7bd4aaa.2204d2f7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.245ae0ef.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/aba21aa0.c42a534c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ad71b5ed.af3ecfd1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c198a0dc.8f31f867.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/ceb2a7a6.5d92d7d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.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/de5f4c65.e8241890.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e04b235d.52cb25ed.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.b1068f9b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.4488e34c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.250993bf.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/main.7acf7ace.js.LICENSE.txt +81 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.9e0813a2.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +154 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +99 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +90 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +107 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +166 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +101 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +219 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +92 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +29 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +55 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +110 -0
- 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 +345 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +83 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +84 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +25 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +47 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +85 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +60 -0
- 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 +144 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +191 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +128 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +54 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +34 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +55 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +267 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +142 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +116 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +86 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +164 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +140 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +57 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +72 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +102 -0
- 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 +37 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +86 -0
- 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 +247 -0
- 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 +184 -0
- 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 +75 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +54 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +85 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +41 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +290 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +78 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +25 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +78 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +160 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +142 -0
- 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 +100 -0
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +52 -0
- solace_agent_mesh/assets/docs/img/Solace_AI_Framework_With_Broker.png +0 -0
- solace_agent_mesh/assets/docs/img/logo.png +0 -0
- solace_agent_mesh/assets/docs/img/sac-flows.png +0 -0
- solace_agent_mesh/assets/docs/img/sac_parts_of_a_component.png +0 -0
- solace_agent_mesh/assets/docs/img/sam-enterprise-credentials.png +0 -0
- solace_agent_mesh/assets/docs/img/solace-logo-text.svg +18 -0
- solace_agent_mesh/assets/docs/img/solace-logo.png +0 -0
- solace_agent_mesh/assets/docs/lunr-index-1765810064709.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -0
- solace_agent_mesh/assets/docs/search-doc-1765810064709.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -0
- solace_agent_mesh/assets/docs/sitemap.xml +1 -0
- solace_agent_mesh/cli/__init__.py +1 -0
- solace_agent_mesh/cli/commands/__init__.py +0 -0
- solace_agent_mesh/cli/commands/add_cmd/__init__.py +15 -0
- solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +250 -0
- solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +729 -0
- solace_agent_mesh/cli/commands/add_cmd/gateway_cmd.py +322 -0
- solace_agent_mesh/cli/commands/add_cmd/web_add_agent_step.py +102 -0
- solace_agent_mesh/cli/commands/add_cmd/web_add_gateway_step.py +114 -0
- solace_agent_mesh/cli/commands/docs_cmd.py +60 -0
- solace_agent_mesh/cli/commands/eval_cmd.py +46 -0
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +439 -0
- solace_agent_mesh/cli/commands/init_cmd/broker_step.py +201 -0
- solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
- solace_agent_mesh/cli/commands/init_cmd/directory_step.py +28 -0
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +238 -0
- solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +365 -0
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +464 -0
- solace_agent_mesh/cli/commands/init_cmd/project_files_step.py +38 -0
- solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +119 -0
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +215 -0
- solace_agent_mesh/cli/commands/plugin_cmd/__init__.py +20 -0
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +137 -0
- solace_agent_mesh/cli/commands/plugin_cmd/build_cmd.py +86 -0
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +144 -0
- solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +306 -0
- solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +283 -0
- solace_agent_mesh/cli/commands/plugin_cmd/official_registry.py +175 -0
- solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +305 -0
- solace_agent_mesh/cli/commands/run_cmd.py +215 -0
- solace_agent_mesh/cli/main.py +52 -0
- solace_agent_mesh/cli/utils.py +262 -0
- solace_agent_mesh/client/webui/frontend/static/assets/authCallback-Dj3JtK42.js +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/client-ZKk9kEJ5.js +25 -0
- solace_agent_mesh/client/webui/frontend/static/assets/favicon-BLgzUch9.ico +0 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-BcUaNZ-Q.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-vjch4RYc.js +435 -0
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-BNV4kZN0.js +535 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +15 -0
- solace_agent_mesh/client/webui/frontend/static/index.html +16 -0
- 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/__init__.py +1 -0
- solace_agent_mesh/common/a2a/__init__.py +241 -0
- solace_agent_mesh/common/a2a/a2a_llm.txt +175 -0
- solace_agent_mesh/common/a2a/a2a_llm_detail.txt +193 -0
- solace_agent_mesh/common/a2a/artifact.py +368 -0
- solace_agent_mesh/common/a2a/events.py +213 -0
- solace_agent_mesh/common/a2a/message.py +375 -0
- solace_agent_mesh/common/a2a/protocol.py +689 -0
- solace_agent_mesh/common/a2a/task.py +127 -0
- solace_agent_mesh/common/a2a/translation.py +655 -0
- solace_agent_mesh/common/a2a/types.py +55 -0
- solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +445 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +736 -0
- solace_agent_mesh/common/a2a_spec/schemas/agent_progress_update.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +48 -0
- solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
- solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +41 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +330 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +48 -0
- solace_agent_mesh/common/agent_registry.py +122 -0
- solace_agent_mesh/common/common_llm.txt +230 -0
- solace_agent_mesh/common/common_llm_detail.txt +2562 -0
- solace_agent_mesh/common/constants.py +6 -0
- solace_agent_mesh/common/data_parts.py +150 -0
- solace_agent_mesh/common/exceptions.py +49 -0
- solace_agent_mesh/common/middleware/__init__.py +12 -0
- solace_agent_mesh/common/middleware/config_resolver.py +132 -0
- solace_agent_mesh/common/middleware/middleware_llm.txt +174 -0
- solace_agent_mesh/common/middleware/middleware_llm_detail.txt +185 -0
- solace_agent_mesh/common/middleware/registry.py +127 -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/sac/__init__.py +0 -0
- solace_agent_mesh/common/sac/sac_llm.txt +71 -0
- solace_agent_mesh/common/sac/sac_llm_detail.txt +82 -0
- solace_agent_mesh/common/sac/sam_component_base.py +730 -0
- solace_agent_mesh/common/sam_events/__init__.py +9 -0
- solace_agent_mesh/common/sam_events/event_service.py +208 -0
- solace_agent_mesh/common/sam_events/sam_events_llm.txt +104 -0
- solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +115 -0
- solace_agent_mesh/common/services/__init__.py +4 -0
- solace_agent_mesh/common/services/employee_service.py +164 -0
- solace_agent_mesh/common/services/identity_service.py +134 -0
- solace_agent_mesh/common/services/providers/__init__.py +4 -0
- solace_agent_mesh/common/services/providers/local_file_identity_service.py +151 -0
- solace_agent_mesh/common/services/providers/providers_llm.txt +81 -0
- solace_agent_mesh/common/services/services_llm.txt +368 -0
- solace_agent_mesh/common/services/services_llm_detail.txt +459 -0
- solace_agent_mesh/common/utils/__init__.py +7 -0
- solace_agent_mesh/common/utils/artifact_utils.py +31 -0
- solace_agent_mesh/common/utils/asyncio_macos_fix.py +88 -0
- solace_agent_mesh/common/utils/embeds/__init__.py +33 -0
- solace_agent_mesh/common/utils/embeds/constants.py +56 -0
- solace_agent_mesh/common/utils/embeds/converter.py +447 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +220 -0
- solace_agent_mesh/common/utils/embeds/evaluators.py +395 -0
- solace_agent_mesh/common/utils/embeds/modifiers.py +793 -0
- solace_agent_mesh/common/utils/embeds/resolver.py +967 -0
- solace_agent_mesh/common/utils/embeds/types.py +23 -0
- solace_agent_mesh/common/utils/in_memory_cache.py +108 -0
- solace_agent_mesh/common/utils/initializer.py +52 -0
- solace_agent_mesh/common/utils/log_formatters.py +64 -0
- solace_agent_mesh/common/utils/message_utils.py +80 -0
- solace_agent_mesh/common/utils/mime_helpers.py +172 -0
- solace_agent_mesh/common/utils/push_notification_auth.py +135 -0
- solace_agent_mesh/common/utils/pydantic_utils.py +159 -0
- 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/common/utils/type_utils.py +28 -0
- solace_agent_mesh/common/utils/utils_llm.txt +335 -0
- solace_agent_mesh/common/utils/utils_llm_detail.txt +572 -0
- solace_agent_mesh/config_portal/__init__.py +0 -0
- solace_agent_mesh/config_portal/backend/__init__.py +0 -0
- solace_agent_mesh/config_portal/backend/common.py +77 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/__init__.py +0 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +24 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/models.py +49 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/registry_manager.py +166 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog/scraper.py +521 -0
- solace_agent_mesh/config_portal/backend/plugin_catalog_server.py +217 -0
- solace_agent_mesh/config_portal/backend/server.py +644 -0
- solace_agent_mesh/config_portal/frontend/static/client/Solace_community_logo.png +0 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-DiOiAjzL.js +103 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/components-Rk0n-9cK.js +140 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/entry.client-mvZjNKiz.js +19 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/index-DzNKzXrc.js +68 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-ba77705e.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-B17tZKK7.css +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-V2BeTIUc.js +10 -0
- solace_agent_mesh/config_portal/frontend/static/client/favicon.ico +0 -0
- solace_agent_mesh/config_portal/frontend/static/client/index.html +7 -0
- solace_agent_mesh/core_a2a/__init__.py +1 -0
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +90 -0
- solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +101 -0
- solace_agent_mesh/core_a2a/service.py +307 -0
- solace_agent_mesh/evaluation/__init__.py +0 -0
- solace_agent_mesh/evaluation/evaluator.py +691 -0
- solace_agent_mesh/evaluation/message_organizer.py +553 -0
- solace_agent_mesh/evaluation/report/benchmark_info.html +35 -0
- solace_agent_mesh/evaluation/report/chart_section.html +141 -0
- solace_agent_mesh/evaluation/report/detailed_breakdown.html +28 -0
- solace_agent_mesh/evaluation/report/modal.html +59 -0
- solace_agent_mesh/evaluation/report/modal_chart_functions.js +411 -0
- solace_agent_mesh/evaluation/report/modal_script.js +296 -0
- solace_agent_mesh/evaluation/report/modal_styles.css +340 -0
- solace_agent_mesh/evaluation/report/performance_metrics_styles.css +93 -0
- solace_agent_mesh/evaluation/report/templates/footer.html +2 -0
- solace_agent_mesh/evaluation/report/templates/header.html +340 -0
- solace_agent_mesh/evaluation/report_data_processor.py +970 -0
- solace_agent_mesh/evaluation/report_generator.py +607 -0
- solace_agent_mesh/evaluation/run.py +954 -0
- solace_agent_mesh/evaluation/shared/__init__.py +92 -0
- solace_agent_mesh/evaluation/shared/constants.py +47 -0
- solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
- solace_agent_mesh/evaluation/shared/helpers.py +35 -0
- solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
- solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
- solace_agent_mesh/evaluation/subscriber.py +776 -0
- solace_agent_mesh/evaluation/summary_builder.py +880 -0
- solace_agent_mesh/gateway/__init__.py +0 -0
- solace_agent_mesh/gateway/adapter/__init__.py +1 -0
- solace_agent_mesh/gateway/adapter/base.py +143 -0
- solace_agent_mesh/gateway/adapter/types.py +221 -0
- solace_agent_mesh/gateway/base/__init__.py +1 -0
- solace_agent_mesh/gateway/base/app.py +345 -0
- solace_agent_mesh/gateway/base/base_llm.txt +226 -0
- solace_agent_mesh/gateway/base/base_llm_detail.txt +235 -0
- solace_agent_mesh/gateway/base/component.py +2030 -0
- solace_agent_mesh/gateway/base/task_context.py +75 -0
- solace_agent_mesh/gateway/gateway_llm.txt +369 -0
- solace_agent_mesh/gateway/gateway_llm_detail.txt +3885 -0
- 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 +727 -0
- solace_agent_mesh/gateway/http_sse/__init__.py +0 -0
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +345 -0
- solace_agent_mesh/gateway/http_sse/alembic/env.py +87 -0
- solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20250910_d5b3f8f2e9a0_create_initial_database.py +58 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20250911_b1c2d3e4f5g6_add_database_indexes.py +83 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20250916_f6e7d8c9b0a1_convert_timestamps_to_epoch_and_align_columns.py +412 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251006_98882922fa59_add_tasks_events_feedback_chat_tasks.py +190 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
- 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/versions/versions_llm.txt +161 -0
- solace_agent_mesh/gateway/http_sse/alembic.ini +109 -0
- solace_agent_mesh/gateway/http_sse/app.py +351 -0
- solace_agent_mesh/gateway/http_sse/component.py +2360 -0
- solace_agent_mesh/gateway/http_sse/components/__init__.py +7 -0
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +105 -0
- solace_agent_mesh/gateway/http_sse/components/task_logger_forwarder.py +109 -0
- solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +110 -0
- solace_agent_mesh/gateway/http_sse/dependencies.py +653 -0
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +299 -0
- solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +3278 -0
- solace_agent_mesh/gateway/http_sse/main.py +789 -0
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +46 -0
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +102 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +11 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/chat_task.py +75 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +221 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/feedback.py +20 -0
- 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 +66 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +0 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/task.py +32 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/task_event.py +21 -0
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +125 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +239 -0
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +34 -0
- solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
- solace_agent_mesh/gateway/http_sse/repository/models/chat_task_model.py +31 -0
- solace_agent_mesh/gateway/http_sse/repository/models/feedback_model.py +21 -0
- solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +257 -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 +53 -0
- solace_agent_mesh/gateway/http_sse/repository/models/task_event_model.py +25 -0
- solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +39 -0
- 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/repository_llm.txt +308 -0
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +268 -0
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +248 -0
- solace_agent_mesh/gateway/http_sse/routers/__init__.py +4 -0
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +74 -0
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +1137 -0
- solace_agent_mesh/gateway/http_sse/routers/auth.py +311 -0
- solace_agent_mesh/gateway/http_sse/routers/config.py +371 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/__init__.py +10 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +450 -0
- 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/__init__.py +15 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +133 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +33 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/task_requests.py +58 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +18 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +42 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +123 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +33 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/task_responses.py +30 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +168 -0
- solace_agent_mesh/gateway/http_sse/routers/people.py +38 -0
- solace_agent_mesh/gateway/http_sse/routers/projects.py +767 -0
- solace_agent_mesh/gateway/http_sse/routers/prompts.py +1415 -0
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +312 -0
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +634 -0
- solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
- solace_agent_mesh/gateway/http_sse/routers/sse.py +230 -0
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +1089 -0
- solace_agent_mesh/gateway/http_sse/routers/users.py +83 -0
- solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +1220 -0
- solace_agent_mesh/gateway/http_sse/services/__init__.py +4 -0
- solace_agent_mesh/gateway/http_sse/services/agent_card_service.py +71 -0
- 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 +273 -0
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +250 -0
- solace_agent_mesh/gateway/http_sse/services/people_service.py +78 -0
- 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/services_llm.txt +303 -0
- solace_agent_mesh/gateway/http_sse/services/session_service.py +702 -0
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +593 -0
- solace_agent_mesh/gateway/http_sse/services/task_service.py +119 -0
- solace_agent_mesh/gateway/http_sse/session_manager.py +219 -0
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +146 -0
- solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
- solace_agent_mesh/gateway/http_sse/shared/base_repository.py +252 -0
- solace_agent_mesh/gateway/http_sse/shared/database_exceptions.py +274 -0
- solace_agent_mesh/gateway/http_sse/shared/database_helpers.py +43 -0
- solace_agent_mesh/gateway/http_sse/shared/enums.py +40 -0
- solace_agent_mesh/gateway/http_sse/shared/error_dto.py +107 -0
- solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +217 -0
- solace_agent_mesh/gateway/http_sse/shared/exceptions.py +192 -0
- solace_agent_mesh/gateway/http_sse/shared/pagination.py +138 -0
- solace_agent_mesh/gateway/http_sse/shared/response_utils.py +134 -0
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +319 -0
- solace_agent_mesh/gateway/http_sse/shared/timestamp_utils.py +97 -0
- solace_agent_mesh/gateway/http_sse/shared/types.py +50 -0
- solace_agent_mesh/gateway/http_sse/shared/utils.py +22 -0
- solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +88 -0
- solace_agent_mesh/gateway/http_sse/sse_manager.py +491 -0
- solace_agent_mesh/gateway/http_sse/utils/__init__.py +1 -0
- solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
- solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +72 -0
- solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +47 -0
- solace_agent_mesh/llm.txt +228 -0
- solace_agent_mesh/llm_detail.txt +2835 -0
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/platform/__init__.py +18 -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 +147 -0
- solace_agent_mesh/services/platform/api/main.py +280 -0
- solace_agent_mesh/services/platform/api/middleware.py +51 -0
- solace_agent_mesh/services/platform/api/routers/__init__.py +24 -0
- solace_agent_mesh/services/platform/app.py +114 -0
- solace_agent_mesh/services/platform/component.py +235 -0
- solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
- solace_agent_mesh/solace_agent_mesh_llm_detail.txt +8599 -0
- solace_agent_mesh/templates/agent_template.yaml +53 -0
- solace_agent_mesh/templates/eval_backend_template.yaml +54 -0
- solace_agent_mesh/templates/gateway_app_template.py +75 -0
- solace_agent_mesh/templates/gateway_component_template.py +484 -0
- solace_agent_mesh/templates/gateway_config_template.yaml +38 -0
- solace_agent_mesh/templates/logging_config_template.yaml +48 -0
- solace_agent_mesh/templates/main_orchestrator.yaml +66 -0
- solace_agent_mesh/templates/plugin_agent_config_template.yaml +122 -0
- solace_agent_mesh/templates/plugin_custom_config_template.yaml +27 -0
- solace_agent_mesh/templates/plugin_custom_template.py +10 -0
- solace_agent_mesh/templates/plugin_gateway_config_template.yaml +60 -0
- solace_agent_mesh/templates/plugin_pyproject_template.toml +32 -0
- solace_agent_mesh/templates/plugin_readme_template.md +12 -0
- solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
- solace_agent_mesh/templates/plugin_tools_template.py +224 -0
- solace_agent_mesh/templates/shared_config.yaml +112 -0
- solace_agent_mesh/templates/templates_llm.txt +147 -0
- solace_agent_mesh/templates/webui.yaml +177 -0
- solace_agent_mesh-1.11.2.dist-info/METADATA +504 -0
- solace_agent_mesh-1.11.2.dist-info/RECORD +624 -0
- solace_agent_mesh-1.11.2.dist-info/WHEEL +4 -0
- solace_agent_mesh-1.11.2.dist-info/entry_points.txt +3 -0
- solace_agent_mesh-1.11.2.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,1353 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Helper functions for artifact management, including metadata handling and schema inference.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import base64
|
|
7
|
+
import binascii
|
|
8
|
+
import json
|
|
9
|
+
import csv
|
|
10
|
+
import io
|
|
11
|
+
import inspect
|
|
12
|
+
import os
|
|
13
|
+
import yaml
|
|
14
|
+
import traceback
|
|
15
|
+
from datetime import datetime, timezone
|
|
16
|
+
from typing import Any, Dict, Optional, Tuple, List, Union, TYPE_CHECKING
|
|
17
|
+
from urllib.parse import urlparse, parse_qs, urlunparse, urlencode
|
|
18
|
+
from google.adk.artifacts import BaseArtifactService
|
|
19
|
+
from google.genai import types as adk_types
|
|
20
|
+
from ...common.a2a.types import ArtifactInfo
|
|
21
|
+
from ...common.utils.mime_helpers import is_text_based_mime_type, is_text_based_file
|
|
22
|
+
from ...common.constants import (
|
|
23
|
+
TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY,
|
|
24
|
+
TEXT_ARTIFACT_CONTEXT_DEFAULT_LENGTH,
|
|
25
|
+
)
|
|
26
|
+
from ...agent.utils.context_helpers import get_original_session_id
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from google.adk.tools import ToolContext
|
|
30
|
+
from ...agent.sac.component import SamAgentComponent
|
|
31
|
+
|
|
32
|
+
log = logging.getLogger(__name__)
|
|
33
|
+
|
|
34
|
+
METADATA_SUFFIX = ".metadata.json"
|
|
35
|
+
DEFAULT_SCHEMA_MAX_KEYS = 20
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def is_filename_safe(filename: str) -> bool:
|
|
39
|
+
"""
|
|
40
|
+
Checks if a filename is safe for artifact creation.
|
|
41
|
+
- Must not be empty or just whitespace.
|
|
42
|
+
- Must not contain path traversal sequences ('..').
|
|
43
|
+
- Must not contain path separators ('/' or '\\').
|
|
44
|
+
- Must not be a reserved name like '.' or '..'.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
filename: The filename to validate.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
True if the filename is safe, False otherwise.
|
|
51
|
+
"""
|
|
52
|
+
if not filename or not filename.strip():
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
# Check for path traversal
|
|
56
|
+
if ".." in filename:
|
|
57
|
+
return False
|
|
58
|
+
|
|
59
|
+
# Check for path separators
|
|
60
|
+
if "/" in filename or "\\" in filename:
|
|
61
|
+
return False
|
|
62
|
+
|
|
63
|
+
# Check for reserved names
|
|
64
|
+
if filename.strip() in [".", ".."]:
|
|
65
|
+
return False
|
|
66
|
+
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def ensure_correct_extension(filename_from_llm: str, desired_extension: str) -> str:
|
|
71
|
+
"""
|
|
72
|
+
Ensures a filename has the correct extension, handling cases where the LLM
|
|
73
|
+
might provide a filename with or without an extension, or with the wrong one.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
filename_from_llm: The filename string provided by the LLM.
|
|
77
|
+
desired_extension: The correct extension for the file (e.g., 'png', 'md').
|
|
78
|
+
Should be provided without a leading dot.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
A string with the correctly formatted filename.
|
|
82
|
+
"""
|
|
83
|
+
if not filename_from_llm:
|
|
84
|
+
return f"unnamed.{desired_extension.lower()}"
|
|
85
|
+
filename_stripped = filename_from_llm.strip()
|
|
86
|
+
desired_ext_clean = desired_extension.lower().strip().lstrip(".")
|
|
87
|
+
base_name, current_ext = os.path.splitext(filename_stripped)
|
|
88
|
+
current_ext_clean = current_ext.lower().lstrip(".")
|
|
89
|
+
if current_ext_clean == desired_ext_clean:
|
|
90
|
+
return filename_stripped
|
|
91
|
+
else:
|
|
92
|
+
return f"{base_name}.{desired_ext_clean}"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def format_artifact_uri(
|
|
96
|
+
app_name: str,
|
|
97
|
+
user_id: str,
|
|
98
|
+
session_id: str,
|
|
99
|
+
filename: str,
|
|
100
|
+
version: Union[int, str],
|
|
101
|
+
) -> str:
|
|
102
|
+
"""Formats the components into a standard artifact:// URI."""
|
|
103
|
+
path = f"/{user_id}/{session_id}/{filename}"
|
|
104
|
+
query = urlencode({"version": str(version)})
|
|
105
|
+
return urlunparse(("artifact", app_name, path, "", query, ""))
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def parse_artifact_uri(uri: str) -> Dict[str, Any]:
|
|
109
|
+
"""Parses an artifact:// URI into its constituent parts."""
|
|
110
|
+
parsed = urlparse(uri)
|
|
111
|
+
if parsed.scheme != "artifact":
|
|
112
|
+
raise ValueError("Invalid URI scheme, must be 'artifact'.")
|
|
113
|
+
|
|
114
|
+
path_parts = parsed.path.strip("/").split("/")
|
|
115
|
+
if len(path_parts) != 3:
|
|
116
|
+
raise ValueError("Invalid URI path. Expected /user_id/session_id/filename")
|
|
117
|
+
|
|
118
|
+
query_params = parse_qs(parsed.query)
|
|
119
|
+
version = query_params.get("version", [None])[0]
|
|
120
|
+
if not version:
|
|
121
|
+
raise ValueError("Version is missing from URI query parameters.")
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
"app_name": parsed.netloc,
|
|
125
|
+
"user_id": path_parts[0],
|
|
126
|
+
"session_id": path_parts[1],
|
|
127
|
+
"filename": path_parts[2],
|
|
128
|
+
"version": int(version) if version.isdigit() else version,
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _clean_metadata_for_output(metadata: Dict[str, Any]) -> Dict[str, Any]:
|
|
133
|
+
"""
|
|
134
|
+
Remove null, False, and empty values from metadata to reduce token usage.
|
|
135
|
+
Recursively cleans nested dictionaries.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
metadata: The metadata dictionary to clean
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
A cleaned dictionary with unnecessary fields removed
|
|
142
|
+
"""
|
|
143
|
+
cleaned = {}
|
|
144
|
+
for key, value in metadata.items():
|
|
145
|
+
# Skip None, False, and empty collections
|
|
146
|
+
if value is None or value is False or value == {} or value == []:
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
# Recursively clean nested dictionaries
|
|
150
|
+
if isinstance(value, dict):
|
|
151
|
+
cleaned_nested = _clean_metadata_for_output(value)
|
|
152
|
+
if cleaned_nested: # Only include if not empty after cleaning
|
|
153
|
+
cleaned[key] = cleaned_nested
|
|
154
|
+
else:
|
|
155
|
+
cleaned[key] = value
|
|
156
|
+
|
|
157
|
+
return cleaned
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _inspect_structure(
|
|
161
|
+
data: Any, max_depth: int, max_keys: int, current_depth: int = 0
|
|
162
|
+
) -> Any:
|
|
163
|
+
"""
|
|
164
|
+
Recursively inspects data structure up to max_depth and max_keys for dictionaries.
|
|
165
|
+
"""
|
|
166
|
+
if current_depth >= max_depth:
|
|
167
|
+
return type(data).__name__
|
|
168
|
+
if isinstance(data, dict):
|
|
169
|
+
if not data:
|
|
170
|
+
return {}
|
|
171
|
+
inspected_dict = {}
|
|
172
|
+
keys = list(data.keys())
|
|
173
|
+
keys_to_inspect = keys[:max_keys]
|
|
174
|
+
for key in keys_to_inspect:
|
|
175
|
+
inspected_dict[key] = _inspect_structure(
|
|
176
|
+
data[key], max_depth, max_keys, current_depth + 1
|
|
177
|
+
)
|
|
178
|
+
if len(keys) > max_keys:
|
|
179
|
+
inspected_dict["..."] = f"{len(keys) - max_keys} more keys"
|
|
180
|
+
return inspected_dict
|
|
181
|
+
elif isinstance(data, list):
|
|
182
|
+
if not data:
|
|
183
|
+
return []
|
|
184
|
+
return [_inspect_structure(data[0], max_depth, max_keys, current_depth + 1)]
|
|
185
|
+
else:
|
|
186
|
+
return type(data).__name__
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _infer_schema(
|
|
190
|
+
content_bytes: bytes,
|
|
191
|
+
mime_type: str,
|
|
192
|
+
depth: int = 3,
|
|
193
|
+
max_keys: int = DEFAULT_SCHEMA_MAX_KEYS,
|
|
194
|
+
) -> Dict[str, Any]:
|
|
195
|
+
"""
|
|
196
|
+
Infers basic schema information for common text-based types.
|
|
197
|
+
Args:
|
|
198
|
+
content_bytes: The raw byte content.
|
|
199
|
+
mime_type: The MIME type.
|
|
200
|
+
depth: Maximum recursion depth for nested structures.
|
|
201
|
+
max_keys: Maximum number of dictionary keys to inspect at each level.
|
|
202
|
+
Returns:
|
|
203
|
+
A dictionary representing the inferred schema, including an 'inferred' flag
|
|
204
|
+
and potential 'error' field.
|
|
205
|
+
"""
|
|
206
|
+
schema_info = {"type": mime_type, "inferred": False, "error": None}
|
|
207
|
+
normalized_mime_type = mime_type.lower() if mime_type else ""
|
|
208
|
+
try:
|
|
209
|
+
if normalized_mime_type == "text/csv":
|
|
210
|
+
try:
|
|
211
|
+
text_content = io.TextIOWrapper(
|
|
212
|
+
io.BytesIO(content_bytes), encoding="utf-8"
|
|
213
|
+
)
|
|
214
|
+
reader = csv.reader(text_content)
|
|
215
|
+
header = next(reader)
|
|
216
|
+
schema_info["columns"] = header
|
|
217
|
+
schema_info["inferred"] = True
|
|
218
|
+
except (StopIteration, csv.Error, UnicodeDecodeError) as e:
|
|
219
|
+
schema_info["error"] = f"CSV header inference failed: {e}"
|
|
220
|
+
elif normalized_mime_type in ["application/json", "text/json"]:
|
|
221
|
+
try:
|
|
222
|
+
data = json.loads(content_bytes.decode("utf-8"))
|
|
223
|
+
schema_info["structure"] = _inspect_structure(data, depth, max_keys)
|
|
224
|
+
schema_info["inferred"] = True
|
|
225
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as e:
|
|
226
|
+
schema_info["error"] = f"JSON structure inference failed: {e}"
|
|
227
|
+
elif normalized_mime_type in [
|
|
228
|
+
"application/yaml",
|
|
229
|
+
"text/yaml",
|
|
230
|
+
"application/x-yaml",
|
|
231
|
+
"text/x-yaml",
|
|
232
|
+
]:
|
|
233
|
+
try:
|
|
234
|
+
data = yaml.safe_load(content_bytes)
|
|
235
|
+
schema_info["structure"] = _inspect_structure(data, depth, max_keys)
|
|
236
|
+
schema_info["inferred"] = True
|
|
237
|
+
except (yaml.YAMLError, UnicodeDecodeError) as e:
|
|
238
|
+
schema_info["error"] = f"YAML structure inference failed: {e}"
|
|
239
|
+
except Exception as e:
|
|
240
|
+
schema_info["error"] = f"Unexpected error during schema inference: {e}"
|
|
241
|
+
if schema_info["error"]:
|
|
242
|
+
log.warning(
|
|
243
|
+
"Schema inference for mime_type '%s' encountered error: %s",
|
|
244
|
+
mime_type,
|
|
245
|
+
schema_info["error"],
|
|
246
|
+
)
|
|
247
|
+
elif not schema_info["inferred"]:
|
|
248
|
+
log.debug(
|
|
249
|
+
"No specific schema inference logic applied for mime_type '%s'.", mime_type
|
|
250
|
+
)
|
|
251
|
+
return schema_info
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
async def save_artifact_with_metadata(
|
|
255
|
+
artifact_service: BaseArtifactService,
|
|
256
|
+
app_name: str,
|
|
257
|
+
user_id: str,
|
|
258
|
+
session_id: str,
|
|
259
|
+
filename: str,
|
|
260
|
+
content_bytes: bytes,
|
|
261
|
+
mime_type: str,
|
|
262
|
+
metadata_dict: Dict[str, Any],
|
|
263
|
+
timestamp: datetime,
|
|
264
|
+
explicit_schema: Optional[Dict] = None,
|
|
265
|
+
schema_inference_depth: int = 2,
|
|
266
|
+
schema_max_keys: int = DEFAULT_SCHEMA_MAX_KEYS,
|
|
267
|
+
tool_context: Optional["ToolContext"] = None,
|
|
268
|
+
) -> Dict[str, Any]:
|
|
269
|
+
"""
|
|
270
|
+
Saves a data artifact and its corresponding metadata artifact using BaseArtifactService.
|
|
271
|
+
"""
|
|
272
|
+
log_identifier = f"[ArtifactHelper:save:{filename}]"
|
|
273
|
+
log.debug("%s Saving artifact and metadata (async)...", log_identifier)
|
|
274
|
+
data_version = None
|
|
275
|
+
metadata_version = None
|
|
276
|
+
metadata_filename = f"{filename}{METADATA_SUFFIX}"
|
|
277
|
+
status = "error"
|
|
278
|
+
status_message = "Initialization error"
|
|
279
|
+
try:
|
|
280
|
+
data_artifact_part = adk_types.Part.from_bytes(
|
|
281
|
+
data=content_bytes, mime_type=mime_type
|
|
282
|
+
)
|
|
283
|
+
log.debug(
|
|
284
|
+
f"{log_identifier} artifact_service object type: {type(artifact_service)}"
|
|
285
|
+
)
|
|
286
|
+
log.debug(
|
|
287
|
+
f"{log_identifier} artifact_service object dir: {dir(artifact_service)}"
|
|
288
|
+
)
|
|
289
|
+
if hasattr(artifact_service, "save_artifact"):
|
|
290
|
+
save_artifact_method = getattr(artifact_service, "save_artifact")
|
|
291
|
+
log.debug(
|
|
292
|
+
f"{log_identifier} type of artifact_service.save_artifact: {type(save_artifact_method)}"
|
|
293
|
+
)
|
|
294
|
+
log.debug(
|
|
295
|
+
f"{log_identifier} Is save_artifact a coroutine function? {inspect.iscoroutinefunction(save_artifact_method)}"
|
|
296
|
+
)
|
|
297
|
+
log.debug(
|
|
298
|
+
f"{log_identifier} Is save_artifact an async generator function? {inspect.isasyncgenfunction(save_artifact_method)}"
|
|
299
|
+
)
|
|
300
|
+
if callable(save_artifact_method):
|
|
301
|
+
try:
|
|
302
|
+
sig = inspect.signature(save_artifact_method)
|
|
303
|
+
log.debug(f"{log_identifier} Signature of save_artifact: {sig}")
|
|
304
|
+
except Exception as e_inspect:
|
|
305
|
+
log.debug(
|
|
306
|
+
f"{log_identifier} Could not get signature of save_artifact: {e_inspect}"
|
|
307
|
+
)
|
|
308
|
+
save_data_method = getattr(artifact_service, "save_artifact")
|
|
309
|
+
data_version = await save_data_method(
|
|
310
|
+
app_name=app_name,
|
|
311
|
+
user_id=user_id,
|
|
312
|
+
session_id=session_id,
|
|
313
|
+
filename=filename,
|
|
314
|
+
artifact=data_artifact_part,
|
|
315
|
+
)
|
|
316
|
+
log.info(
|
|
317
|
+
"%s Saved data artifact '%s' as version %s.",
|
|
318
|
+
log_identifier,
|
|
319
|
+
filename,
|
|
320
|
+
data_version,
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Populate artifact_delta for ADK callbacks if tool_context is provided
|
|
324
|
+
if (
|
|
325
|
+
tool_context
|
|
326
|
+
and hasattr(tool_context, "actions")
|
|
327
|
+
and hasattr(tool_context.actions, "artifact_delta")
|
|
328
|
+
):
|
|
329
|
+
tool_context.actions.artifact_delta[filename] = data_version
|
|
330
|
+
log.debug(
|
|
331
|
+
"%s Populated artifact_delta for ADK callbacks: %s -> %s",
|
|
332
|
+
log_identifier,
|
|
333
|
+
filename,
|
|
334
|
+
data_version,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
final_metadata = {
|
|
338
|
+
"filename": filename,
|
|
339
|
+
"mime_type": mime_type,
|
|
340
|
+
"size_bytes": len(content_bytes),
|
|
341
|
+
"timestamp_utc": (
|
|
342
|
+
timestamp
|
|
343
|
+
if isinstance(timestamp, (int, float))
|
|
344
|
+
else timestamp.timestamp()
|
|
345
|
+
),
|
|
346
|
+
**(metadata_dict or {}),
|
|
347
|
+
}
|
|
348
|
+
if explicit_schema:
|
|
349
|
+
final_metadata["schema"] = {
|
|
350
|
+
"type": mime_type,
|
|
351
|
+
"inferred": False,
|
|
352
|
+
**explicit_schema,
|
|
353
|
+
}
|
|
354
|
+
log.debug("%s Using explicit schema provided by caller.", log_identifier)
|
|
355
|
+
else:
|
|
356
|
+
inferred_schema = _infer_schema(
|
|
357
|
+
content_bytes, mime_type, schema_inference_depth, schema_max_keys
|
|
358
|
+
)
|
|
359
|
+
final_metadata["schema"] = inferred_schema
|
|
360
|
+
if inferred_schema.get("inferred"):
|
|
361
|
+
log.debug(
|
|
362
|
+
"%s Added inferred schema (max_keys=%d).",
|
|
363
|
+
log_identifier,
|
|
364
|
+
schema_max_keys,
|
|
365
|
+
)
|
|
366
|
+
elif inferred_schema.get("error"):
|
|
367
|
+
log.warning(
|
|
368
|
+
"%s Schema inference failed: %s",
|
|
369
|
+
log_identifier,
|
|
370
|
+
inferred_schema["error"],
|
|
371
|
+
)
|
|
372
|
+
try:
|
|
373
|
+
metadata_bytes = json.dumps(final_metadata, indent=2).encode("utf-8")
|
|
374
|
+
metadata_artifact_part = adk_types.Part.from_bytes(
|
|
375
|
+
data=metadata_bytes, mime_type="application/json"
|
|
376
|
+
)
|
|
377
|
+
save_metadata_method = getattr(artifact_service, "save_artifact")
|
|
378
|
+
metadata_version = await save_metadata_method(
|
|
379
|
+
app_name=app_name,
|
|
380
|
+
user_id=user_id,
|
|
381
|
+
session_id=session_id,
|
|
382
|
+
filename=metadata_filename,
|
|
383
|
+
artifact=metadata_artifact_part,
|
|
384
|
+
)
|
|
385
|
+
log.info(
|
|
386
|
+
"%s Saved metadata artifact '%s' as version %s.",
|
|
387
|
+
log_identifier,
|
|
388
|
+
metadata_filename,
|
|
389
|
+
metadata_version,
|
|
390
|
+
)
|
|
391
|
+
status = "success"
|
|
392
|
+
status_message = "Artifact and metadata saved successfully."
|
|
393
|
+
except Exception as meta_save_err:
|
|
394
|
+
log.exception(
|
|
395
|
+
"%s Failed to save metadata artifact '%s': %s",
|
|
396
|
+
log_identifier,
|
|
397
|
+
metadata_filename,
|
|
398
|
+
meta_save_err,
|
|
399
|
+
)
|
|
400
|
+
status = "partial_success"
|
|
401
|
+
status_message = f"Data artifact saved (v{data_version}), but failed to save metadata: {meta_save_err}"
|
|
402
|
+
except Exception as data_save_err:
|
|
403
|
+
log.exception(
|
|
404
|
+
"%s Failed to save data artifact '%s': %s",
|
|
405
|
+
log_identifier,
|
|
406
|
+
filename,
|
|
407
|
+
data_save_err,
|
|
408
|
+
)
|
|
409
|
+
status = "error"
|
|
410
|
+
status_message = f"Failed to save data artifact: {data_save_err}"
|
|
411
|
+
return {
|
|
412
|
+
"status": status,
|
|
413
|
+
"data_filename": filename,
|
|
414
|
+
"data_version": data_version,
|
|
415
|
+
"metadata_filename": metadata_filename,
|
|
416
|
+
"metadata_version": metadata_version,
|
|
417
|
+
"message": status_message,
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
async def process_artifact_upload(
|
|
422
|
+
artifact_service: BaseArtifactService,
|
|
423
|
+
component: Any,
|
|
424
|
+
user_id: str,
|
|
425
|
+
session_id: str,
|
|
426
|
+
filename: str,
|
|
427
|
+
content_bytes: bytes,
|
|
428
|
+
mime_type: str,
|
|
429
|
+
metadata_json: Optional[str] = None,
|
|
430
|
+
log_prefix: str = "[ArtifactUpload]",
|
|
431
|
+
) -> Dict[str, Any]:
|
|
432
|
+
"""
|
|
433
|
+
Common logic for processing artifact uploads.
|
|
434
|
+
|
|
435
|
+
Handles filename validation, metadata parsing, artifact storage, and URI generation.
|
|
436
|
+
|
|
437
|
+
Args:
|
|
438
|
+
artifact_service: The artifact service instance to use for storage.
|
|
439
|
+
component: The component instance (agent or gateway) for configuration.
|
|
440
|
+
user_id: The user ID associated with the artifact.
|
|
441
|
+
session_id: The session ID associated with the artifact.
|
|
442
|
+
filename: The name of the artifact file.
|
|
443
|
+
content_bytes: The raw bytes of the artifact content.
|
|
444
|
+
mime_type: The MIME type of the artifact.
|
|
445
|
+
metadata_json: Optional JSON string containing artifact metadata.
|
|
446
|
+
log_prefix: Prefix for log messages.
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
Dict with keys:
|
|
450
|
+
- status: "success" or "error"
|
|
451
|
+
- artifact_uri: The URI of the stored artifact (on success)
|
|
452
|
+
- version: The version number of the stored artifact (on success)
|
|
453
|
+
- message: Status message
|
|
454
|
+
- error: Error details (on error)
|
|
455
|
+
"""
|
|
456
|
+
log.debug("%s Processing artifact upload for '%s'", log_prefix, filename)
|
|
457
|
+
|
|
458
|
+
# Validate filename
|
|
459
|
+
if not is_filename_safe(filename):
|
|
460
|
+
error_msg = f"Invalid filename: '{filename}'. Filename must not contain path separators or traversal sequences."
|
|
461
|
+
log.warning("%s %s", log_prefix, error_msg)
|
|
462
|
+
return {"status": "error", "message": error_msg, "error": "invalid_filename"}
|
|
463
|
+
|
|
464
|
+
# Validate content
|
|
465
|
+
if not content_bytes:
|
|
466
|
+
error_msg = "Uploaded file cannot be empty."
|
|
467
|
+
log.warning("%s %s", log_prefix, error_msg)
|
|
468
|
+
return {"status": "error", "message": error_msg, "error": "empty_file"}
|
|
469
|
+
|
|
470
|
+
# Parse metadata JSON if provided
|
|
471
|
+
metadata_dict = {}
|
|
472
|
+
if metadata_json and metadata_json.strip():
|
|
473
|
+
try:
|
|
474
|
+
metadata_dict = json.loads(metadata_json.strip())
|
|
475
|
+
if not isinstance(metadata_dict, dict):
|
|
476
|
+
log.warning(
|
|
477
|
+
"%s Metadata JSON did not parse to a dictionary. Ignoring.",
|
|
478
|
+
log_prefix,
|
|
479
|
+
)
|
|
480
|
+
metadata_dict = {}
|
|
481
|
+
except json.JSONDecodeError as json_err:
|
|
482
|
+
log.warning(
|
|
483
|
+
"%s Failed to parse metadata_json: %s. Proceeding without it.",
|
|
484
|
+
log_prefix,
|
|
485
|
+
json_err,
|
|
486
|
+
)
|
|
487
|
+
metadata_dict = {}
|
|
488
|
+
|
|
489
|
+
# Get app_name from component configuration
|
|
490
|
+
app_name = component.get_config("name", "A2A_WebUI_App")
|
|
491
|
+
current_timestamp = datetime.now(timezone.utc)
|
|
492
|
+
|
|
493
|
+
# Save artifact with metadata
|
|
494
|
+
try:
|
|
495
|
+
save_result = await save_artifact_with_metadata(
|
|
496
|
+
artifact_service=artifact_service,
|
|
497
|
+
app_name=app_name,
|
|
498
|
+
user_id=user_id,
|
|
499
|
+
session_id=session_id,
|
|
500
|
+
filename=filename,
|
|
501
|
+
content_bytes=content_bytes,
|
|
502
|
+
mime_type=mime_type,
|
|
503
|
+
metadata_dict=metadata_dict,
|
|
504
|
+
timestamp=current_timestamp,
|
|
505
|
+
schema_max_keys=component.get_config(
|
|
506
|
+
"schema_max_keys", DEFAULT_SCHEMA_MAX_KEYS
|
|
507
|
+
),
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
if save_result["status"] == "success":
|
|
511
|
+
saved_version = save_result.get("data_version")
|
|
512
|
+
artifact_uri = format_artifact_uri(
|
|
513
|
+
app_name=app_name,
|
|
514
|
+
user_id=user_id,
|
|
515
|
+
session_id=session_id,
|
|
516
|
+
filename=filename,
|
|
517
|
+
version=saved_version,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
log.info(
|
|
521
|
+
"%s Artifact '%s' uploaded successfully. URI: %s, Version: %s",
|
|
522
|
+
log_prefix,
|
|
523
|
+
filename,
|
|
524
|
+
artifact_uri,
|
|
525
|
+
saved_version,
|
|
526
|
+
)
|
|
527
|
+
|
|
528
|
+
return {
|
|
529
|
+
"status": "success",
|
|
530
|
+
"artifact_uri": artifact_uri,
|
|
531
|
+
"version": saved_version,
|
|
532
|
+
"message": save_result.get(
|
|
533
|
+
"message", "Artifact uploaded successfully."
|
|
534
|
+
),
|
|
535
|
+
"data_version": saved_version,
|
|
536
|
+
"metadata_version": save_result.get("metadata_version"),
|
|
537
|
+
}
|
|
538
|
+
else:
|
|
539
|
+
error_msg = save_result.get("message", "Failed to save artifact.")
|
|
540
|
+
log.error("%s %s", log_prefix, error_msg)
|
|
541
|
+
return {"status": "error", "message": error_msg, "error": "save_failed"}
|
|
542
|
+
|
|
543
|
+
except Exception as e:
|
|
544
|
+
error_msg = f"Unexpected error storing artifact: {str(e)}"
|
|
545
|
+
log.exception("%s %s", log_prefix, error_msg)
|
|
546
|
+
return {"status": "error", "message": error_msg, "error": "unexpected_error"}
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def format_metadata_for_llm(metadata: Dict[str, Any]) -> str:
|
|
550
|
+
"""Formats loaded metadata into an LLM-friendly text block."""
|
|
551
|
+
lines = []
|
|
552
|
+
filename = metadata.get("filename", "Unknown Filename")
|
|
553
|
+
version = metadata.get("version", "N/A")
|
|
554
|
+
lines.append(f"--- Metadata for artifact '{filename}' (v{version}) ---")
|
|
555
|
+
lines.append("**Artifact Metadata:**")
|
|
556
|
+
if "description" in metadata:
|
|
557
|
+
lines.append(f"* **Description:** {metadata['description']}")
|
|
558
|
+
if "source" in metadata:
|
|
559
|
+
lines.append(f"* **Source:** {metadata['source']}")
|
|
560
|
+
if "mime_type" in metadata:
|
|
561
|
+
lines.append(f"* **Type:** {metadata['mime_type']}")
|
|
562
|
+
if "size_bytes" in metadata:
|
|
563
|
+
lines.append(f"* **Size:** {metadata['size_bytes']} bytes")
|
|
564
|
+
schema = metadata.get("schema", {})
|
|
565
|
+
schema_type = schema.get("type", metadata.get("mime_type", "unknown"))
|
|
566
|
+
schema_details = []
|
|
567
|
+
if schema.get("inferred"):
|
|
568
|
+
schema_details.append("(Inferred)")
|
|
569
|
+
if "columns" in schema:
|
|
570
|
+
schema_details.append(f"Columns: {','.join(schema['columns'])}")
|
|
571
|
+
if "structure" in schema:
|
|
572
|
+
schema_details.append(f"Structure: {json.dumps(schema['structure'])}")
|
|
573
|
+
if schema.get("error"):
|
|
574
|
+
schema_details.append(f"Schema Error: {schema['error']}")
|
|
575
|
+
if schema_details:
|
|
576
|
+
lines.append(f"* **Schema:** {schema_type} {' '.join(schema_details)}")
|
|
577
|
+
elif schema_type != "unknown":
|
|
578
|
+
lines.append(f"* **Schema Type:** {schema_type}")
|
|
579
|
+
custom_fields = {
|
|
580
|
+
k: v
|
|
581
|
+
for k, v in metadata.items()
|
|
582
|
+
if k
|
|
583
|
+
not in [
|
|
584
|
+
"filename",
|
|
585
|
+
"mime_type",
|
|
586
|
+
"size_bytes",
|
|
587
|
+
"timestamp_utc",
|
|
588
|
+
"schema",
|
|
589
|
+
"version",
|
|
590
|
+
"description",
|
|
591
|
+
"source",
|
|
592
|
+
]
|
|
593
|
+
}
|
|
594
|
+
if custom_fields:
|
|
595
|
+
lines.append("* **Other:**")
|
|
596
|
+
for k, v in custom_fields.items():
|
|
597
|
+
lines.append(f" * {k}: {v}")
|
|
598
|
+
lines.append("--- End Metadata ---")
|
|
599
|
+
return "\n".join(lines)
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
async def generate_artifact_metadata_summary(
|
|
603
|
+
component: "SamAgentComponent",
|
|
604
|
+
artifact_identifiers: List[Dict[str, Any]],
|
|
605
|
+
user_id: str,
|
|
606
|
+
session_id: str,
|
|
607
|
+
app_name: str,
|
|
608
|
+
header_text: Optional[str] = None,
|
|
609
|
+
) -> str:
|
|
610
|
+
"""
|
|
611
|
+
Loads metadata for a list of artifacts and formats it into a human-readable
|
|
612
|
+
YAML summary string, suitable for LLM context.
|
|
613
|
+
"""
|
|
614
|
+
if not artifact_identifiers:
|
|
615
|
+
return ""
|
|
616
|
+
|
|
617
|
+
log_identifier = f"{component.log_identifier}[ArtifactSummary]"
|
|
618
|
+
summary_parts = []
|
|
619
|
+
if header_text:
|
|
620
|
+
summary_parts.append(header_text)
|
|
621
|
+
|
|
622
|
+
if not (component.artifact_service and user_id and session_id):
|
|
623
|
+
log.warning(
|
|
624
|
+
"%s Cannot load artifact metadata: missing artifact_service or context.",
|
|
625
|
+
log_identifier,
|
|
626
|
+
)
|
|
627
|
+
for artifact_ref in artifact_identifiers:
|
|
628
|
+
filename = artifact_ref.get("filename", "unknown")
|
|
629
|
+
version = artifact_ref.get("version", "latest")
|
|
630
|
+
summary_parts.append(
|
|
631
|
+
f"---\nArtifact: '{filename}' (version: {version})\nError: Could not load metadata. Host component context missing."
|
|
632
|
+
)
|
|
633
|
+
return "\n\n".join(summary_parts)
|
|
634
|
+
|
|
635
|
+
for artifact_ref in artifact_identifiers:
|
|
636
|
+
filename = artifact_ref.get("filename")
|
|
637
|
+
version = artifact_ref.get("version", "latest")
|
|
638
|
+
if not filename:
|
|
639
|
+
log.warning(
|
|
640
|
+
"%s Skipping artifact with no filename in identifier: %s",
|
|
641
|
+
log_identifier,
|
|
642
|
+
artifact_ref,
|
|
643
|
+
)
|
|
644
|
+
continue
|
|
645
|
+
|
|
646
|
+
try:
|
|
647
|
+
metadata_result = await load_artifact_content_or_metadata(
|
|
648
|
+
artifact_service=component.artifact_service,
|
|
649
|
+
app_name=app_name,
|
|
650
|
+
user_id=user_id,
|
|
651
|
+
session_id=get_original_session_id(session_id),
|
|
652
|
+
filename=filename,
|
|
653
|
+
version=version,
|
|
654
|
+
load_metadata_only=True,
|
|
655
|
+
)
|
|
656
|
+
if metadata_result.get("status") == "success":
|
|
657
|
+
metadata = metadata_result.get("metadata", {})
|
|
658
|
+
resolved_version = metadata_result.get("version", version)
|
|
659
|
+
artifact_header = (
|
|
660
|
+
f"Artifact: '{filename}' (version: {resolved_version})"
|
|
661
|
+
)
|
|
662
|
+
|
|
663
|
+
# Remove redundant fields before dumping to YAML
|
|
664
|
+
metadata.pop("filename", None)
|
|
665
|
+
metadata.pop("version", None)
|
|
666
|
+
|
|
667
|
+
# Clean metadata to remove null/false/empty values for token efficiency
|
|
668
|
+
cleaned_metadata = _clean_metadata_for_output(metadata)
|
|
669
|
+
|
|
670
|
+
TRUNCATION_LIMIT_BYTES = 1024
|
|
671
|
+
TRUNCATION_MESSAGE = "\n... [truncated] ..."
|
|
672
|
+
|
|
673
|
+
try:
|
|
674
|
+
formatted_metadata_str = yaml.safe_dump(
|
|
675
|
+
cleaned_metadata,
|
|
676
|
+
default_flow_style=False,
|
|
677
|
+
sort_keys=False,
|
|
678
|
+
allow_unicode=True,
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
if (
|
|
682
|
+
len(formatted_metadata_str.encode("utf-8"))
|
|
683
|
+
> TRUNCATION_LIMIT_BYTES
|
|
684
|
+
):
|
|
685
|
+
cutoff = TRUNCATION_LIMIT_BYTES - len(
|
|
686
|
+
TRUNCATION_MESSAGE.encode("utf-8")
|
|
687
|
+
)
|
|
688
|
+
# Ensure we don't cut in the middle of a multi-byte character
|
|
689
|
+
encoded_str = formatted_metadata_str.encode("utf-8")
|
|
690
|
+
if cutoff > 0:
|
|
691
|
+
truncated_encoded = encoded_str[:cutoff]
|
|
692
|
+
formatted_metadata_str = (
|
|
693
|
+
truncated_encoded.decode("utf-8", "ignore")
|
|
694
|
+
+ TRUNCATION_MESSAGE
|
|
695
|
+
)
|
|
696
|
+
else:
|
|
697
|
+
formatted_metadata_str = TRUNCATION_MESSAGE
|
|
698
|
+
|
|
699
|
+
summary_parts.append(
|
|
700
|
+
f"---\n{artifact_header}\n{formatted_metadata_str}"
|
|
701
|
+
)
|
|
702
|
+
except Exception as e_format:
|
|
703
|
+
log.error(
|
|
704
|
+
"%s Error formatting metadata for %s v%s: %s",
|
|
705
|
+
log_identifier,
|
|
706
|
+
filename,
|
|
707
|
+
version,
|
|
708
|
+
e_format,
|
|
709
|
+
)
|
|
710
|
+
summary_parts.append(
|
|
711
|
+
f"---\n{artifact_header}\nError: Could not format metadata."
|
|
712
|
+
)
|
|
713
|
+
else:
|
|
714
|
+
error_message = metadata_result.get(
|
|
715
|
+
"message", "Could not load metadata."
|
|
716
|
+
)
|
|
717
|
+
log.warning(
|
|
718
|
+
"%s Failed to load metadata for %s v%s: %s",
|
|
719
|
+
log_identifier,
|
|
720
|
+
filename,
|
|
721
|
+
version,
|
|
722
|
+
error_message,
|
|
723
|
+
)
|
|
724
|
+
artifact_header = f"Artifact: '{filename}' (version: {version})"
|
|
725
|
+
summary_parts.append(f"---\n{artifact_header}\nError: {error_message}")
|
|
726
|
+
except Exception as e_meta:
|
|
727
|
+
log.error(
|
|
728
|
+
"%s Unexpected error loading metadata for %s v%s: %s",
|
|
729
|
+
log_identifier,
|
|
730
|
+
filename,
|
|
731
|
+
version,
|
|
732
|
+
e_meta,
|
|
733
|
+
)
|
|
734
|
+
artifact_header = f"Artifact: '{filename}' (version: {version})"
|
|
735
|
+
summary_parts.append(
|
|
736
|
+
f"---\n{artifact_header}\nError: An unexpected error occurred while loading metadata."
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
return "\n\n".join(summary_parts)
|
|
740
|
+
|
|
741
|
+
|
|
742
|
+
def decode_and_get_bytes(
|
|
743
|
+
content_str: str, mime_type: str, log_identifier: str
|
|
744
|
+
) -> Tuple[bytes, str]:
|
|
745
|
+
"""
|
|
746
|
+
Decodes content if necessary (based on mime_type) and returns bytes and final mime_type.
|
|
747
|
+
Args:
|
|
748
|
+
content_str: The input content string (potentially base64).
|
|
749
|
+
mime_type: The provided MIME type.
|
|
750
|
+
log_identifier: Identifier for logging.
|
|
751
|
+
Returns:
|
|
752
|
+
A tuple containing (content_bytes, final_mime_type).
|
|
753
|
+
"""
|
|
754
|
+
file_bytes: bytes
|
|
755
|
+
final_mime_type = mime_type
|
|
756
|
+
normalized_mime_type = mime_type.lower() if mime_type else ""
|
|
757
|
+
if is_text_based_mime_type(normalized_mime_type):
|
|
758
|
+
file_bytes = content_str.encode("utf-8")
|
|
759
|
+
log.debug(
|
|
760
|
+
"%s Encoded text content for text mimeType '%s'.",
|
|
761
|
+
log_identifier,
|
|
762
|
+
mime_type,
|
|
763
|
+
)
|
|
764
|
+
else:
|
|
765
|
+
try:
|
|
766
|
+
file_bytes = base64.b64decode(content_str, validate=True)
|
|
767
|
+
log.debug(
|
|
768
|
+
"%s Decoded base64 content for non-text mimeType '%s'.",
|
|
769
|
+
log_identifier,
|
|
770
|
+
mime_type,
|
|
771
|
+
)
|
|
772
|
+
except (binascii.Error, ValueError) as decode_error:
|
|
773
|
+
log.warning(
|
|
774
|
+
"%s Failed to base64 decode content for mimeType '%s'. Treating as text/plain. Error: %s",
|
|
775
|
+
log_identifier,
|
|
776
|
+
mime_type,
|
|
777
|
+
decode_error,
|
|
778
|
+
)
|
|
779
|
+
file_bytes = content_str.encode("utf-8")
|
|
780
|
+
final_mime_type = "text/plain"
|
|
781
|
+
return file_bytes, final_mime_type
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
async def get_latest_artifact_version(
|
|
785
|
+
artifact_service: BaseArtifactService,
|
|
786
|
+
app_name: str,
|
|
787
|
+
user_id: str,
|
|
788
|
+
session_id: str,
|
|
789
|
+
filename: str,
|
|
790
|
+
) -> Optional[int]:
|
|
791
|
+
"""
|
|
792
|
+
Retrieves the latest version number for a given artifact.
|
|
793
|
+
|
|
794
|
+
Args:
|
|
795
|
+
artifact_service: The artifact service instance.
|
|
796
|
+
app_name: The application name.
|
|
797
|
+
user_id: The user ID.
|
|
798
|
+
session_id: The session ID.
|
|
799
|
+
filename: The name of the artifact.
|
|
800
|
+
|
|
801
|
+
Returns:
|
|
802
|
+
The latest version number as an integer, or None if no versions exist.
|
|
803
|
+
"""
|
|
804
|
+
log_identifier = f"[ArtifactHelper:get_latest_version:{filename}]"
|
|
805
|
+
try:
|
|
806
|
+
if not hasattr(artifact_service, "list_versions"):
|
|
807
|
+
log.warning(
|
|
808
|
+
"%s Artifact service does not support 'list_versions'.", log_identifier
|
|
809
|
+
)
|
|
810
|
+
return None
|
|
811
|
+
|
|
812
|
+
versions = await artifact_service.list_versions(
|
|
813
|
+
app_name=app_name,
|
|
814
|
+
user_id=user_id,
|
|
815
|
+
session_id=session_id,
|
|
816
|
+
filename=filename,
|
|
817
|
+
)
|
|
818
|
+
if not versions:
|
|
819
|
+
log.debug("%s No versions found for artifact.", log_identifier)
|
|
820
|
+
return None
|
|
821
|
+
|
|
822
|
+
latest_version = max(versions)
|
|
823
|
+
log.debug("%s Resolved latest version to %d.", log_identifier, latest_version)
|
|
824
|
+
return latest_version
|
|
825
|
+
except Exception as e:
|
|
826
|
+
log.error("%s Error resolving latest version: %s", log_identifier, e)
|
|
827
|
+
return None
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
async def get_artifact_counts_batch(
|
|
831
|
+
artifact_service: BaseArtifactService,
|
|
832
|
+
app_name: str,
|
|
833
|
+
user_id: str,
|
|
834
|
+
session_ids: List[str],
|
|
835
|
+
) -> Dict[str, int]:
|
|
836
|
+
"""
|
|
837
|
+
Get artifact counts for multiple sessions in a batch operation.
|
|
838
|
+
|
|
839
|
+
Args:
|
|
840
|
+
artifact_service: The artifact service instance.
|
|
841
|
+
app_name: The application name.
|
|
842
|
+
user_id: The user ID.
|
|
843
|
+
session_ids: List of session IDs to get counts for.
|
|
844
|
+
|
|
845
|
+
Returns:
|
|
846
|
+
Dict mapping session_id to artifact_count (excluding metadata files)
|
|
847
|
+
"""
|
|
848
|
+
log_prefix = f"[ArtifactHelper:get_counts_batch] App={app_name}, User={user_id} -"
|
|
849
|
+
counts: Dict[str, int] = {}
|
|
850
|
+
|
|
851
|
+
try:
|
|
852
|
+
list_keys_method = getattr(artifact_service, "list_artifact_keys")
|
|
853
|
+
|
|
854
|
+
for session_id in session_ids:
|
|
855
|
+
try:
|
|
856
|
+
keys = await list_keys_method(
|
|
857
|
+
app_name=app_name, user_id=user_id, session_id=session_id
|
|
858
|
+
)
|
|
859
|
+
# Count only non-metadata files
|
|
860
|
+
count = sum(1 for key in keys if not key.endswith(METADATA_SUFFIX))
|
|
861
|
+
counts[session_id] = count
|
|
862
|
+
log.debug("%s Session %s has %d artifacts", log_prefix, session_id, count)
|
|
863
|
+
except Exception as e:
|
|
864
|
+
log.warning("%s Failed to get count for session %s: %s", log_prefix, session_id, e)
|
|
865
|
+
counts[session_id] = 0
|
|
866
|
+
|
|
867
|
+
except Exception as e:
|
|
868
|
+
log.exception("%s Error in batch count operation: %s", log_prefix, e)
|
|
869
|
+
# Return 0 for all sessions on error
|
|
870
|
+
return {session_id: 0 for session_id in session_ids}
|
|
871
|
+
|
|
872
|
+
return counts
|
|
873
|
+
|
|
874
|
+
|
|
875
|
+
async def get_artifact_info_list(
|
|
876
|
+
artifact_service: BaseArtifactService,
|
|
877
|
+
app_name: str,
|
|
878
|
+
user_id: str,
|
|
879
|
+
session_id: str,
|
|
880
|
+
) -> List[ArtifactInfo]:
|
|
881
|
+
"""
|
|
882
|
+
Retrieves detailed information for all artifacts using the artifact service.
|
|
883
|
+
|
|
884
|
+
Args:
|
|
885
|
+
artifact_service: The artifact service instance.
|
|
886
|
+
app_name: The application name.
|
|
887
|
+
user_id: The user ID.
|
|
888
|
+
session_id: The session ID.
|
|
889
|
+
|
|
890
|
+
Returns:
|
|
891
|
+
A list of ArtifactInfo objects.
|
|
892
|
+
"""
|
|
893
|
+
log_prefix = f"[ArtifactHelper:get_info_list] App={app_name}, User={user_id}, Session={session_id} -"
|
|
894
|
+
artifact_info_list: List[ArtifactInfo] = []
|
|
895
|
+
|
|
896
|
+
try:
|
|
897
|
+
list_keys_method = getattr(artifact_service, "list_artifact_keys")
|
|
898
|
+
keys = await list_keys_method(
|
|
899
|
+
app_name=app_name, user_id=user_id, session_id=session_id
|
|
900
|
+
)
|
|
901
|
+
log.debug(
|
|
902
|
+
"%s Found %d artifact keys. Fetching details...", log_prefix, len(keys)
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
for filename in keys:
|
|
906
|
+
if filename.endswith(METADATA_SUFFIX):
|
|
907
|
+
continue
|
|
908
|
+
|
|
909
|
+
log_identifier_item = f"{log_prefix} [{filename}]"
|
|
910
|
+
try:
|
|
911
|
+
|
|
912
|
+
version_count: int = 0
|
|
913
|
+
latest_version_num: Optional[int] = await get_latest_artifact_version(
|
|
914
|
+
artifact_service, app_name, user_id, session_id, filename
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
if hasattr(artifact_service, "list_versions"):
|
|
918
|
+
try:
|
|
919
|
+
available_versions = await artifact_service.list_versions(
|
|
920
|
+
app_name=app_name,
|
|
921
|
+
user_id=user_id,
|
|
922
|
+
session_id=session_id,
|
|
923
|
+
filename=filename,
|
|
924
|
+
)
|
|
925
|
+
version_count = len(available_versions)
|
|
926
|
+
except Exception as list_ver_err:
|
|
927
|
+
log.error(
|
|
928
|
+
"%s Error listing versions for count: %s.",
|
|
929
|
+
log_identifier_item,
|
|
930
|
+
list_ver_err,
|
|
931
|
+
)
|
|
932
|
+
|
|
933
|
+
data = await load_artifact_content_or_metadata(
|
|
934
|
+
artifact_service=artifact_service,
|
|
935
|
+
app_name=app_name,
|
|
936
|
+
user_id=user_id,
|
|
937
|
+
session_id=session_id,
|
|
938
|
+
filename=filename,
|
|
939
|
+
version="latest",
|
|
940
|
+
load_metadata_only=True,
|
|
941
|
+
log_identifier_prefix=log_identifier_item,
|
|
942
|
+
)
|
|
943
|
+
|
|
944
|
+
metadata = data.get("metadata", {})
|
|
945
|
+
mime_type = metadata.get("mime_type", "application/data")
|
|
946
|
+
size = metadata.get("size_bytes", 0)
|
|
947
|
+
schema_definition = metadata.get("schema", {})
|
|
948
|
+
description = metadata.get("description", "No description provided")
|
|
949
|
+
loaded_version_num = data.get("version", latest_version_num)
|
|
950
|
+
|
|
951
|
+
last_modified_ts = metadata.get("timestamp_utc")
|
|
952
|
+
last_modified_ts = metadata.get("timestamp_utc")
|
|
953
|
+
last_modified_iso = (
|
|
954
|
+
datetime.fromtimestamp(
|
|
955
|
+
last_modified_ts, tz=timezone.utc
|
|
956
|
+
).isoformat()
|
|
957
|
+
if last_modified_ts
|
|
958
|
+
else None
|
|
959
|
+
)
|
|
960
|
+
|
|
961
|
+
# Extract source from metadata
|
|
962
|
+
source = metadata.get("source")
|
|
963
|
+
|
|
964
|
+
artifact_info_list.append(
|
|
965
|
+
ArtifactInfo(
|
|
966
|
+
filename=filename,
|
|
967
|
+
mime_type=mime_type,
|
|
968
|
+
size=size,
|
|
969
|
+
last_modified=last_modified_iso,
|
|
970
|
+
schema_definition=schema_definition,
|
|
971
|
+
description=description,
|
|
972
|
+
version=loaded_version_num,
|
|
973
|
+
version_count=version_count,
|
|
974
|
+
source=source,
|
|
975
|
+
)
|
|
976
|
+
)
|
|
977
|
+
log.debug(
|
|
978
|
+
"%s Successfully processed artifact info.", log_identifier_item
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
except FileNotFoundError:
|
|
982
|
+
log.warning(
|
|
983
|
+
"%s Artifact file not found by service for key '%s'. Skipping.",
|
|
984
|
+
log_prefix,
|
|
985
|
+
filename,
|
|
986
|
+
)
|
|
987
|
+
except Exception as detail_e:
|
|
988
|
+
log.error(
|
|
989
|
+
"%s Error processing details for artifact '%s': %s\n%s",
|
|
990
|
+
log_prefix,
|
|
991
|
+
filename,
|
|
992
|
+
detail_e,
|
|
993
|
+
traceback.format_exc(),
|
|
994
|
+
)
|
|
995
|
+
artifact_info_list.append(
|
|
996
|
+
ArtifactInfo(
|
|
997
|
+
filename=filename,
|
|
998
|
+
size=0,
|
|
999
|
+
description=f"Error loading details: {detail_e}",
|
|
1000
|
+
mime_type="application/octet-stream",
|
|
1001
|
+
)
|
|
1002
|
+
)
|
|
1003
|
+
|
|
1004
|
+
except Exception as e:
|
|
1005
|
+
log.exception(
|
|
1006
|
+
"%s Error listing artifact keys or processing list: %s", log_prefix, e
|
|
1007
|
+
)
|
|
1008
|
+
return []
|
|
1009
|
+
return artifact_info_list
|
|
1010
|
+
|
|
1011
|
+
|
|
1012
|
+
async def load_artifact_content_or_metadata(
|
|
1013
|
+
artifact_service: BaseArtifactService,
|
|
1014
|
+
app_name: str,
|
|
1015
|
+
user_id: str,
|
|
1016
|
+
session_id: str,
|
|
1017
|
+
filename: str,
|
|
1018
|
+
version: Union[int, str],
|
|
1019
|
+
load_metadata_only: bool = False,
|
|
1020
|
+
return_raw_bytes: bool = False,
|
|
1021
|
+
max_content_length: Optional[int] = None,
|
|
1022
|
+
include_line_numbers: bool = False,
|
|
1023
|
+
component: Optional[Any] = None,
|
|
1024
|
+
log_identifier_prefix: str = "[ArtifactHelper:load]",
|
|
1025
|
+
encoding: str = "utf-8",
|
|
1026
|
+
error_handling: str = "strict",
|
|
1027
|
+
) -> Dict[str, Any]:
|
|
1028
|
+
"""
|
|
1029
|
+
Loads the content or metadata of a specific artifact version using the artifact service.
|
|
1030
|
+
"""
|
|
1031
|
+
log_identifier_req = f"{log_identifier_prefix}:{filename}:{version}"
|
|
1032
|
+
log.debug(
|
|
1033
|
+
"%s Processing request (load_metadata_only=%s, return_raw_bytes=%s) (async).",
|
|
1034
|
+
log_identifier_req,
|
|
1035
|
+
load_metadata_only,
|
|
1036
|
+
return_raw_bytes,
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
if max_content_length is None and component:
|
|
1040
|
+
max_content_length = component.get_config("text_artifact_content_max_length")
|
|
1041
|
+
if max_content_length is None:
|
|
1042
|
+
raise ValueError(
|
|
1043
|
+
f"{log_identifier_req} Component config 'text_artifact_content_max_length' is not set."
|
|
1044
|
+
)
|
|
1045
|
+
|
|
1046
|
+
if max_content_length < 100:
|
|
1047
|
+
log.warning(
|
|
1048
|
+
"%s text_artifact_content_max_length too small (%d), using minimum: 100",
|
|
1049
|
+
log_identifier_req,
|
|
1050
|
+
max_content_length,
|
|
1051
|
+
)
|
|
1052
|
+
max_content_length = 100
|
|
1053
|
+
elif max_content_length > TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY:
|
|
1054
|
+
log.warning(
|
|
1055
|
+
"%s text_artifact_content_max_length too large (%d), using maximum: %d",
|
|
1056
|
+
log_identifier_req,
|
|
1057
|
+
max_content_length,
|
|
1058
|
+
TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY,
|
|
1059
|
+
)
|
|
1060
|
+
max_content_length = TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY
|
|
1061
|
+
elif max_content_length is None:
|
|
1062
|
+
max_content_length = TEXT_ARTIFACT_CONTEXT_DEFAULT_LENGTH
|
|
1063
|
+
|
|
1064
|
+
log.debug(
|
|
1065
|
+
"%s Using max_content_length: %d characters (from %s).",
|
|
1066
|
+
log_identifier_req,
|
|
1067
|
+
max_content_length,
|
|
1068
|
+
"app config" if component else "default",
|
|
1069
|
+
)
|
|
1070
|
+
|
|
1071
|
+
try:
|
|
1072
|
+
actual_version: int
|
|
1073
|
+
if isinstance(version, str) and version.lower() == "latest":
|
|
1074
|
+
log.debug(
|
|
1075
|
+
"%s Requested version is 'latest', resolving...", log_identifier_req
|
|
1076
|
+
)
|
|
1077
|
+
try:
|
|
1078
|
+
list_versions_method = getattr(artifact_service, "list_versions")
|
|
1079
|
+
available_versions = await list_versions_method(
|
|
1080
|
+
app_name=app_name,
|
|
1081
|
+
user_id=user_id,
|
|
1082
|
+
session_id=session_id,
|
|
1083
|
+
filename=filename,
|
|
1084
|
+
)
|
|
1085
|
+
if not available_versions:
|
|
1086
|
+
raise FileNotFoundError(
|
|
1087
|
+
f"Artifact '{filename}' has no versions available to determine 'latest'."
|
|
1088
|
+
)
|
|
1089
|
+
actual_version = max(available_versions)
|
|
1090
|
+
log.info(
|
|
1091
|
+
"%s Resolved 'latest' to version %d.",
|
|
1092
|
+
log_identifier_req,
|
|
1093
|
+
actual_version,
|
|
1094
|
+
)
|
|
1095
|
+
except Exception as list_err:
|
|
1096
|
+
log.error(
|
|
1097
|
+
"%s Failed to list versions for '%s' to resolve 'latest': %s",
|
|
1098
|
+
log_identifier_req,
|
|
1099
|
+
filename,
|
|
1100
|
+
list_err,
|
|
1101
|
+
)
|
|
1102
|
+
raise FileNotFoundError(
|
|
1103
|
+
f"Could not determine latest version for '{filename}': {list_err}"
|
|
1104
|
+
) from list_err
|
|
1105
|
+
elif isinstance(version, int):
|
|
1106
|
+
actual_version = version
|
|
1107
|
+
elif isinstance(version, str):
|
|
1108
|
+
try:
|
|
1109
|
+
actual_version = int(version)
|
|
1110
|
+
except ValueError:
|
|
1111
|
+
raise ValueError(
|
|
1112
|
+
f"Invalid version specified: '{version}'. Must be a positive integer string or 'latest'."
|
|
1113
|
+
)
|
|
1114
|
+
else:
|
|
1115
|
+
raise ValueError(
|
|
1116
|
+
f"Invalid version type: '{type(version).__name__}'. Must be an integer or 'latest'."
|
|
1117
|
+
)
|
|
1118
|
+
|
|
1119
|
+
if actual_version < 0:
|
|
1120
|
+
raise ValueError(
|
|
1121
|
+
f"Version number must be a positive integer. Got: {actual_version}"
|
|
1122
|
+
)
|
|
1123
|
+
|
|
1124
|
+
target_filename = (
|
|
1125
|
+
f"{filename}{METADATA_SUFFIX}" if load_metadata_only else filename
|
|
1126
|
+
)
|
|
1127
|
+
version_to_load = actual_version
|
|
1128
|
+
|
|
1129
|
+
log_identifier = f"{log_identifier_prefix}:{filename}:{version_to_load}"
|
|
1130
|
+
|
|
1131
|
+
log.debug(
|
|
1132
|
+
"%s Attempting to load '%s' v%d (async)",
|
|
1133
|
+
log_identifier,
|
|
1134
|
+
target_filename,
|
|
1135
|
+
version_to_load,
|
|
1136
|
+
)
|
|
1137
|
+
|
|
1138
|
+
load_artifact_method = getattr(artifact_service, "load_artifact")
|
|
1139
|
+
artifact_part = await load_artifact_method(
|
|
1140
|
+
app_name=app_name,
|
|
1141
|
+
user_id=user_id,
|
|
1142
|
+
session_id=session_id,
|
|
1143
|
+
filename=target_filename,
|
|
1144
|
+
version=version_to_load,
|
|
1145
|
+
)
|
|
1146
|
+
|
|
1147
|
+
if not artifact_part or not artifact_part.inline_data:
|
|
1148
|
+
raise FileNotFoundError(
|
|
1149
|
+
f"Artifact '{target_filename}' version {version_to_load} not found or has no data."
|
|
1150
|
+
)
|
|
1151
|
+
|
|
1152
|
+
mime_type = artifact_part.inline_data.mime_type
|
|
1153
|
+
data_bytes = artifact_part.inline_data.data
|
|
1154
|
+
size_bytes = len(data_bytes)
|
|
1155
|
+
|
|
1156
|
+
if load_metadata_only:
|
|
1157
|
+
if mime_type != "application/json":
|
|
1158
|
+
log.warning(
|
|
1159
|
+
"%s Expected metadata file '%s' v%d to be application/json, but got '%s'. Attempting parse anyway.",
|
|
1160
|
+
log_identifier,
|
|
1161
|
+
target_filename,
|
|
1162
|
+
version_to_load,
|
|
1163
|
+
mime_type,
|
|
1164
|
+
)
|
|
1165
|
+
try:
|
|
1166
|
+
metadata_dict = json.loads(data_bytes.decode("utf-8"))
|
|
1167
|
+
log.info(
|
|
1168
|
+
"%s Successfully loaded and parsed metadata for '%s' v%d.",
|
|
1169
|
+
log_identifier,
|
|
1170
|
+
filename,
|
|
1171
|
+
version_to_load,
|
|
1172
|
+
)
|
|
1173
|
+
return {
|
|
1174
|
+
"status": "success",
|
|
1175
|
+
"filename": filename,
|
|
1176
|
+
"version": version_to_load,
|
|
1177
|
+
"metadata": metadata_dict,
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
except (json.JSONDecodeError, UnicodeDecodeError) as parse_err:
|
|
1181
|
+
raise ValueError(
|
|
1182
|
+
f"Failed to parse metadata file '{target_filename}' v{version_to_load}: {parse_err}"
|
|
1183
|
+
) from parse_err
|
|
1184
|
+
|
|
1185
|
+
else:
|
|
1186
|
+
if return_raw_bytes:
|
|
1187
|
+
log.info(
|
|
1188
|
+
"%s Loaded artifact '%s' v%d (%d bytes, type: %s). Returning raw_bytes.",
|
|
1189
|
+
log_identifier,
|
|
1190
|
+
filename,
|
|
1191
|
+
version_to_load,
|
|
1192
|
+
size_bytes,
|
|
1193
|
+
mime_type,
|
|
1194
|
+
)
|
|
1195
|
+
return {
|
|
1196
|
+
"status": "success",
|
|
1197
|
+
"filename": filename,
|
|
1198
|
+
"version": version_to_load,
|
|
1199
|
+
"mime_type": mime_type,
|
|
1200
|
+
"raw_bytes": data_bytes,
|
|
1201
|
+
"size_bytes": size_bytes,
|
|
1202
|
+
}
|
|
1203
|
+
else:
|
|
1204
|
+
is_text = is_text_based_file(mime_type, data_bytes)
|
|
1205
|
+
|
|
1206
|
+
if is_text:
|
|
1207
|
+
try:
|
|
1208
|
+
content_str = data_bytes.decode(encoding, errors=error_handling)
|
|
1209
|
+
original_content_str = content_str # Save for line count calculation
|
|
1210
|
+
|
|
1211
|
+
# Add line numbers if requested (before truncation)
|
|
1212
|
+
if include_line_numbers:
|
|
1213
|
+
lines = content_str.split('\n')
|
|
1214
|
+
numbered_lines = [f"{i+1}\t{line}" for i, line in enumerate(lines)]
|
|
1215
|
+
content_str = '\n'.join(numbered_lines)
|
|
1216
|
+
log.debug(
|
|
1217
|
+
"%s Added line numbers to %d lines.",
|
|
1218
|
+
log_identifier,
|
|
1219
|
+
len(lines)
|
|
1220
|
+
)
|
|
1221
|
+
|
|
1222
|
+
message_to_llm = ""
|
|
1223
|
+
if len(content_str) > max_content_length:
|
|
1224
|
+
truncated_content = content_str[:max_content_length] + "..."
|
|
1225
|
+
|
|
1226
|
+
# Calculate line range if line numbers are included
|
|
1227
|
+
line_range_msg = ""
|
|
1228
|
+
if include_line_numbers:
|
|
1229
|
+
visible_line_count = truncated_content.count('\n') + 1
|
|
1230
|
+
total_line_count = original_content_str.count('\n') + 1
|
|
1231
|
+
line_range_msg = f" Lines 1-{visible_line_count} of {total_line_count} total."
|
|
1232
|
+
|
|
1233
|
+
if (
|
|
1234
|
+
max_content_length
|
|
1235
|
+
< TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY
|
|
1236
|
+
):
|
|
1237
|
+
message_to_llm = f"""This artifact content has been truncated to {max_content_length} characters.{line_range_msg}
|
|
1238
|
+
The artifact is larger ({len(content_str)} characters).
|
|
1239
|
+
Please request again with larger max size up to {TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY} for the full artifact."""
|
|
1240
|
+
else:
|
|
1241
|
+
message_to_llm = f"""This artifact content has been truncated to {max_content_length} characters.{line_range_msg}
|
|
1242
|
+
The artifact content met the maximum allowed size of {TEXT_ARTIFACT_CONTEXT_MAX_LENGTH_CAPACITY} characters.
|
|
1243
|
+
Please continue with this truncated content as the full artifact cannot be provided."""
|
|
1244
|
+
log.info(
|
|
1245
|
+
"%s Loaded and decoded text artifact '%s' v%d. Returning truncated content (%d chars, limit: %d).%s",
|
|
1246
|
+
log_identifier,
|
|
1247
|
+
filename,
|
|
1248
|
+
version_to_load,
|
|
1249
|
+
len(truncated_content),
|
|
1250
|
+
max_content_length,
|
|
1251
|
+
line_range_msg,
|
|
1252
|
+
)
|
|
1253
|
+
else:
|
|
1254
|
+
truncated_content = content_str
|
|
1255
|
+
log.info(
|
|
1256
|
+
"%s Loaded and decoded text artifact '%s' v%d. Returning full content (%d chars).",
|
|
1257
|
+
log_identifier,
|
|
1258
|
+
filename,
|
|
1259
|
+
version_to_load,
|
|
1260
|
+
len(content_str),
|
|
1261
|
+
)
|
|
1262
|
+
return {
|
|
1263
|
+
"status": "success",
|
|
1264
|
+
"filename": filename,
|
|
1265
|
+
"version": version_to_load,
|
|
1266
|
+
"mime_type": mime_type,
|
|
1267
|
+
"content": truncated_content,
|
|
1268
|
+
"message_to_llm": message_to_llm,
|
|
1269
|
+
"size_bytes": size_bytes,
|
|
1270
|
+
}
|
|
1271
|
+
except UnicodeDecodeError as decode_err:
|
|
1272
|
+
log.error(
|
|
1273
|
+
"%s Failed to decode text artifact '%s' v%d with encoding '%s': %s",
|
|
1274
|
+
log_identifier,
|
|
1275
|
+
filename,
|
|
1276
|
+
version_to_load,
|
|
1277
|
+
mime_type,
|
|
1278
|
+
decode_err,
|
|
1279
|
+
)
|
|
1280
|
+
raise ValueError(
|
|
1281
|
+
f"Failed to decode artifact '{filename}' v{version_to_load} as {encoding}."
|
|
1282
|
+
) from decode_err
|
|
1283
|
+
else:
|
|
1284
|
+
log.info(
|
|
1285
|
+
"%s Loaded binary/unknown artifact '%s' v%d. Returning metadata summary.",
|
|
1286
|
+
log_identifier,
|
|
1287
|
+
filename,
|
|
1288
|
+
version_to_load,
|
|
1289
|
+
)
|
|
1290
|
+
|
|
1291
|
+
metadata_for_binary = {}
|
|
1292
|
+
if not filename.endswith(METADATA_SUFFIX):
|
|
1293
|
+
try:
|
|
1294
|
+
metadata_filename_for_binary = (
|
|
1295
|
+
f"{filename}{METADATA_SUFFIX}"
|
|
1296
|
+
)
|
|
1297
|
+
log.debug(
|
|
1298
|
+
"%s Attempting to load linked metadata file '%s' for binary artifact '%s' v%d.",
|
|
1299
|
+
log_identifier,
|
|
1300
|
+
metadata_filename_for_binary,
|
|
1301
|
+
filename,
|
|
1302
|
+
version_to_load,
|
|
1303
|
+
)
|
|
1304
|
+
metadata_data = await load_artifact_content_or_metadata(
|
|
1305
|
+
artifact_service=artifact_service,
|
|
1306
|
+
app_name=app_name,
|
|
1307
|
+
user_id=user_id,
|
|
1308
|
+
session_id=session_id,
|
|
1309
|
+
filename=f"{filename}{METADATA_SUFFIX}",
|
|
1310
|
+
version=version,
|
|
1311
|
+
load_metadata_only=True,
|
|
1312
|
+
log_identifier_prefix=f"{log_identifier}[meta_for_binary]",
|
|
1313
|
+
)
|
|
1314
|
+
if metadata_data.get("status") == "success":
|
|
1315
|
+
metadata_for_binary = metadata_data.get("metadata", {})
|
|
1316
|
+
except Exception as e_meta_bin:
|
|
1317
|
+
log.warning(
|
|
1318
|
+
f"{log_identifier} Could not load metadata for binary artifact {filename}: {e_meta_bin}"
|
|
1319
|
+
)
|
|
1320
|
+
metadata_for_binary = {
|
|
1321
|
+
"error": f"Could not load metadata: {e_meta_bin}"
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
return {
|
|
1325
|
+
"status": "success",
|
|
1326
|
+
"filename": filename,
|
|
1327
|
+
"version": version_to_load,
|
|
1328
|
+
"mime_type": mime_type,
|
|
1329
|
+
"size_bytes": size_bytes,
|
|
1330
|
+
"metadata": metadata_for_binary,
|
|
1331
|
+
"content": f"Binary data of type {mime_type}. Content not displayed.",
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
except FileNotFoundError as fnf_err:
|
|
1335
|
+
log.warning("%s Artifact not found: %s", log_identifier_req, fnf_err)
|
|
1336
|
+
return {"status": "not_found", "message": str(fnf_err)}
|
|
1337
|
+
except ValueError as val_err:
|
|
1338
|
+
log.error(
|
|
1339
|
+
"%s Value error during artifact load: %s", log_identifier_req, val_err
|
|
1340
|
+
)
|
|
1341
|
+
return {"status": "error", "message": str(val_err)}
|
|
1342
|
+
except Exception as e:
|
|
1343
|
+
log.exception(
|
|
1344
|
+
"%s Unexpected error loading artifact '%s' version '%s': %s",
|
|
1345
|
+
log_identifier_req,
|
|
1346
|
+
filename,
|
|
1347
|
+
version,
|
|
1348
|
+
e,
|
|
1349
|
+
)
|
|
1350
|
+
return {
|
|
1351
|
+
"status": "error",
|
|
1352
|
+
"message": f"Unexpected error loading artifact: {e}",
|
|
1353
|
+
}
|