solace-agent-mesh 1.4.12__py3-none-any.whl → 1.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- solace_agent_mesh/agent/adk/adk_llm.txt +3 -4
- solace_agent_mesh/agent/adk/adk_llm_detail.txt +566 -0
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +1 -1
- solace_agent_mesh/agent/adk/callbacks.py +56 -5
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +3 -1
- solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +2 -1
- solace_agent_mesh/agent/adk/mcp_content_processor.py +2 -1
- solace_agent_mesh/agent/adk/models/lite_llm.py +1 -0
- solace_agent_mesh/agent/adk/models/models_llm.txt +1 -2
- solace_agent_mesh/agent/adk/runner.py +3 -1
- solace_agent_mesh/agent/adk/services.py +4 -1
- solace_agent_mesh/agent/adk/setup.py +3 -1
- solace_agent_mesh/agent/adk/tool_wrapper.py +2 -2
- solace_agent_mesh/agent/agent_llm.txt +1 -1
- solace_agent_mesh/agent/agent_llm_detail.txt +1702 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +4 -14
- solace_agent_mesh/agent/protocol/protocol_llm.txt +15 -2
- solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +92 -0
- solace_agent_mesh/agent/sac/app.py +3 -1
- solace_agent_mesh/agent/sac/component.py +55 -22
- solace_agent_mesh/agent/sac/sac_llm.txt +15 -1
- solace_agent_mesh/agent/sac/sac_llm_detail.txt +200 -0
- solace_agent_mesh/agent/sac/task_execution_context.py +73 -0
- solace_agent_mesh/agent/testing/testing_llm_detail.txt +68 -0
- solace_agent_mesh/agent/tools/audio_tools.py +2 -1
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +3 -1
- solace_agent_mesh/agent/tools/builtin_data_analysis_tools.py +3 -1
- solace_agent_mesh/agent/tools/dynamic_tool.py +2 -1
- solace_agent_mesh/agent/tools/general_agent_tools.py +2 -1
- solace_agent_mesh/agent/tools/image_tools.py +2 -1
- solace_agent_mesh/agent/tools/peer_agent_tool.py +2 -1
- solace_agent_mesh/agent/tools/registry.py +3 -1
- solace_agent_mesh/agent/tools/test_tools.py +2 -1
- solace_agent_mesh/agent/tools/tools_llm.txt +148 -154
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +274 -0
- solace_agent_mesh/agent/tools/web_tools.py +2 -1
- solace_agent_mesh/agent/utils/artifact_helpers.py +3 -1
- solace_agent_mesh/agent/utils/config_parser.py +3 -1
- solace_agent_mesh/agent/utils/utils_llm.txt +1 -1
- solace_agent_mesh/agent/utils/utils_llm_detail.txt +149 -0
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/{b7006a3a.73a79653.js → 032c2d61.f3d37824.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/0bcf40b7.c019ad46.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2131ec11.5c7a1f6e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{2334.622a6395.js → 2334.1cf50a20.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/341393d4.0fac2613.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{3624.b524e433.js → 3624.0eaa1fd0.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.76654dd9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/509e993c.4c7a1a6d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55b7b518.f2b1d1ba.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.e49689dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6063ff4c.ef84f702.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.39d5851d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.4a5fbf39.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6fdfefc7.99de744e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.804d6567.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/722f809d.965da774.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/742f027b.46c07808.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/77cf947d.64c9bd6c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/81a99df0.07034dd9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{8591.d7c16be6.js → 8591.5d015485.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/{8731.49e930c2.js → 8731.6c1dbf0c.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/945fb41e.6f4cdffd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9bb13469.dd1c9b54.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.dd810042.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e6dd091.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ad71b5ed.60668e9e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c198a0dc.8f31f867.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.9d369087.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/db924877.cbc66f02.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de5f4c65.e8241890.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.139b4b9c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.2b916f9e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.582a78ca.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.5766a13d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.9c0297a6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.bd3c34f3.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.18dc45dd.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +143 -0
- solace_agent_mesh/assets/docs/docs/documentation/{user-guide → components}/builtin-tools/artifact-management/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/{user-guide → components}/builtin-tools/audio-tools/index.html +7 -7
- solace_agent_mesh/assets/docs/docs/documentation/{user-guide → components}/builtin-tools/data-analysis-tools/index.html +8 -8
- solace_agent_mesh/assets/docs/docs/documentation/{user-guide → components}/builtin-tools/embeds/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/{user-guide → components}/builtin-tools/index.html +11 -11
- solace_agent_mesh/assets/docs/docs/documentation/{concepts → components}/cli/index.html +25 -25
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +91 -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/deploying/debugging/index.html +104 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +57 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +25 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +59 -0
- solace_agent_mesh/assets/docs/docs/documentation/{user-guide → developing}/create-agents/index.html +113 -152
- solace_agent_mesh/assets/docs/docs/documentation/{user-guide → developing}/create-gateways/index.html +9 -9
- solace_agent_mesh/assets/docs/docs/documentation/{user-guide → developing}/creating-python-tools/index.html +12 -12
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +54 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +32 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +55 -0
- solace_agent_mesh/assets/docs/docs/documentation/{tutorials → developing/tutorials}/bedrock-agents/index.html +25 -25
- solace_agent_mesh/assets/docs/docs/documentation/{tutorials → developing/tutorials}/custom-agent/index.html +13 -13
- solace_agent_mesh/assets/docs/docs/documentation/{tutorials → developing/tutorials}/event-mesh-gateway/index.html +11 -11
- solace_agent_mesh/assets/docs/docs/documentation/{tutorials → developing/tutorials}/mcp-integration/index.html +10 -10
- solace_agent_mesh/assets/docs/docs/documentation/{tutorials → developing/tutorials}/mongodb-integration/index.html +13 -13
- solace_agent_mesh/assets/docs/docs/documentation/{tutorials → developing/tutorials}/rag-integration/index.html +13 -13
- solace_agent_mesh/assets/docs/docs/documentation/{tutorials → developing/tutorials}/rest-gateway/index.html +10 -10
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +72 -0
- solace_agent_mesh/assets/docs/docs/documentation/{tutorials → developing/tutorials}/sql-database/index.html +14 -14
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +33 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +83 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +222 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +161 -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 +53 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +35 -100
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +41 -0
- solace_agent_mesh/assets/docs/docs/documentation/{getting-started → installing-and-configuring}/configurations/index.html +56 -50
- 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 +76 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +63 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +142 -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 To 0.3.0/a2a-technical-migration-map/index.html → migrations/a2a-upgrade/a2a-technical-migration-map/index.html} +10 -11
- solace_agent_mesh/assets/docs/img/solace-logo.png +0 -0
- solace_agent_mesh/assets/docs/lunr-index-1760121512891.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1760121512891.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/assets/docs/sitemap.xml +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-j1LW-wlq.js → authCallback-DwrxZE0E.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-B9p_nFNA.js → client-DarGQzyw.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-2nd1gbaH.js +339 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-DoKXctCM.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CS5YMf8a.js → vendor-BKIeiHj_.js} +80 -70
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
- solace_agent_mesh/common/a2a/a2a_llm.txt +1 -1
- solace_agent_mesh/common/a2a/a2a_llm_detail.txt +193 -0
- solace_agent_mesh/common/a2a/artifact.py +2 -1
- solace_agent_mesh/common/a2a/protocol.py +3 -2
- solace_agent_mesh/common/a2a/translation.py +3 -1
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +1 -1
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +736 -0
- solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +23 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +93 -15
- solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +23 -0
- solace_agent_mesh/common/common_llm.txt +24 -39
- solace_agent_mesh/common/common_llm_detail.txt +2562 -0
- solace_agent_mesh/common/data_parts.py +9 -1
- solace_agent_mesh/common/middleware/config_resolver.py +3 -1
- solace_agent_mesh/common/middleware/middleware_llm_detail.txt +185 -0
- solace_agent_mesh/common/middleware/registry.py +3 -1
- solace_agent_mesh/common/sac/sac_llm.txt +1 -1
- solace_agent_mesh/common/sac/sac_llm_detail.txt +82 -0
- solace_agent_mesh/common/sac/sam_component_base.py +2 -1
- solace_agent_mesh/common/sam_events/event_service.py +3 -2
- 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/employee_service.py +3 -1
- solace_agent_mesh/common/services/identity_service.py +2 -1
- solace_agent_mesh/common/services/providers/local_file_identity_service.py +2 -1
- solace_agent_mesh/common/services/services_llm.txt +57 -6
- solace_agent_mesh/common/services/services_llm_detail.txt +459 -0
- solace_agent_mesh/common/utils/artifact_utils.py +3 -1
- solace_agent_mesh/common/utils/asyncio_macos_fix.py +3 -1
- solace_agent_mesh/common/utils/embeds/converter.py +3 -1
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +1 -1
- solace_agent_mesh/common/utils/embeds/evaluators.py +2 -1
- solace_agent_mesh/common/utils/embeds/modifiers.py +3 -2
- solace_agent_mesh/common/utils/embeds/resolver.py +2 -1
- solace_agent_mesh/common/utils/initializer.py +3 -1
- solace_agent_mesh/common/utils/message_utils.py +2 -1
- solace_agent_mesh/common/utils/push_notification_auth.py +3 -2
- solace_agent_mesh/common/utils/utils_llm.txt +75 -87
- solace_agent_mesh/common/utils/utils_llm_detail.txt +572 -0
- solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +101 -0
- solace_agent_mesh/core_a2a/service.py +2 -2
- solace_agent_mesh/gateway/base/app.py +3 -2
- solace_agent_mesh/gateway/base/base_llm.txt +1 -1
- solace_agent_mesh/gateway/base/base_llm_detail.txt +235 -0
- solace_agent_mesh/gateway/base/component.py +3 -1
- solace_agent_mesh/gateway/base/task_context.py +2 -1
- solace_agent_mesh/gateway/gateway_llm.txt +242 -235
- solace_agent_mesh/gateway/gateway_llm_detail.txt +3885 -0
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +295 -0
- solace_agent_mesh/gateway/http_sse/alembic/env.py +10 -1
- 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/versions_llm.txt +155 -0
- solace_agent_mesh/gateway/http_sse/alembic.ini +1 -1
- solace_agent_mesh/gateway/http_sse/app.py +150 -3
- solace_agent_mesh/gateway/http_sse/component.py +372 -61
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +46 -6
- solace_agent_mesh/gateway/http_sse/components/task_logger_forwarder.py +109 -0
- solace_agent_mesh/gateway/http_sse/components/visualization_forwarder_component.py +4 -2
- solace_agent_mesh/gateway/http_sse/dependencies.py +119 -27
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +172 -172
- solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +3278 -0
- solace_agent_mesh/gateway/http_sse/main.py +149 -42
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +3 -12
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +103 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +5 -3
- solace_agent_mesh/gateway/http_sse/repository/entities/chat_task.py +75 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +263 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/feedback.py +20 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +0 -16
- solace_agent_mesh/gateway/http_sse/repository/entities/task.py +25 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/task_event.py +21 -0
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +81 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +73 -18
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -5
- 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 +266 -0
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +3 -3
- 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 +32 -0
- solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +340 -0
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +4 -53
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +173 -0
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +3 -2
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +3 -3
- solace_agent_mesh/gateway/http_sse/routers/auth.py +3 -1
- solace_agent_mesh/gateway/http_sse/routers/config.py +29 -6
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +346 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +3 -3
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +83 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +2 -10
- 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 +5 -3
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +107 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +1 -15
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/task_responses.py +30 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +37 -0
- solace_agent_mesh/gateway/http_sse/routers/people.py +3 -1
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +255 -204
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +223 -41
- solace_agent_mesh/gateway/http_sse/routers/sse.py +3 -2
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +170 -43
- solace_agent_mesh/gateway/http_sse/routers/users.py +3 -1
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +2 -1
- solace_agent_mesh/gateway/http_sse/services/agent_card_service.py +3 -1
- solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +273 -0
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +242 -0
- solace_agent_mesh/gateway/http_sse/services/people_service.py +2 -82
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +177 -13
- solace_agent_mesh/gateway/http_sse/services/session_service.py +154 -85
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +318 -0
- solace_agent_mesh/gateway/http_sse/services/task_service.py +3 -2
- solace_agent_mesh/gateway/http_sse/session_manager.py +2 -1
- solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +25 -14
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +285 -0
- solace_agent_mesh/gateway/http_sse/shared/types.py +7 -0
- solace_agent_mesh/gateway/http_sse/sse_event_buffer.py +2 -1
- solace_agent_mesh/gateway/http_sse/sse_manager.py +2 -2
- solace_agent_mesh/gateway/http_sse/utils/__init__.py +1 -0
- solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +32 -0
- solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +47 -0
- solace_agent_mesh/solace_agent_mesh_llm.txt +1 -1
- solace_agent_mesh/solace_agent_mesh_llm_detail.txt +8599 -0
- solace_agent_mesh/templates/gateway_app_template.py +4 -2
- solace_agent_mesh/templates/gateway_component_template.py +3 -1
- solace_agent_mesh/templates/logging_config_template.ini +22 -45
- solace_agent_mesh/templates/plugin_tools_template.py +2 -2
- {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.1.dist-info}/METADATA +2 -2
- solace_agent_mesh-1.5.1.dist-info/RECORD +504 -0
- solace_agent_mesh/agent/adk/invocation_monitor.py +0 -295
- solace_agent_mesh/assets/docs/assets/images/sac-flows-80d5b603c6aafd33e87945680ce0abf3.png +0 -0
- solace_agent_mesh/assets/docs/assets/images/sac_parts_of_a_component-cb3d0424b1d0c17734c5435cca6b4082.png +0 -0
- solace_agent_mesh/assets/docs/assets/js/04989206.a248f00c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/0e682baa.d54b8668.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/1023fc19.8a8a9309.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/1523c6b4.2645ef68.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/166ab619.e27886d9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/1c6e87d2.e056b7e0.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/21ceee5f.3bf39250.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/2a9cab12.2afaee76.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/332e10b5.f7629851.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3d406171.5560fdf9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/42b3f8d8.508ae8db.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/442a8107.b5c2532a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/453a82a6.3c6bb61d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/483cef9a.4736f2d8.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/4c2787c2.c1290a40.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/55f47984.bcd00a86.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/5b4258a4.fdfd2325.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/664b740a.ba305a89.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/75384d09.c19e8b51.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/768e31b0.9abcdc48.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/85387663.be2bc838.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/945fb41e.16e00776.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9a09e75d.92de8cf5.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9eff14a2.d62aad71.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/a12a4955.25fbed32.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/a3a92b25.af35e313.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/aba87c2f.4ddf32f2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ae0e903d.5fe5203f.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ae4415af.16cc58d3.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/bac0be12.17de4316.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/c2c06897.87cb1f47.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/c835a94d.ce21f0bf.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/cc969b05.feef7dcc.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/cd3d4052.a19e7d78.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ced92a13.fb92e7ca.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/cee5d587.47904f5e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/d6a81ee7.829198f1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.ed8dd236.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f897a61a.126663fe.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/fbfa3e75.e144b16c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.f67fc9f4.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.40527046.js +0 -1
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +0 -46
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/rbac-setup-guilde/index.html +0 -201
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +0 -25
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +0 -105
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +0 -144
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +0 -91
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +0 -91
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +0 -55
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +0 -111
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +0 -77
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +0 -48
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +0 -54
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +0 -45
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/litellm_models/index.html +0 -49
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +0 -76
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +0 -73
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +0 -72
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +0 -54
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +0 -69
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +0 -59
- solace_agent_mesh/assets/docs/lunr-index-1759936913198.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1759936913198.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-ChRwcV89.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-DnnE01OM.js +0 -339
- solace_agent_mesh/gateway/http_sse/repository/entities/message.py +0 -41
- solace_agent_mesh/gateway/http_sse/repository/message_repository.py +0 -84
- solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +0 -45
- solace_agent_mesh-1.4.12.dist-info/RECORD +0 -448
- /solace_agent_mesh/assets/docs/assets/js/{8591.d7c16be6.js.LICENSE.txt → 8591.5d015485.js.LICENSE.txt} +0 -0
- /solace_agent_mesh/assets/docs/assets/js/{main.f67fc9f4.js.LICENSE.txt → main.bd3c34f3.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.1.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.1.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.4.12.dist-info → solace_agent_mesh-1.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,21 +1,22 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
import uuid
|
|
2
|
-
from typing import TYPE_CHECKING, Optional
|
|
3
|
+
from typing import TYPE_CHECKING, Optional, List, Dict, Any
|
|
3
4
|
|
|
4
|
-
from solace_ai_connector.common.log import log
|
|
5
5
|
from sqlalchemy.orm import Session as DbSession
|
|
6
6
|
|
|
7
7
|
from ..repository import (
|
|
8
|
-
IMessageRepository,
|
|
9
8
|
ISessionRepository,
|
|
10
|
-
Message,
|
|
11
9
|
Session,
|
|
12
|
-
SessionHistory,
|
|
13
10
|
)
|
|
14
|
-
from ..
|
|
15
|
-
from ..
|
|
11
|
+
from ..repository.chat_task_repository import ChatTaskRepository
|
|
12
|
+
from ..repository.entities import ChatTask
|
|
13
|
+
from ..shared.enums import SenderType
|
|
14
|
+
from ..shared.types import SessionId, UserId
|
|
16
15
|
from ..shared import now_epoch_ms
|
|
17
16
|
from ..shared.pagination import PaginationParams, PaginatedResponse, get_pagination_or_default
|
|
18
17
|
|
|
18
|
+
log = logging.getLogger(__name__)
|
|
19
|
+
|
|
19
20
|
if TYPE_CHECKING:
|
|
20
21
|
from ..component import WebUIBackendComponent
|
|
21
22
|
|
|
@@ -28,11 +29,10 @@ class SessionService:
|
|
|
28
29
|
self.component = component
|
|
29
30
|
|
|
30
31
|
def _get_repositories(self, db: DbSession):
|
|
31
|
-
"""Create
|
|
32
|
-
from ..repository import SessionRepository
|
|
32
|
+
"""Create session repository for the given database session."""
|
|
33
|
+
from ..repository import SessionRepository
|
|
33
34
|
session_repository = SessionRepository(db)
|
|
34
|
-
|
|
35
|
-
return session_repository, message_repository
|
|
35
|
+
return session_repository
|
|
36
36
|
|
|
37
37
|
def is_persistence_enabled(self) -> bool:
|
|
38
38
|
"""Checks if the service is configured with a persistent backend."""
|
|
@@ -54,18 +54,10 @@ class SessionService:
|
|
|
54
54
|
raise ValueError("User ID cannot be empty")
|
|
55
55
|
|
|
56
56
|
pagination = get_pagination_or_default(pagination)
|
|
57
|
-
session_repository
|
|
58
|
-
|
|
59
|
-
pagination_info = PaginationInfo(
|
|
60
|
-
page=pagination.page_number,
|
|
61
|
-
page_size=pagination.page_size,
|
|
62
|
-
total_items=0,
|
|
63
|
-
total_pages=0,
|
|
64
|
-
has_next=False,
|
|
65
|
-
has_previous=False,
|
|
66
|
-
)
|
|
57
|
+
session_repository = self._get_repositories(db)
|
|
67
58
|
|
|
68
|
-
|
|
59
|
+
# Pass pagination params directly - repository will handle offset calculation
|
|
60
|
+
sessions = session_repository.find_by_user(user_id, pagination)
|
|
69
61
|
total_count = session_repository.count_by_user(user_id)
|
|
70
62
|
|
|
71
63
|
return PaginatedResponse.create(sessions, total_count, pagination)
|
|
@@ -76,33 +68,9 @@ class SessionService:
|
|
|
76
68
|
if not self._is_valid_session_id(session_id):
|
|
77
69
|
return None
|
|
78
70
|
|
|
79
|
-
session_repository
|
|
71
|
+
session_repository = self._get_repositories(db)
|
|
80
72
|
return session_repository.find_user_session(session_id, user_id)
|
|
81
73
|
|
|
82
|
-
def get_session_history(
|
|
83
|
-
self,
|
|
84
|
-
db: DbSession,
|
|
85
|
-
session_id: SessionId,
|
|
86
|
-
user_id: UserId,
|
|
87
|
-
pagination: PaginationInfo | None = None,
|
|
88
|
-
) -> SessionHistory | None:
|
|
89
|
-
if not self._is_valid_session_id(session_id):
|
|
90
|
-
return None
|
|
91
|
-
|
|
92
|
-
session_repository, _ = self._get_repositories(db)
|
|
93
|
-
result = session_repository.find_user_session_with_messages(
|
|
94
|
-
session_id, user_id, pagination
|
|
95
|
-
)
|
|
96
|
-
if not result:
|
|
97
|
-
return None
|
|
98
|
-
|
|
99
|
-
session, messages = result
|
|
100
|
-
return SessionHistory(
|
|
101
|
-
session=session,
|
|
102
|
-
messages=messages,
|
|
103
|
-
total_message_count=len(messages),
|
|
104
|
-
)
|
|
105
|
-
|
|
106
74
|
def create_session(
|
|
107
75
|
self,
|
|
108
76
|
db: DbSession,
|
|
@@ -131,7 +99,7 @@ class SessionService:
|
|
|
131
99
|
updated_time=now_ms,
|
|
132
100
|
)
|
|
133
101
|
|
|
134
|
-
session_repository
|
|
102
|
+
session_repository = self._get_repositories(db)
|
|
135
103
|
created_session = session_repository.save(session)
|
|
136
104
|
log.info("Created new session %s for user %s", created_session.id, user_id)
|
|
137
105
|
|
|
@@ -152,7 +120,7 @@ class SessionService:
|
|
|
152
120
|
if len(name.strip()) > 255:
|
|
153
121
|
raise ValueError("Session name cannot exceed 255 characters")
|
|
154
122
|
|
|
155
|
-
session_repository
|
|
123
|
+
session_repository = self._get_repositories(db)
|
|
156
124
|
session = session_repository.find_user_session(session_id, user_id)
|
|
157
125
|
if not session:
|
|
158
126
|
return None
|
|
@@ -169,7 +137,7 @@ class SessionService:
|
|
|
169
137
|
if not self._is_valid_session_id(session_id):
|
|
170
138
|
raise ValueError("Invalid session ID")
|
|
171
139
|
|
|
172
|
-
session_repository
|
|
140
|
+
session_repository = self._get_repositories(db)
|
|
173
141
|
session = session_repository.find_user_session(session_id, user_id)
|
|
174
142
|
if not session:
|
|
175
143
|
log.warning(
|
|
@@ -198,50 +166,151 @@ class SessionService:
|
|
|
198
166
|
|
|
199
167
|
return True
|
|
200
168
|
|
|
201
|
-
def
|
|
169
|
+
def save_task(
|
|
202
170
|
self,
|
|
203
171
|
db: DbSession,
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
172
|
+
task_id: str,
|
|
173
|
+
session_id: str,
|
|
174
|
+
user_id: str,
|
|
175
|
+
user_message: Optional[str],
|
|
176
|
+
message_bubbles: str, # JSON string (opaque)
|
|
177
|
+
task_metadata: Optional[str] = None # JSON string (opaque)
|
|
178
|
+
) -> ChatTask:
|
|
179
|
+
"""
|
|
180
|
+
Save a complete task interaction.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
db: Database session
|
|
184
|
+
task_id: A2A task ID
|
|
185
|
+
session_id: Session ID
|
|
186
|
+
user_id: User ID
|
|
187
|
+
user_message: Original user input text
|
|
188
|
+
message_bubbles: Array of all message bubbles displayed during this task
|
|
189
|
+
task_metadata: Task-level metadata (status, feedback, agent name, etc.)
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Saved ChatTask entity
|
|
193
|
+
|
|
194
|
+
Raises:
|
|
195
|
+
ValueError: If session not found or validation fails
|
|
196
|
+
"""
|
|
197
|
+
# Validate session exists and belongs to user
|
|
198
|
+
session_repository = self._get_repositories(db)
|
|
219
199
|
session = session_repository.find_user_session(session_id, user_id)
|
|
220
200
|
if not session:
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
)
|
|
227
|
-
|
|
228
|
-
message_entity = Message(
|
|
229
|
-
id=str(uuid.uuid4()),
|
|
201
|
+
raise ValueError(f"Session {session_id} not found for user {user_id}")
|
|
202
|
+
|
|
203
|
+
# Create task entity - pass strings directly
|
|
204
|
+
task = ChatTask(
|
|
205
|
+
id=task_id,
|
|
230
206
|
session_id=session_id,
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
207
|
+
user_id=user_id,
|
|
208
|
+
user_message=user_message,
|
|
209
|
+
message_bubbles=message_bubbles, # Already a string
|
|
210
|
+
task_metadata=task_metadata, # Already a string
|
|
235
211
|
created_time=now_epoch_ms(),
|
|
212
|
+
updated_time=None
|
|
236
213
|
)
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
214
|
+
|
|
215
|
+
# Save via repository
|
|
216
|
+
task_repo = ChatTaskRepository(db)
|
|
217
|
+
saved_task = task_repo.save(task)
|
|
218
|
+
|
|
219
|
+
# Update session activity
|
|
240
220
|
session.mark_activity()
|
|
241
221
|
session_repository.save(session)
|
|
222
|
+
|
|
223
|
+
log.info(f"Saved task {task_id} for session {session_id}")
|
|
224
|
+
return saved_task
|
|
242
225
|
|
|
243
|
-
|
|
244
|
-
|
|
226
|
+
def get_session_tasks(
|
|
227
|
+
self,
|
|
228
|
+
db: DbSession,
|
|
229
|
+
session_id: str,
|
|
230
|
+
user_id: str
|
|
231
|
+
) -> List[ChatTask]:
|
|
232
|
+
"""
|
|
233
|
+
Get all tasks for a session.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
db: Database session
|
|
237
|
+
session_id: Session ID
|
|
238
|
+
user_id: User ID
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
List of ChatTask entities in chronological order
|
|
242
|
+
|
|
243
|
+
Raises:
|
|
244
|
+
ValueError: If session not found
|
|
245
|
+
"""
|
|
246
|
+
# Validate session exists and belongs to user
|
|
247
|
+
session_repository = self._get_repositories(db)
|
|
248
|
+
session = session_repository.find_user_session(session_id, user_id)
|
|
249
|
+
if not session:
|
|
250
|
+
raise ValueError(f"Session {session_id} not found for user {user_id}")
|
|
251
|
+
|
|
252
|
+
# Load tasks
|
|
253
|
+
task_repo = ChatTaskRepository(db)
|
|
254
|
+
return task_repo.find_by_session(session_id, user_id)
|
|
255
|
+
|
|
256
|
+
def get_session_messages_from_tasks(
|
|
257
|
+
self,
|
|
258
|
+
db: DbSession,
|
|
259
|
+
session_id: str,
|
|
260
|
+
user_id: str
|
|
261
|
+
) -> List[Dict[str, Any]]:
|
|
262
|
+
"""
|
|
263
|
+
Get session messages by flattening task message_bubbles.
|
|
264
|
+
This provides backward compatibility with the old message-based API.
|
|
265
|
+
|
|
266
|
+
Args:
|
|
267
|
+
db: Database session
|
|
268
|
+
session_id: Session ID
|
|
269
|
+
user_id: User ID
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
List of message dictionaries flattened from tasks
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
ValueError: If session not found
|
|
276
|
+
"""
|
|
277
|
+
# Load tasks
|
|
278
|
+
tasks = self.get_session_tasks(db, session_id, user_id)
|
|
279
|
+
|
|
280
|
+
# Flatten message_bubbles from all tasks
|
|
281
|
+
messages = []
|
|
282
|
+
for task in tasks:
|
|
283
|
+
import json
|
|
284
|
+
message_bubbles = json.loads(task.message_bubbles) if isinstance(task.message_bubbles, str) else task.message_bubbles
|
|
285
|
+
|
|
286
|
+
for bubble in message_bubbles:
|
|
287
|
+
# Determine sender type from bubble type
|
|
288
|
+
bubble_type = bubble.get("type", "agent")
|
|
289
|
+
sender_type = "user" if bubble_type == "user" else "agent"
|
|
290
|
+
|
|
291
|
+
# Get sender name
|
|
292
|
+
if bubble_type == "user":
|
|
293
|
+
sender_name = user_id
|
|
294
|
+
else:
|
|
295
|
+
# Try to get agent name from task metadata, fallback to "agent"
|
|
296
|
+
sender_name = "agent"
|
|
297
|
+
if task.task_metadata:
|
|
298
|
+
task_metadata = json.loads(task.task_metadata) if isinstance(task.task_metadata, str) else task.task_metadata
|
|
299
|
+
sender_name = task_metadata.get("agent_name", "agent")
|
|
300
|
+
|
|
301
|
+
# Create message dictionary
|
|
302
|
+
message = {
|
|
303
|
+
"id": bubble.get("id", str(uuid.uuid4())),
|
|
304
|
+
"session_id": session_id,
|
|
305
|
+
"message": bubble.get("text", ""),
|
|
306
|
+
"sender_type": sender_type,
|
|
307
|
+
"sender_name": sender_name,
|
|
308
|
+
"message_type": "text",
|
|
309
|
+
"created_time": task.created_time
|
|
310
|
+
}
|
|
311
|
+
messages.append(message)
|
|
312
|
+
|
|
313
|
+
return messages
|
|
245
314
|
|
|
246
315
|
def _is_valid_session_id(self, session_id: SessionId) -> bool:
|
|
247
316
|
return (
|
|
@@ -289,4 +358,4 @@ class SessionService:
|
|
|
289
358
|
"Failed to publish session deletion event to agent %s: %s",
|
|
290
359
|
agent_id,
|
|
291
360
|
e,
|
|
292
|
-
)
|
|
361
|
+
)
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Service for logging A2A tasks and events to the database.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
import logging
|
|
7
|
+
import uuid
|
|
8
|
+
from typing import Any, Callable, Dict, Union
|
|
9
|
+
|
|
10
|
+
from a2a.types import (
|
|
11
|
+
A2ARequest,
|
|
12
|
+
JSONRPCError,
|
|
13
|
+
JSONRPCResponse,
|
|
14
|
+
Task as A2ATask,
|
|
15
|
+
TaskArtifactUpdateEvent,
|
|
16
|
+
TaskStatusUpdateEvent,
|
|
17
|
+
)
|
|
18
|
+
from sqlalchemy.orm import Session as DBSession
|
|
19
|
+
|
|
20
|
+
from ....common import a2a
|
|
21
|
+
from ..repository.entities import Task, TaskEvent
|
|
22
|
+
from ..repository.task_repository import TaskRepository
|
|
23
|
+
from ..shared import now_epoch_ms
|
|
24
|
+
|
|
25
|
+
log = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
class TaskLoggerService:
|
|
28
|
+
"""Service for logging A2A tasks and events to the database."""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self, session_factory: Callable[[], DBSession] | None, config: Dict[str, Any]
|
|
32
|
+
):
|
|
33
|
+
self.session_factory = session_factory
|
|
34
|
+
self.config = config
|
|
35
|
+
self.log_identifier = "[TaskLoggerService]"
|
|
36
|
+
log.info(f"{self.log_identifier} Initialized.")
|
|
37
|
+
|
|
38
|
+
def log_event(self, event_data: Dict[str, Any]):
|
|
39
|
+
"""
|
|
40
|
+
Parses a raw A2A message and logs it as a task event.
|
|
41
|
+
Creates or updates the master task record as needed.
|
|
42
|
+
"""
|
|
43
|
+
if not self.config.get("enabled", False):
|
|
44
|
+
return
|
|
45
|
+
|
|
46
|
+
if not self.session_factory:
|
|
47
|
+
log.warning(
|
|
48
|
+
f"{self.log_identifier} Task logging is enabled but no database is configured. Skipping event."
|
|
49
|
+
)
|
|
50
|
+
return
|
|
51
|
+
|
|
52
|
+
topic = event_data.get("topic")
|
|
53
|
+
payload = event_data.get("payload")
|
|
54
|
+
user_properties = event_data.get("user_properties", {})
|
|
55
|
+
|
|
56
|
+
if not topic or not payload:
|
|
57
|
+
log.warning(
|
|
58
|
+
f"{self.log_identifier} Received event with missing topic or payload."
|
|
59
|
+
)
|
|
60
|
+
return
|
|
61
|
+
|
|
62
|
+
if "discovery" in topic:
|
|
63
|
+
# Ignore discovery messages
|
|
64
|
+
return
|
|
65
|
+
|
|
66
|
+
# Parse the event into a Pydantic model first.
|
|
67
|
+
parsed_event = self._parse_a2a_event(topic, payload)
|
|
68
|
+
if parsed_event is None:
|
|
69
|
+
# Parsing failed or event should be ignored.
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
db = self.session_factory()
|
|
73
|
+
try:
|
|
74
|
+
repo = TaskRepository(db)
|
|
75
|
+
|
|
76
|
+
# Infer details from the parsed event
|
|
77
|
+
direction, task_id, user_id = self._infer_event_details(
|
|
78
|
+
topic, parsed_event, user_properties
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if not task_id:
|
|
82
|
+
log.debug(
|
|
83
|
+
f"{self.log_identifier} Could not determine task_id for event on topic {topic}. Skipping."
|
|
84
|
+
)
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
# Check if we should log this event type
|
|
88
|
+
if not self._should_log_event(topic, parsed_event):
|
|
89
|
+
log.debug(
|
|
90
|
+
f"{self.log_identifier} Event on topic {topic} is configured to be skipped."
|
|
91
|
+
)
|
|
92
|
+
return
|
|
93
|
+
|
|
94
|
+
# Sanitize the original raw payload before storing
|
|
95
|
+
sanitized_payload = self._sanitize_payload(payload)
|
|
96
|
+
|
|
97
|
+
# Check for existing task or create a new one
|
|
98
|
+
task = repo.find_by_id(task_id)
|
|
99
|
+
if not task:
|
|
100
|
+
if direction == "request":
|
|
101
|
+
initial_text = self._extract_initial_text(parsed_event)
|
|
102
|
+
new_task = Task(
|
|
103
|
+
id=task_id,
|
|
104
|
+
user_id=user_id or "unknown",
|
|
105
|
+
start_time=now_epoch_ms(),
|
|
106
|
+
initial_request_text=(
|
|
107
|
+
initial_text[:1024] if initial_text else None
|
|
108
|
+
), # Truncate
|
|
109
|
+
)
|
|
110
|
+
repo.save_task(new_task)
|
|
111
|
+
log.info(
|
|
112
|
+
f"{self.log_identifier} Created new task record for ID: {task_id}"
|
|
113
|
+
)
|
|
114
|
+
else:
|
|
115
|
+
# We received an event for a task we haven't seen the start of.
|
|
116
|
+
# This can happen if the logger starts mid-conversation. Create a placeholder.
|
|
117
|
+
placeholder_task = Task(
|
|
118
|
+
id=task_id,
|
|
119
|
+
user_id=user_id or "unknown",
|
|
120
|
+
start_time=now_epoch_ms(),
|
|
121
|
+
initial_request_text="[Task started before logger was active]",
|
|
122
|
+
)
|
|
123
|
+
repo.save_task(placeholder_task)
|
|
124
|
+
log.info(
|
|
125
|
+
f"{self.log_identifier} Created placeholder task record for ID: {task_id}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Create and save the event using the sanitized raw payload
|
|
129
|
+
task_event = TaskEvent(
|
|
130
|
+
id=str(uuid.uuid4()),
|
|
131
|
+
task_id=task_id,
|
|
132
|
+
user_id=user_id,
|
|
133
|
+
created_time=now_epoch_ms(),
|
|
134
|
+
topic=topic,
|
|
135
|
+
direction=direction,
|
|
136
|
+
payload=sanitized_payload,
|
|
137
|
+
)
|
|
138
|
+
repo.save_event(task_event)
|
|
139
|
+
|
|
140
|
+
# If it's a final event, update the master task record
|
|
141
|
+
final_status = self._get_final_status(parsed_event)
|
|
142
|
+
if final_status:
|
|
143
|
+
task_to_update = repo.find_by_id(task_id)
|
|
144
|
+
if task_to_update:
|
|
145
|
+
task_to_update.end_time = now_epoch_ms()
|
|
146
|
+
task_to_update.status = final_status
|
|
147
|
+
|
|
148
|
+
# Extract and store token usage if present
|
|
149
|
+
if isinstance(parsed_event, A2ATask) and parsed_event.metadata:
|
|
150
|
+
token_usage = parsed_event.metadata.get("token_usage")
|
|
151
|
+
if token_usage and isinstance(token_usage, dict):
|
|
152
|
+
task_to_update.total_input_tokens = token_usage.get("total_input_tokens")
|
|
153
|
+
task_to_update.total_output_tokens = token_usage.get("total_output_tokens")
|
|
154
|
+
task_to_update.total_cached_input_tokens = token_usage.get("total_cached_input_tokens")
|
|
155
|
+
task_to_update.token_usage_details = token_usage
|
|
156
|
+
log.info(
|
|
157
|
+
f"{self.log_identifier} Stored token usage for task {task_id}: "
|
|
158
|
+
f"input={token_usage.get('total_input_tokens')}, "
|
|
159
|
+
f"output={token_usage.get('total_output_tokens')}, "
|
|
160
|
+
f"cached={token_usage.get('total_cached_input_tokens')}"
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
repo.save_task(task_to_update)
|
|
164
|
+
log.info(
|
|
165
|
+
f"{self.log_identifier} Finalized task record for ID: {task_id} with status: {final_status}"
|
|
166
|
+
)
|
|
167
|
+
db.commit()
|
|
168
|
+
except Exception as e:
|
|
169
|
+
log.exception(
|
|
170
|
+
f"{self.log_identifier} Error logging event on topic {topic}: {e}"
|
|
171
|
+
)
|
|
172
|
+
db.rollback()
|
|
173
|
+
finally:
|
|
174
|
+
db.close()
|
|
175
|
+
|
|
176
|
+
def _parse_a2a_event(self, topic: str, payload: dict) -> Union[
|
|
177
|
+
A2ARequest,
|
|
178
|
+
A2ATask,
|
|
179
|
+
TaskStatusUpdateEvent,
|
|
180
|
+
TaskArtifactUpdateEvent,
|
|
181
|
+
JSONRPCError,
|
|
182
|
+
None,
|
|
183
|
+
]:
|
|
184
|
+
"""
|
|
185
|
+
Safely parses a raw A2A message payload into a Pydantic model.
|
|
186
|
+
Returns the parsed model or None if parsing fails or is not applicable.
|
|
187
|
+
"""
|
|
188
|
+
# Ignore discovery messages
|
|
189
|
+
if "/discovery/agentcards" in topic:
|
|
190
|
+
return None
|
|
191
|
+
|
|
192
|
+
try:
|
|
193
|
+
# Check if it's a response (has 'result' or 'error')
|
|
194
|
+
if "result" in payload or "error" in payload:
|
|
195
|
+
rpc_response = JSONRPCResponse.model_validate(payload)
|
|
196
|
+
error = a2a.get_response_error(rpc_response)
|
|
197
|
+
if error:
|
|
198
|
+
return error
|
|
199
|
+
result = a2a.get_response_result(rpc_response)
|
|
200
|
+
if result:
|
|
201
|
+
# The result is already a parsed Pydantic model
|
|
202
|
+
return result
|
|
203
|
+
# Check if it's a request
|
|
204
|
+
elif "method" in payload:
|
|
205
|
+
return A2ARequest.model_validate(payload)
|
|
206
|
+
|
|
207
|
+
log.warning(
|
|
208
|
+
f"{self.log_identifier} Payload for topic '{topic}' is not a recognizable JSON-RPC request or response. Payload: {payload}"
|
|
209
|
+
)
|
|
210
|
+
return None
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
log.error(
|
|
214
|
+
f"{self.log_identifier} Failed to parse A2A event for topic '{topic}': {e}. Payload: {payload}"
|
|
215
|
+
)
|
|
216
|
+
return None
|
|
217
|
+
|
|
218
|
+
def _infer_event_details(
|
|
219
|
+
self, topic: str, parsed_event: Any, user_props: Dict | None
|
|
220
|
+
) -> tuple[str, str | None, str | None]:
|
|
221
|
+
"""Infers direction, task_id, and user_id from a parsed A2A event."""
|
|
222
|
+
direction = "unknown"
|
|
223
|
+
task_id = None
|
|
224
|
+
# Ensure user_props is a dict, not None
|
|
225
|
+
user_props = user_props or {}
|
|
226
|
+
user_id = user_props.get("userId")
|
|
227
|
+
|
|
228
|
+
if isinstance(parsed_event, A2ARequest):
|
|
229
|
+
direction = "request"
|
|
230
|
+
task_id = a2a.get_request_id(parsed_event)
|
|
231
|
+
elif isinstance(
|
|
232
|
+
parsed_event, (A2ATask, TaskStatusUpdateEvent, TaskArtifactUpdateEvent)
|
|
233
|
+
):
|
|
234
|
+
direction = "response" if isinstance(parsed_event, A2ATask) else "status"
|
|
235
|
+
task_id = getattr(parsed_event, "task_id", None) or getattr(
|
|
236
|
+
parsed_event, "id", None
|
|
237
|
+
)
|
|
238
|
+
elif isinstance(parsed_event, JSONRPCError):
|
|
239
|
+
direction = "error"
|
|
240
|
+
if isinstance(parsed_event.data, dict):
|
|
241
|
+
task_id = parsed_event.data.get("taskId")
|
|
242
|
+
|
|
243
|
+
if not user_id:
|
|
244
|
+
user_config = user_props.get("a2aUserConfig") or user_props.get("a2a_user_config")
|
|
245
|
+
if isinstance(user_config, dict):
|
|
246
|
+
user_profile = user_config.get("user_profile", {})
|
|
247
|
+
if isinstance(user_profile, dict):
|
|
248
|
+
user_id = user_profile.get("id")
|
|
249
|
+
|
|
250
|
+
return direction, str(task_id) if task_id else None, user_id
|
|
251
|
+
|
|
252
|
+
def _extract_initial_text(self, parsed_event: Any) -> str | None:
|
|
253
|
+
"""Extracts the initial text from a send message request."""
|
|
254
|
+
try:
|
|
255
|
+
if isinstance(parsed_event, A2ARequest):
|
|
256
|
+
message = a2a.get_message_from_send_request(parsed_event)
|
|
257
|
+
if message:
|
|
258
|
+
return a2a.get_text_from_message(message)
|
|
259
|
+
except Exception:
|
|
260
|
+
return None
|
|
261
|
+
return None
|
|
262
|
+
|
|
263
|
+
def _get_final_status(self, parsed_event: Any) -> str | None:
|
|
264
|
+
"""Checks if a parsed event represents a final task status and returns the state."""
|
|
265
|
+
if isinstance(parsed_event, A2ATask):
|
|
266
|
+
return parsed_event.status.state.value
|
|
267
|
+
elif isinstance(parsed_event, JSONRPCError):
|
|
268
|
+
return "failed"
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
def _should_log_event(self, topic: str, parsed_event: Any) -> bool:
|
|
272
|
+
"""Determines if an event should be logged based on configuration."""
|
|
273
|
+
if not self.config.get("log_status_updates", True):
|
|
274
|
+
if "status" in topic:
|
|
275
|
+
return False
|
|
276
|
+
if not self.config.get("log_artifact_events", True):
|
|
277
|
+
if isinstance(parsed_event, TaskArtifactUpdateEvent):
|
|
278
|
+
return False
|
|
279
|
+
return True
|
|
280
|
+
|
|
281
|
+
def _sanitize_payload(self, payload: Dict) -> Dict:
|
|
282
|
+
"""Strips or truncates file content from payload based on configuration."""
|
|
283
|
+
new_payload = copy.deepcopy(payload)
|
|
284
|
+
|
|
285
|
+
def walk_and_sanitize(node):
|
|
286
|
+
if isinstance(node, dict):
|
|
287
|
+
for key, value in list(node.items()):
|
|
288
|
+
if key == "parts" and isinstance(value, list):
|
|
289
|
+
new_parts = []
|
|
290
|
+
for part in value:
|
|
291
|
+
if isinstance(part, dict) and "file" in part:
|
|
292
|
+
if not self.config.get("log_file_parts", True):
|
|
293
|
+
continue # Skip this part entirely
|
|
294
|
+
|
|
295
|
+
file_dict = part.get("file")
|
|
296
|
+
if isinstance(file_dict, dict) and "bytes" in file_dict:
|
|
297
|
+
max_bytes = self.config.get(
|
|
298
|
+
"max_file_part_size_bytes", 102400
|
|
299
|
+
)
|
|
300
|
+
file_bytes_b64 = file_dict.get("bytes")
|
|
301
|
+
if isinstance(file_bytes_b64, str):
|
|
302
|
+
if (len(file_bytes_b64) * 3 / 4) > max_bytes:
|
|
303
|
+
file_dict["bytes"] = (
|
|
304
|
+
f"[Content stripped, size > {max_bytes} bytes]"
|
|
305
|
+
)
|
|
306
|
+
new_parts.append(part)
|
|
307
|
+
else:
|
|
308
|
+
walk_and_sanitize(part)
|
|
309
|
+
new_parts.append(part)
|
|
310
|
+
node["parts"] = new_parts
|
|
311
|
+
else:
|
|
312
|
+
walk_and_sanitize(value)
|
|
313
|
+
elif isinstance(node, list):
|
|
314
|
+
for item in node:
|
|
315
|
+
walk_and_sanitize(item)
|
|
316
|
+
|
|
317
|
+
walk_and_sanitize(new_payload)
|
|
318
|
+
return new_payload
|
|
@@ -3,15 +3,16 @@ Service layer for handling A2A task submissions and cancellations.
|
|
|
3
3
|
Uses CoreA2AService for logic and a provided function for publishing.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
import logging
|
|
6
7
|
import threading
|
|
7
8
|
from typing import Callable, Dict, Optional
|
|
8
9
|
|
|
9
|
-
from solace_ai_connector.common.log import log
|
|
10
|
-
|
|
11
10
|
from ....common import a2a
|
|
12
11
|
from ....gateway.http_sse.sse_manager import SSEManager
|
|
13
12
|
from ....core_a2a.service import CoreA2AService
|
|
14
13
|
|
|
14
|
+
log = logging.getLogger(__name__)
|
|
15
|
+
|
|
15
16
|
PublishFunc = Callable[[str, Dict, Optional[Dict]], None]
|
|
16
17
|
|
|
17
18
|
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
Manages web user sessions and mapping to A2A Client IDs.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
import logging
|
|
5
6
|
import uuid
|
|
6
7
|
from collections.abc import Callable
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
9
|
-
from solace_ai_connector.common.log import log
|
|
10
10
|
from starlette.requests import Request
|
|
11
11
|
|
|
12
|
+
log = logging.getLogger(__name__)
|
|
12
13
|
|
|
13
14
|
SESSION_KEY_CLIENT_ID = "a2a_client_id"
|
|
14
15
|
SESSION_KEY_SESSION_ID = "a2a_session_id"
|