solace-agent-mesh 1.0.9__py3-none-any.whl → 1.3.0__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 +182 -42
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +171 -0
- solace_agent_mesh/agent/adk/callbacks.py +165 -104
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +0 -18
- solace_agent_mesh/agent/adk/models/models_llm.txt +104 -55
- solace_agent_mesh/agent/adk/runner.py +25 -17
- solace_agent_mesh/agent/adk/services.py +3 -3
- solace_agent_mesh/agent/adk/setup.py +11 -0
- solace_agent_mesh/agent/adk/stream_parser.py +8 -1
- solace_agent_mesh/agent/adk/tool_wrapper.py +10 -3
- solace_agent_mesh/agent/agent_llm.txt +355 -18
- solace_agent_mesh/agent/protocol/event_handlers.py +460 -317
- solace_agent_mesh/agent/protocol/protocol_llm.txt +54 -7
- solace_agent_mesh/agent/sac/app.py +2 -2
- solace_agent_mesh/agent/sac/component.py +211 -517
- solace_agent_mesh/agent/sac/sac_llm.txt +133 -63
- solace_agent_mesh/agent/testing/testing_llm.txt +25 -58
- solace_agent_mesh/agent/tools/peer_agent_tool.py +15 -11
- solace_agent_mesh/agent/tools/tools_llm.txt +234 -69
- solace_agent_mesh/agent/utils/artifact_helpers.py +35 -1
- solace_agent_mesh/agent/utils/utils_llm.txt +90 -105
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{75384d09.ccd480c4.js → 75384d09.bf78fbdb.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.08d30374.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +105 -0
- solace_agent_mesh/assets/docs/docs/documentation/migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html +53 -0
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/lunr-index-1757433031159.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1757433031159.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/assets/docs/sitemap.xml +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +125 -48
- solace_agent_mesh/cli/commands/eval_cmd.py +14 -0
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +53 -31
- solace_agent_mesh/cli/commands/init_cmd/database_step.py +91 -0
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +19 -8
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +80 -25
- solace_agent_mesh/cli/commands/init_cmd/web_init_step.py +32 -10
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +74 -15
- solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +0 -2
- solace_agent_mesh/cli/commands/run_cmd.py +5 -3
- solace_agent_mesh/cli/utils.py +68 -12
- solace_agent_mesh/client/webui/frontend/static/assets/authCallback-vY5eu2lI.js +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/client-BeBkzgWW.js +25 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-Bjys1KQs.js +339 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-C03yrETa.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-CE0AeXyK.js +395 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -2
- solace_agent_mesh/client/webui/frontend/static/index.html +4 -3
- solace_agent_mesh/common/a2a/__init__.py +213 -0
- solace_agent_mesh/common/a2a/a2a_llm.txt +182 -0
- solace_agent_mesh/common/a2a/artifact.py +328 -0
- solace_agent_mesh/common/a2a/events.py +183 -0
- solace_agent_mesh/common/a2a/message.py +307 -0
- solace_agent_mesh/common/a2a/protocol.py +513 -0
- solace_agent_mesh/common/a2a/task.py +127 -0
- solace_agent_mesh/common/a2a/translation.py +653 -0
- solace_agent_mesh/common/a2a/types.py +54 -0
- solace_agent_mesh/common/a2a_spec/a2a.json +2576 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +407 -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 +31 -0
- solace_agent_mesh/common/a2a_spec/schemas/llm_invocation.json +18 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +235 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_invocation_start.json +26 -0
- solace_agent_mesh/common/a2a_spec/schemas/tool_result.json +25 -0
- solace_agent_mesh/common/agent_registry.py +1 -1
- solace_agent_mesh/common/common_llm.txt +192 -70
- solace_agent_mesh/common/data_parts.py +99 -0
- solace_agent_mesh/common/middleware/middleware_llm.txt +17 -17
- solace_agent_mesh/common/sac/__init__.py +0 -0
- solace_agent_mesh/common/sac/sac_llm.txt +71 -0
- solace_agent_mesh/common/sac/sam_component_base.py +252 -0
- solace_agent_mesh/common/services/providers/providers_llm.txt +51 -84
- solace_agent_mesh/common/services/services_llm.txt +206 -26
- solace_agent_mesh/common/utils/artifact_utils.py +29 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +176 -80
- solace_agent_mesh/common/utils/embeds/resolver.py +1 -0
- solace_agent_mesh/common/utils/utils_llm.txt +323 -42
- solace_agent_mesh/config_portal/backend/common.py +2 -2
- solace_agent_mesh/config_portal/backend/plugin_catalog/constants.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-bFMKlzKf.js +98 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-d845808d.js → manifest-89db7c30.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +10 -8
- solace_agent_mesh/core_a2a/service.py +20 -44
- solace_agent_mesh/evaluation/message_organizer.py +35 -56
- solace_agent_mesh/evaluation/run.py +26 -5
- solace_agent_mesh/evaluation/subscriber.py +35 -10
- solace_agent_mesh/evaluation/summary_builder.py +27 -34
- solace_agent_mesh/gateway/base/app.py +27 -1
- solace_agent_mesh/gateway/base/base_llm.txt +177 -72
- solace_agent_mesh/gateway/base/component.py +294 -523
- solace_agent_mesh/gateway/gateway_llm.txt +299 -58
- solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +676 -0
- solace_agent_mesh/gateway/http_sse/alembic/env.py +85 -0
- solace_agent_mesh/gateway/http_sse/alembic/script.py.mako +28 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/b1c2d3e4f5g6_add_database_indexes.py +83 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/d5b3f8f2e9a0_create_initial_database.py +58 -0
- solace_agent_mesh/gateway/http_sse/alembic.ini +147 -0
- solace_agent_mesh/gateway/http_sse/api/__init__.py +11 -0
- solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +9 -0
- solace_agent_mesh/gateway/http_sse/api/controllers/session_controller.py +355 -0
- solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +279 -0
- solace_agent_mesh/gateway/http_sse/api/controllers/user_controller.py +35 -0
- solace_agent_mesh/gateway/http_sse/api/dto/__init__.py +10 -0
- solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +37 -0
- solace_agent_mesh/gateway/http_sse/api/dto/requests/session_requests.py +49 -0
- solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +66 -0
- solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +43 -0
- solace_agent_mesh/gateway/http_sse/api/dto/responses/session_responses.py +68 -0
- solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +74 -0
- solace_agent_mesh/gateway/http_sse/app.py +31 -1
- solace_agent_mesh/gateway/http_sse/application/__init__.py +3 -0
- solace_agent_mesh/gateway/http_sse/application/services/__init__.py +3 -0
- solace_agent_mesh/gateway/http_sse/application/services/session_service.py +135 -0
- solace_agent_mesh/gateway/http_sse/component.py +371 -236
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +29 -29
- solace_agent_mesh/gateway/http_sse/dependencies.py +142 -39
- solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +3 -0
- solace_agent_mesh/gateway/http_sse/domain/entities/session.py +90 -0
- solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +3 -0
- solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +54 -0
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +272 -36
- solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +4 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +3 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +123 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +4 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +16 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +119 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +31 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +12 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +3 -0
- solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +174 -0
- solace_agent_mesh/gateway/http_sse/main.py +293 -91
- solace_agent_mesh/gateway/http_sse/routers/agents.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +137 -56
- solace_agent_mesh/gateway/http_sse/routers/config.py +3 -1
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +231 -5
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +199 -171
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +7 -7
- solace_agent_mesh/gateway/http_sse/services/agent_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +89 -135
- solace_agent_mesh/gateway/http_sse/services/task_service.py +2 -5
- solace_agent_mesh/gateway/http_sse/session_manager.py +64 -30
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +9 -0
- solace_agent_mesh/gateway/http_sse/shared/auth_utils.py +29 -0
- solace_agent_mesh/gateway/http_sse/shared/enums.py +45 -0
- solace_agent_mesh/gateway/http_sse/shared/types.py +45 -0
- solace_agent_mesh/solace_agent_mesh_llm.txt +362 -0
- solace_agent_mesh/templates/gateway_component_template.py +149 -98
- solace_agent_mesh/templates/shared_config.yaml +4 -5
- solace_agent_mesh/templates/webui.yaml +8 -10
- {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/METADATA +9 -6
- {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/RECORD +197 -141
- solace_agent_mesh/assets/docs/assets/js/f284c35a.731836ad.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.3d0e7879.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.05d19492.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1757091012487.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1757091012487.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/authCallback-BmF2l6vg.js +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/client-D881Dttc.js +0 -49
- solace_agent_mesh/client/webui/frontend/static/assets/main-D0FnP_W4.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-Do32sFPX.js +0 -708
- solace_agent_mesh/common/a2a_protocol.py +0 -564
- solace_agent_mesh/common/client/__init__.py +0 -4
- solace_agent_mesh/common/client/card_resolver.py +0 -21
- solace_agent_mesh/common/client/client.py +0 -85
- solace_agent_mesh/common/client/client_llm.txt +0 -133
- solace_agent_mesh/common/server/__init__.py +0 -4
- solace_agent_mesh/common/server/server.py +0 -122
- solace_agent_mesh/common/server/server_llm.txt +0 -169
- solace_agent_mesh/common/server/task_manager.py +0 -291
- solace_agent_mesh/common/server/utils.py +0 -28
- solace_agent_mesh/common/types.py +0 -411
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-Bym6YkMd.js +0 -98
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +0 -80
- solace_agent_mesh/gateway/http_sse/routers/users.py +0 -59
- /solace_agent_mesh/assets/docs/assets/js/{main.3d0e7879.js.LICENSE.txt → main.08d30374.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.0.9.dist-info → solace_agent_mesh-1.3.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,65 +1,118 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
from fastapi
|
|
1
|
+
import os
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
import sqlalchemy as sa
|
|
7
|
+
from a2a.types import InternalError, JSONRPCError
|
|
8
|
+
from a2a.types import JSONRPCResponse as A2AJSONRPCResponse
|
|
9
|
+
from alembic import command
|
|
10
|
+
from alembic.config import Config
|
|
11
|
+
from fastapi import FastAPI, HTTPException, status
|
|
12
|
+
from fastapi import Request as FastAPIRequest
|
|
12
13
|
from fastapi.exceptions import RequestValidationError
|
|
13
14
|
from fastapi.middleware.cors import CORSMiddleware
|
|
15
|
+
from fastapi.responses import JSONResponse
|
|
16
|
+
from solace_ai_connector.common.log import log
|
|
14
17
|
from starlette.middleware.sessions import SessionMiddleware
|
|
15
18
|
from starlette.staticfiles import StaticFiles
|
|
16
|
-
import os
|
|
17
|
-
from pathlib import Path
|
|
18
|
-
import httpx
|
|
19
19
|
|
|
20
|
-
from
|
|
20
|
+
from ...common import a2a
|
|
21
|
+
from ...gateway.http_sse import dependencies
|
|
21
22
|
from ...gateway.http_sse.routers import (
|
|
22
23
|
agents,
|
|
23
|
-
tasks,
|
|
24
|
-
sse,
|
|
25
|
-
config,
|
|
26
24
|
artifacts,
|
|
27
|
-
visualization,
|
|
28
|
-
sessions,
|
|
29
|
-
people,
|
|
30
25
|
auth,
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
JSONRPCResponse as A2AJSONRPCResponse,
|
|
37
|
-
JSONRPCError,
|
|
38
|
-
InternalError,
|
|
39
|
-
InvalidRequestError,
|
|
26
|
+
config,
|
|
27
|
+
people,
|
|
28
|
+
sse,
|
|
29
|
+
tasks,
|
|
30
|
+
visualization,
|
|
40
31
|
)
|
|
41
32
|
|
|
42
|
-
|
|
33
|
+
# Import persistence-aware controllers
|
|
34
|
+
from .api.controllers.session_controller import router as session_router
|
|
35
|
+
from .api.controllers.task_controller import router as task_router
|
|
36
|
+
from .api.controllers.user_controller import router as user_router
|
|
37
|
+
from .infrastructure.persistence.database_service import DatabaseService
|
|
43
38
|
|
|
44
39
|
if TYPE_CHECKING:
|
|
45
40
|
from gateway.http_sse.component import WebUIBackendComponent
|
|
46
41
|
|
|
47
42
|
app = FastAPI(
|
|
48
43
|
title="A2A Web UI Backend",
|
|
49
|
-
version="
|
|
44
|
+
version="1.0.0", # Updated to reflect simplified architecture
|
|
50
45
|
description="Backend API and SSE server for the A2A Web UI, hosted by Solace AI Connector.",
|
|
51
46
|
)
|
|
52
47
|
|
|
53
48
|
|
|
54
|
-
def setup_dependencies(component: "WebUIBackendComponent"):
|
|
49
|
+
def setup_dependencies(component: "WebUIBackendComponent", persistence_service=None):
|
|
55
50
|
"""
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
This function initializes the modern architecture while maintaining full
|
|
52
|
+
backward compatibility with existing API contracts.
|
|
53
|
+
|
|
54
|
+
If persistence_service is None, runs in compatibility mode with in-memory sessions.
|
|
59
55
|
"""
|
|
60
|
-
|
|
56
|
+
|
|
57
|
+
if persistence_service:
|
|
58
|
+
database_url = persistence_service.engine.url.__str__()
|
|
59
|
+
global database_service
|
|
60
|
+
database_service = DatabaseService(database_url)
|
|
61
|
+
log.info("Database service initialized")
|
|
62
|
+
|
|
63
|
+
from .infrastructure.dependency_injection.container import initialize_container
|
|
64
|
+
|
|
65
|
+
initialize_container(database_url)
|
|
66
|
+
log.info("Persistence enabled - sessions will be stored in database")
|
|
67
|
+
else:
|
|
68
|
+
from .infrastructure.dependency_injection.container import initialize_container
|
|
69
|
+
|
|
70
|
+
initialize_container()
|
|
71
|
+
log.warning(
|
|
72
|
+
"No persistence service provided - using in-memory session storage (data not persisted across restarts)"
|
|
73
|
+
)
|
|
74
|
+
log.info("This maintains backward compatibility for existing SAM installations")
|
|
75
|
+
|
|
61
76
|
dependencies.set_component_instance(component)
|
|
62
77
|
|
|
78
|
+
if persistence_service:
|
|
79
|
+
log.info("Checking database migrations...")
|
|
80
|
+
try:
|
|
81
|
+
inspector = sa.inspect(persistence_service.engine)
|
|
82
|
+
existing_tables = inspector.get_table_names()
|
|
83
|
+
|
|
84
|
+
if not existing_tables or "sessions" not in existing_tables:
|
|
85
|
+
log.info("Running database migrations...")
|
|
86
|
+
alembic_cfg = Config()
|
|
87
|
+
alembic_cfg.set_main_option(
|
|
88
|
+
"script_location",
|
|
89
|
+
os.path.join(os.path.dirname(__file__), "alembic"),
|
|
90
|
+
)
|
|
91
|
+
alembic_cfg.set_main_option("sqlalchemy.url", database_url)
|
|
92
|
+
command.upgrade(alembic_cfg, "head")
|
|
93
|
+
log.info("Database migrations complete.")
|
|
94
|
+
else:
|
|
95
|
+
log.info("Database tables already exist, skipping migrations.")
|
|
96
|
+
except Exception as e:
|
|
97
|
+
log.warning(
|
|
98
|
+
f"Migration check failed, attempting to run migrations anyway: {e}"
|
|
99
|
+
)
|
|
100
|
+
try:
|
|
101
|
+
alembic_cfg = Config()
|
|
102
|
+
alembic_cfg.set_main_option(
|
|
103
|
+
"script_location",
|
|
104
|
+
os.path.join(os.path.dirname(__file__), "alembic"),
|
|
105
|
+
)
|
|
106
|
+
alembic_cfg.set_main_option("sqlalchemy.url", database_url)
|
|
107
|
+
command.upgrade(alembic_cfg, "head")
|
|
108
|
+
log.info("Database migrations complete.")
|
|
109
|
+
except Exception as migration_error:
|
|
110
|
+
log.warning(f"Migration failed but continuing: {migration_error}")
|
|
111
|
+
|
|
112
|
+
dependencies.set_persistence_service(persistence_service)
|
|
113
|
+
else:
|
|
114
|
+
log.info("Skipping database migrations - no persistence service configured")
|
|
115
|
+
|
|
63
116
|
webui_app = component.get_app()
|
|
64
117
|
app_config = {}
|
|
65
118
|
if webui_app:
|
|
@@ -84,6 +137,7 @@ def setup_dependencies(component: "WebUIBackendComponent"):
|
|
|
84
137
|
"frontend_redirect_url": app_config.get(
|
|
85
138
|
"frontend_redirect_url", "http://localhost:3000"
|
|
86
139
|
),
|
|
140
|
+
"persistence_enabled": persistence_service is not None,
|
|
87
141
|
}
|
|
88
142
|
|
|
89
143
|
dependencies.set_api_config(api_config_dict)
|
|
@@ -215,17 +269,80 @@ def setup_dependencies(component: "WebUIBackendComponent"):
|
|
|
215
269
|
return
|
|
216
270
|
|
|
217
271
|
user_info = userinfo_response.json()
|
|
218
|
-
|
|
272
|
+
log.info(
|
|
273
|
+
"AuthMiddleware: Raw user info from OAuth provider: %s",
|
|
274
|
+
user_info,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
# Priority order for user identifier (most specific to least specific)
|
|
278
|
+
user_identifier = (
|
|
279
|
+
user_info.get("sub") # Standard OIDC subject claim
|
|
280
|
+
or user_info.get("client_id") # Mini IDP and some custom IDPs
|
|
281
|
+
or user_info.get("username") # Mini IDP returns username field
|
|
282
|
+
or user_info.get("oid") # Azure AD object ID
|
|
283
|
+
or user_info.get(
|
|
284
|
+
"preferred_username"
|
|
285
|
+
) # Common in enterprise IDPs
|
|
286
|
+
or user_info.get("upn") # Azure AD User Principal Name
|
|
287
|
+
or user_info.get("unique_name") # Some Azure configurations
|
|
288
|
+
or user_info.get("email") # Fallback to email
|
|
289
|
+
or user_info.get("name") # Last resort
|
|
290
|
+
or user_info.get("azp") # Authorized party (rare but possible)
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# IMPORTANT: If the extracted identifier is "Unknown", it means the IDP
|
|
294
|
+
# didn't properly authenticate or is misconfigured. Use a fallback.
|
|
295
|
+
if user_identifier and user_identifier.lower() == "unknown":
|
|
296
|
+
log.warning(
|
|
297
|
+
"AuthMiddleware: IDP returned 'Unknown' as user identifier. This indicates misconfiguration. Using fallback."
|
|
298
|
+
)
|
|
299
|
+
# In development mode with mini IDP, default to sam_dev_user
|
|
300
|
+
# This is a workaround for the OAuth2 proxy service returning "Unknown"
|
|
301
|
+
user_identifier = "sam_dev_user" # Fallback for development
|
|
302
|
+
log.info(
|
|
303
|
+
"AuthMiddleware: Using development fallback user: sam_dev_user"
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Extract email separately (may be different from user identifier)
|
|
307
|
+
email_from_auth = (
|
|
308
|
+
user_info.get("email")
|
|
309
|
+
or user_info.get("preferred_username")
|
|
310
|
+
or user_info.get("upn")
|
|
311
|
+
or user_identifier
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
# Extract display name
|
|
315
|
+
display_name = (
|
|
316
|
+
user_info.get("name")
|
|
317
|
+
or user_info.get("given_name", "")
|
|
318
|
+
+ " "
|
|
319
|
+
+ user_info.get("family_name", "")
|
|
320
|
+
or user_info.get("preferred_username")
|
|
321
|
+
or user_identifier
|
|
322
|
+
).strip()
|
|
323
|
+
|
|
324
|
+
log.info(
|
|
325
|
+
"AuthMiddleware: Extracted user identifier: %s, email: %s, name: %s",
|
|
326
|
+
user_identifier,
|
|
327
|
+
email_from_auth,
|
|
328
|
+
display_name,
|
|
329
|
+
)
|
|
219
330
|
|
|
220
|
-
if not
|
|
331
|
+
if not user_identifier or user_identifier.lower() in [
|
|
332
|
+
"null",
|
|
333
|
+
"none",
|
|
334
|
+
"",
|
|
335
|
+
]:
|
|
221
336
|
log.error(
|
|
222
|
-
"AuthMiddleware:
|
|
337
|
+
"AuthMiddleware: No valid user identifier from OAuth provider. Full user info: %s. Expected valid user identifier.",
|
|
338
|
+
user_info,
|
|
223
339
|
)
|
|
224
340
|
response = JSONResponse(
|
|
225
341
|
status_code=status.HTTP_401_UNAUTHORIZED,
|
|
226
342
|
content={
|
|
227
|
-
"detail": "
|
|
228
|
-
"error_type": "
|
|
343
|
+
"detail": "OAuth provider returned no valid user identifier. Provider must return at least one of: sub, username, client_id, preferred_username, email, or name field.",
|
|
344
|
+
"error_type": "invalid_user_identifier_from_provider",
|
|
345
|
+
"received_user_info": user_info,
|
|
229
346
|
},
|
|
230
347
|
)
|
|
231
348
|
await response(scope, receive, send)
|
|
@@ -233,24 +350,51 @@ def setup_dependencies(component: "WebUIBackendComponent"):
|
|
|
233
350
|
|
|
234
351
|
identity_service = self.component.identity_service
|
|
235
352
|
if not identity_service:
|
|
353
|
+
# Make absolutely sure we have a valid user ID - never "Unknown"
|
|
354
|
+
final_user_id = (
|
|
355
|
+
user_identifier or email_from_auth or "sam_dev_user"
|
|
356
|
+
)
|
|
357
|
+
if not final_user_id or final_user_id.lower() in [
|
|
358
|
+
"unknown",
|
|
359
|
+
"null",
|
|
360
|
+
"none",
|
|
361
|
+
"",
|
|
362
|
+
]:
|
|
363
|
+
final_user_id = "sam_dev_user"
|
|
364
|
+
log.warning(
|
|
365
|
+
"AuthMiddleware: Had to use fallback user ID due to invalid identifier: %s",
|
|
366
|
+
user_identifier,
|
|
367
|
+
)
|
|
368
|
+
|
|
236
369
|
log.error(
|
|
237
|
-
"AuthMiddleware: Internal IdentityService not configured on component.
|
|
370
|
+
"AuthMiddleware: Internal IdentityService not configured on component. Using user ID: %s",
|
|
371
|
+
final_user_id,
|
|
238
372
|
)
|
|
239
373
|
request.state.user = {
|
|
240
|
-
"id":
|
|
241
|
-
"email": email_from_auth,
|
|
242
|
-
"name":
|
|
374
|
+
"id": final_user_id,
|
|
375
|
+
"email": email_from_auth or final_user_id,
|
|
376
|
+
"name": display_name or final_user_id,
|
|
243
377
|
"authenticated": True,
|
|
244
378
|
"auth_method": "oidc",
|
|
245
379
|
}
|
|
380
|
+
log.info(
|
|
381
|
+
"AuthMiddleware: Set fallback user state with id: %s",
|
|
382
|
+
final_user_id,
|
|
383
|
+
)
|
|
246
384
|
else:
|
|
385
|
+
# Try to look up user profile using the email or user identifier
|
|
386
|
+
lookup_value = (
|
|
387
|
+
email_from_auth
|
|
388
|
+
if "@" in email_from_auth
|
|
389
|
+
else user_identifier
|
|
390
|
+
)
|
|
247
391
|
user_profile = await identity_service.get_user_profile(
|
|
248
|
-
{identity_service.lookup_key:
|
|
392
|
+
{identity_service.lookup_key: lookup_value}
|
|
249
393
|
)
|
|
250
394
|
if not user_profile:
|
|
251
395
|
log.error(
|
|
252
396
|
"AuthMiddleware: User '%s' authenticated but not found in internal IdentityService.",
|
|
253
|
-
|
|
397
|
+
lookup_value,
|
|
254
398
|
)
|
|
255
399
|
response = JSONResponse(
|
|
256
400
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
@@ -263,10 +407,18 @@ def setup_dependencies(component: "WebUIBackendComponent"):
|
|
|
263
407
|
return
|
|
264
408
|
|
|
265
409
|
request.state.user = user_profile.copy()
|
|
410
|
+
# Ensure the ID is set from the OAuth provider if not present in the profile
|
|
411
|
+
if not request.state.user.get("id"):
|
|
412
|
+
request.state.user["id"] = user_identifier
|
|
413
|
+
# Also ensure email and name are set if not in profile
|
|
414
|
+
if not request.state.user.get("email"):
|
|
415
|
+
request.state.user["email"] = email_from_auth
|
|
416
|
+
if not request.state.user.get("name"):
|
|
417
|
+
request.state.user["name"] = display_name
|
|
266
418
|
request.state.user["authenticated"] = True
|
|
267
419
|
request.state.user["auth_method"] = "oidc"
|
|
268
|
-
log.
|
|
269
|
-
"AuthMiddleware:
|
|
420
|
+
log.info(
|
|
421
|
+
"AuthMiddleware: Set enriched user profile with id: %s",
|
|
270
422
|
request.state.user.get("id"),
|
|
271
423
|
)
|
|
272
424
|
|
|
@@ -290,9 +442,22 @@ def setup_dependencies(component: "WebUIBackendComponent"):
|
|
|
290
442
|
)
|
|
291
443
|
await response(scope, receive, send)
|
|
292
444
|
return
|
|
445
|
+
else:
|
|
446
|
+
# If auth is not used, set a default user
|
|
447
|
+
request.state.user = {
|
|
448
|
+
"id": "sam_dev_user",
|
|
449
|
+
"name": "Sam Dev User",
|
|
450
|
+
"email": "sam@dev.local",
|
|
451
|
+
"authenticated": True,
|
|
452
|
+
"auth_method": "development",
|
|
453
|
+
}
|
|
454
|
+
log.debug(
|
|
455
|
+
"AuthMiddleware: Set development user state with id: sam_dev_user"
|
|
456
|
+
)
|
|
293
457
|
|
|
294
458
|
await self.app(scope, receive, send)
|
|
295
459
|
|
|
460
|
+
# Add middleware
|
|
296
461
|
allowed_origins = component.get_cors_origins()
|
|
297
462
|
app.add_middleware(
|
|
298
463
|
CORSMiddleware,
|
|
@@ -310,10 +475,30 @@ def setup_dependencies(component: "WebUIBackendComponent"):
|
|
|
310
475
|
app.add_middleware(AuthMiddleware, component=component)
|
|
311
476
|
log.info("AuthMiddleware added.")
|
|
312
477
|
|
|
478
|
+
# Mount API routers
|
|
313
479
|
api_prefix = "/api/v1"
|
|
480
|
+
|
|
481
|
+
# Mount persistence-aware controllers (your original controllers with full functionality)
|
|
482
|
+
# These provide the complete API surface with database persistence
|
|
483
|
+
app.include_router(
|
|
484
|
+
session_router, prefix=api_prefix, tags=["Sessions"]
|
|
485
|
+
) # Provides /api/v1/sessions/* endpoints
|
|
486
|
+
app.include_router(
|
|
487
|
+
user_router, prefix=f"{api_prefix}/users", tags=["Users"]
|
|
488
|
+
) # Provides /api/v1/users/me
|
|
489
|
+
app.include_router(
|
|
490
|
+
task_router, prefix=f"{api_prefix}/tasks", tags=["Tasks"]
|
|
491
|
+
) # Provides /api/v1/tasks/send, /subscribe, /cancel
|
|
492
|
+
|
|
493
|
+
# Mount new A2A SDK routers with different paths to avoid conflicts
|
|
314
494
|
app.include_router(config.router, prefix=api_prefix, tags=["Config"])
|
|
315
495
|
app.include_router(agents.router, prefix=api_prefix, tags=["Agents"])
|
|
316
|
-
|
|
496
|
+
# New A2A message endpoints (non-conflicting paths)
|
|
497
|
+
app.include_router(
|
|
498
|
+
tasks.router, prefix=api_prefix, tags=["A2A Messages"]
|
|
499
|
+
) # Provides /api/v1/message:send, /message:stream
|
|
500
|
+
# Note: We only use the full-featured session_router (from api/controllers/session_controller.py)
|
|
501
|
+
# which provides complete session management with database persistence
|
|
317
502
|
app.include_router(sse.router, prefix=f"{api_prefix}/sse", tags=["SSE"])
|
|
318
503
|
app.include_router(
|
|
319
504
|
artifacts.router, prefix=f"{api_prefix}/artifacts", tags=["Artifacts"]
|
|
@@ -323,18 +508,15 @@ def setup_dependencies(component: "WebUIBackendComponent"):
|
|
|
323
508
|
prefix=f"{api_prefix}/visualization",
|
|
324
509
|
tags=["Visualization"],
|
|
325
510
|
)
|
|
326
|
-
app.include_router(
|
|
327
|
-
sessions.router, prefix=f"{api_prefix}/sessions", tags=["Sessions"]
|
|
328
|
-
)
|
|
329
511
|
app.include_router(
|
|
330
512
|
people.router,
|
|
331
513
|
prefix=api_prefix,
|
|
332
514
|
tags=["People"],
|
|
333
515
|
)
|
|
334
516
|
app.include_router(auth.router, prefix=api_prefix, tags=["Auth"])
|
|
335
|
-
|
|
336
|
-
log.info("API routers mounted under prefix: %s", api_prefix)
|
|
517
|
+
log.info("Legacy routers mounted for endpoints not yet migrated")
|
|
337
518
|
|
|
519
|
+
# Mount static files
|
|
338
520
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
339
521
|
root_dir = Path(os.path.normpath(os.path.join(current_dir, "..", "..")))
|
|
340
522
|
static_files_dir = Path.joinpath(root_dir, "client", "webui", "frontend", "static")
|
|
@@ -359,7 +541,10 @@ def setup_dependencies(component: "WebUIBackendComponent"):
|
|
|
359
541
|
|
|
360
542
|
@app.exception_handler(HTTPException)
|
|
361
543
|
async def http_exception_handler(request: FastAPIRequest, exc: HTTPException):
|
|
362
|
-
"""
|
|
544
|
+
"""
|
|
545
|
+
HTTP exception handler with automatic format detection.
|
|
546
|
+
Returns JSON-RPC format for tasks/SSE endpoints, REST format for others.
|
|
547
|
+
"""
|
|
363
548
|
log.warning(
|
|
364
549
|
"HTTP Exception: Status=%s, Detail=%s, Request: %s %s",
|
|
365
550
|
exc.status_code,
|
|
@@ -367,47 +552,67 @@ async def http_exception_handler(request: FastAPIRequest, exc: HTTPException):
|
|
|
367
552
|
request.method,
|
|
368
553
|
request.url,
|
|
369
554
|
)
|
|
370
|
-
error_data = None
|
|
371
|
-
error_code = InternalError().code
|
|
372
|
-
error_message = str(exc.detail)
|
|
373
|
-
|
|
374
|
-
if isinstance(exc.detail, dict):
|
|
375
|
-
if "code" in exc.detail and "message" in exc.detail:
|
|
376
|
-
error_code = exc.detail["code"]
|
|
377
|
-
error_message = exc.detail["message"]
|
|
378
|
-
error_data = exc.detail.get("data")
|
|
379
|
-
else:
|
|
380
|
-
error_data = exc.detail
|
|
381
555
|
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
556
|
+
# Check if this is a JSON-RPC endpoint (tasks and SSE endpoints use JSON-RPC)
|
|
557
|
+
is_jsonrpc_endpoint = request.url.path.startswith(
|
|
558
|
+
"/api/v1/tasks"
|
|
559
|
+
) or request.url.path.startswith("/api/v1/sse")
|
|
560
|
+
|
|
561
|
+
if is_jsonrpc_endpoint:
|
|
562
|
+
# Use JSON-RPC format for tasks and SSE endpoints
|
|
563
|
+
error_data = None
|
|
564
|
+
error_code = InternalError().code
|
|
565
|
+
error_message = str(exc.detail)
|
|
566
|
+
|
|
567
|
+
if isinstance(exc.detail, dict):
|
|
568
|
+
if "code" in exc.detail and "message" in exc.detail:
|
|
569
|
+
error_code = exc.detail["code"]
|
|
570
|
+
error_message = exc.detail["message"]
|
|
571
|
+
error_data = exc.detail.get("data")
|
|
572
|
+
else:
|
|
573
|
+
error_data = exc.detail
|
|
574
|
+
elif isinstance(exc.detail, str):
|
|
575
|
+
if exc.status_code == status.HTTP_400_BAD_REQUEST:
|
|
576
|
+
error_code = -32600
|
|
577
|
+
elif exc.status_code == status.HTTP_404_NOT_FOUND:
|
|
578
|
+
error_code = -32601
|
|
579
|
+
error_message = "Resource not found"
|
|
580
|
+
|
|
581
|
+
error_obj = JSONRPCError(
|
|
582
|
+
code=error_code, message=error_message, data=error_data
|
|
583
|
+
)
|
|
584
|
+
response = A2AJSONRPCResponse(error=error_obj)
|
|
585
|
+
return JSONResponse(
|
|
586
|
+
status_code=exc.status_code, content=response.model_dump(exclude_none=True)
|
|
587
|
+
)
|
|
588
|
+
else:
|
|
589
|
+
# Use standard REST format for sessions and other REST endpoints
|
|
590
|
+
if isinstance(exc.detail, dict):
|
|
591
|
+
error_response = exc.detail
|
|
592
|
+
elif isinstance(exc.detail, str):
|
|
593
|
+
error_response = {"detail": exc.detail}
|
|
594
|
+
else:
|
|
595
|
+
error_response = {"detail": str(exc.detail)}
|
|
388
596
|
|
|
389
|
-
|
|
390
|
-
response = A2AJSONRPCResponse(error=error_obj)
|
|
391
|
-
return JSONResponse(
|
|
392
|
-
status_code=exc.status_code, content=response.model_dump(exclude_none=True)
|
|
393
|
-
)
|
|
597
|
+
return JSONResponse(status_code=exc.status_code, content=error_response)
|
|
394
598
|
|
|
395
599
|
|
|
396
600
|
@app.exception_handler(RequestValidationError)
|
|
397
601
|
async def validation_exception_handler(
|
|
398
602
|
request: FastAPIRequest, exc: RequestValidationError
|
|
399
603
|
):
|
|
400
|
-
"""
|
|
604
|
+
"""
|
|
605
|
+
Handles Pydantic validation errors with format detection.
|
|
606
|
+
"""
|
|
401
607
|
log.warning(
|
|
402
608
|
"Request Validation Error: %s, Request: %s %s",
|
|
403
609
|
exc.errors(),
|
|
404
610
|
request.method,
|
|
405
611
|
request.url,
|
|
406
612
|
)
|
|
407
|
-
|
|
408
|
-
message="Invalid request parameters", data=exc.errors()
|
|
613
|
+
response = a2a.create_invalid_request_error_response(
|
|
614
|
+
message="Invalid request parameters", data=exc.errors(), request_id=None
|
|
409
615
|
)
|
|
410
|
-
response = A2AJSONRPCResponse(error=error_obj)
|
|
411
616
|
return JSONResponse(
|
|
412
617
|
status_code=status.HTTP_400_BAD_REQUEST,
|
|
413
618
|
content=response.model_dump(exclude_none=True),
|
|
@@ -416,14 +621,16 @@ async def validation_exception_handler(
|
|
|
416
621
|
|
|
417
622
|
@app.exception_handler(Exception)
|
|
418
623
|
async def generic_exception_handler(request: FastAPIRequest, exc: Exception):
|
|
419
|
-
"""
|
|
624
|
+
"""
|
|
625
|
+
Handles any other unexpected exceptions with format detection.
|
|
626
|
+
"""
|
|
420
627
|
log.exception(
|
|
421
628
|
"Unhandled Exception: %s, Request: %s %s", exc, request.method, request.url
|
|
422
629
|
)
|
|
423
|
-
error_obj =
|
|
630
|
+
error_obj = a2a.create_internal_error(
|
|
424
631
|
message="An unexpected server error occurred: %s" % type(exc).__name__
|
|
425
632
|
)
|
|
426
|
-
response =
|
|
633
|
+
response = a2a.create_error_response(error=error_obj, request_id=None)
|
|
427
634
|
return JSONResponse(
|
|
428
635
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
429
636
|
content=response.model_dump(exclude_none=True),
|
|
@@ -435,8 +642,3 @@ async def read_root():
|
|
|
435
642
|
"""Basic health check endpoint."""
|
|
436
643
|
log.debug("Health check endpoint '/health' called")
|
|
437
644
|
return {"status": "A2A Web UI Backend is running"}
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
log.info(
|
|
441
|
-
"FastAPI application instance created (endpoints/middleware/static files setup deferred until component startup)."
|
|
442
|
-
)
|
|
@@ -8,7 +8,7 @@ from typing import List
|
|
|
8
8
|
from solace_ai_connector.common.log import log
|
|
9
9
|
|
|
10
10
|
from ....common.agent_registry import AgentRegistry
|
|
11
|
-
from
|
|
11
|
+
from a2a.types import AgentCard
|
|
12
12
|
from ....gateway.http_sse.dependencies import get_agent_registry
|
|
13
13
|
|
|
14
14
|
router = APIRouter()
|