solace-agent-mesh 1.3.0__py3-none-any.whl → 1.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of solace-agent-mesh might be problematic. Click here for more details.

Files changed (145) hide show
  1. solace_agent_mesh/agent/adk/setup.py +141 -34
  2. solace_agent_mesh/agent/protocol/event_handlers.py +91 -0
  3. solace_agent_mesh/agent/sac/app.py +3 -2
  4. solace_agent_mesh/agent/tools/__init__.py +1 -0
  5. solace_agent_mesh/agent/tools/dynamic_tool.py +362 -0
  6. solace_agent_mesh/assets/docs/404.html +3 -3
  7. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.d97b8e94.js +1 -0
  8. solace_agent_mesh/assets/docs/assets/js/483cef9a.03d5dceb.js +1 -0
  9. solace_agent_mesh/assets/docs/assets/js/55f47984.cf3781c4.js +1 -0
  10. solace_agent_mesh/assets/docs/assets/js/664b740a.1b744a32.js +1 -0
  11. solace_agent_mesh/assets/docs/assets/js/75384d09.c193a8f0.js +1 -0
  12. solace_agent_mesh/assets/docs/assets/js/9a09e75d.d6607c56.js +1 -0
  13. solace_agent_mesh/assets/docs/assets/js/aba87c2f.071e2d94.js +1 -0
  14. solace_agent_mesh/assets/docs/assets/js/ae0e903d.4d8dda10.js +1 -0
  15. solace_agent_mesh/assets/docs/assets/js/c835a94d.146e3186.js +1 -0
  16. solace_agent_mesh/assets/docs/assets/js/f284c35a.7334119c.js +1 -0
  17. solace_agent_mesh/assets/docs/assets/js/main.4adc477a.js +2 -0
  18. solace_agent_mesh/assets/docs/assets/js/runtime~main.cf0229ea.js +1 -0
  19. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +29 -0
  20. solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +25 -0
  21. solace_agent_mesh/assets/docs/docs/documentation/{migration-guides/a2a-upgrade-to-0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html → Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html } +6 -6
  22. solace_agent_mesh/assets/docs/docs/documentation/{migration-guides/a2a-upgrade-to-0.3.0/a2a-technical-migration-map/index.html → Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html } +6 -6
  23. solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +19 -27
  24. solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
  25. solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
  26. solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
  27. solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
  28. solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
  29. solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
  30. solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
  31. solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
  32. solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
  33. solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +4 -4
  34. solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
  35. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
  36. solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
  37. solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
  38. solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +4 -4
  39. solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +4 -4
  43. solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
  44. solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
  45. solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
  46. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
  47. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
  50. solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
  51. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +5 -4
  52. solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
  53. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +63 -0
  54. solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
  55. solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
  56. solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
  57. solace_agent_mesh/assets/docs/lunr-index-1757704179464.json +1 -0
  58. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  59. solace_agent_mesh/assets/docs/search-doc-1757704179464.json +1 -0
  60. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  61. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  62. solace_agent_mesh/cli/__init__.py +1 -1
  63. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-vY5eu2lI.js → authCallback-CAX9u8a7.js} +1 -1
  64. solace_agent_mesh/client/webui/frontend/static/assets/{client-BeBkzgWW.js → client-DXU9SPI5.js} +1 -1
  65. solace_agent_mesh/client/webui/frontend/static/assets/{main-Bjys1KQs.js → main-DjoMeldu.js} +26 -26
  66. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-CE0AeXyK.js → vendor-B0BEKoAR.js} +69 -74
  67. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  68. solace_agent_mesh/client/webui/frontend/static/index.html +3 -3
  69. solace_agent_mesh/common/a2a/__init__.py +4 -0
  70. solace_agent_mesh/common/a2a/protocol.py +20 -0
  71. solace_agent_mesh/common/sac/sam_component_base.py +29 -9
  72. solace_agent_mesh/common/sam_events/__init__.py +9 -0
  73. solace_agent_mesh/common/sam_events/event_service.py +207 -0
  74. solace_agent_mesh/gateway/http_sse/alembic/env.py +1 -1
  75. solace_agent_mesh/gateway/http_sse/component.py +45 -35
  76. solace_agent_mesh/gateway/http_sse/dependencies.py +129 -66
  77. solace_agent_mesh/gateway/http_sse/main.py +22 -35
  78. solace_agent_mesh/gateway/http_sse/repository/__init__.py +37 -0
  79. solace_agent_mesh/gateway/http_sse/repository/entities/__init__.py +9 -0
  80. solace_agent_mesh/gateway/http_sse/repository/entities/message.py +41 -0
  81. solace_agent_mesh/gateway/http_sse/repository/entities/session.py +45 -0
  82. solace_agent_mesh/gateway/http_sse/repository/entities/session_history.py +16 -0
  83. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +64 -0
  84. solace_agent_mesh/gateway/http_sse/repository/message_repository.py +78 -0
  85. solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -0
  86. solace_agent_mesh/gateway/http_sse/repository/models/base.py +7 -0
  87. solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +27 -0
  88. solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +27 -0
  89. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +139 -0
  90. solace_agent_mesh/gateway/http_sse/routers/{agents.py → agent_cards.py} +7 -7
  91. solace_agent_mesh/gateway/http_sse/routers/config.py +1 -0
  92. solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +20 -0
  93. solace_agent_mesh/gateway/http_sse/{api → routers}/dto/requests/session_requests.py +1 -8
  94. solace_agent_mesh/gateway/http_sse/routers/dto/responses/__init__.py +16 -0
  95. solace_agent_mesh/gateway/http_sse/{api → routers}/dto/responses/session_responses.py +3 -30
  96. solace_agent_mesh/gateway/http_sse/{api/controllers/session_controller.py → routers/sessions.py} +20 -77
  97. solace_agent_mesh/gateway/http_sse/routers/tasks.py +42 -49
  98. solace_agent_mesh/gateway/http_sse/{api/controllers/user_controller.py → routers/users.py} +1 -1
  99. solace_agent_mesh/gateway/http_sse/services/{agent_service.py → agent_card_service.py} +19 -19
  100. solace_agent_mesh/gateway/http_sse/services/session_service.py +245 -0
  101. solace_agent_mesh/gateway/http_sse/session_manager.py +0 -3
  102. solace_agent_mesh/gateway/http_sse/shared/enums.py +0 -5
  103. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/METADATA +1 -1
  104. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/RECORD +109 -110
  105. solace_agent_mesh/assets/docs/assets/js/42b3f8d8.8ccb9901.js +0 -1
  106. solace_agent_mesh/assets/docs/assets/js/55f47984.c484bf96.js +0 -1
  107. solace_agent_mesh/assets/docs/assets/js/6e0db977.39a79ca9.js +0 -1
  108. solace_agent_mesh/assets/docs/assets/js/75384d09.bf78fbdb.js +0 -1
  109. solace_agent_mesh/assets/docs/assets/js/90dd9cf6.88f385ea.js +0 -1
  110. solace_agent_mesh/assets/docs/assets/js/aba87c2f.76376d7c.js +0 -1
  111. solace_agent_mesh/assets/docs/assets/js/f284c35a.fb68323a.js +0 -1
  112. solace_agent_mesh/assets/docs/assets/js/main.08d30374.js +0 -2
  113. solace_agent_mesh/assets/docs/assets/js/runtime~main.458efb1d.js +0 -1
  114. solace_agent_mesh/assets/docs/lunr-index-1757433031159.json +0 -1
  115. solace_agent_mesh/assets/docs/search-doc-1757433031159.json +0 -1
  116. solace_agent_mesh/gateway/http_sse/ARCHITECTURE_GUIDE.md +0 -676
  117. solace_agent_mesh/gateway/http_sse/api/__init__.py +0 -11
  118. solace_agent_mesh/gateway/http_sse/api/controllers/__init__.py +0 -9
  119. solace_agent_mesh/gateway/http_sse/api/controllers/task_controller.py +0 -279
  120. solace_agent_mesh/gateway/http_sse/api/dto/requests/__init__.py +0 -37
  121. solace_agent_mesh/gateway/http_sse/api/dto/requests/task_requests.py +0 -66
  122. solace_agent_mesh/gateway/http_sse/api/dto/responses/__init__.py +0 -43
  123. solace_agent_mesh/gateway/http_sse/api/dto/responses/task_responses.py +0 -74
  124. solace_agent_mesh/gateway/http_sse/application/__init__.py +0 -3
  125. solace_agent_mesh/gateway/http_sse/application/services/__init__.py +0 -3
  126. solace_agent_mesh/gateway/http_sse/application/services/session_service.py +0 -135
  127. solace_agent_mesh/gateway/http_sse/domain/entities/__init__.py +0 -3
  128. solace_agent_mesh/gateway/http_sse/domain/entities/session.py +0 -90
  129. solace_agent_mesh/gateway/http_sse/domain/repositories/__init__.py +0 -3
  130. solace_agent_mesh/gateway/http_sse/domain/repositories/session_repository.py +0 -54
  131. solace_agent_mesh/gateway/http_sse/infrastructure/__init__.py +0 -4
  132. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/__init__.py +0 -3
  133. solace_agent_mesh/gateway/http_sse/infrastructure/dependency_injection/container.py +0 -123
  134. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/__init__.py +0 -4
  135. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_persistence_service.py +0 -16
  136. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/database_service.py +0 -119
  137. solace_agent_mesh/gateway/http_sse/infrastructure/persistence/models.py +0 -31
  138. solace_agent_mesh/gateway/http_sse/infrastructure/persistence_service.py +0 -12
  139. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/__init__.py +0 -3
  140. solace_agent_mesh/gateway/http_sse/infrastructure/repositories/session_repository.py +0 -174
  141. /solace_agent_mesh/assets/docs/assets/js/{main.08d30374.js.LICENSE.txt → main.4adc477a.js.LICENSE.txt} +0 -0
  142. /solace_agent_mesh/gateway/http_sse/{api → routers}/dto/__init__.py +0 -0
  143. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/WHEEL +0 -0
  144. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/entry_points.txt +0 -0
  145. {solace_agent_mesh-1.3.0.dist-info → solace_agent_mesh-1.3.2.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,9 @@
1
+ """
2
+ SQLAlchemy models for database persistence.
3
+ """
4
+
5
+ from .base import Base
6
+ from .message_model import MessageModel
7
+ from .session_model import SessionModel
8
+
9
+ __all__ = ["Base", "MessageModel", "SessionModel"]
@@ -0,0 +1,7 @@
1
+ """
2
+ SQLAlchemy base and shared database configuration.
3
+ """
4
+
5
+ from sqlalchemy.orm import declarative_base
6
+
7
+ Base = declarative_base()
@@ -0,0 +1,27 @@
1
+ """
2
+ Message SQLAlchemy model.
3
+ """
4
+
5
+ from sqlalchemy import Column, DateTime, ForeignKey, String, Text
6
+ from sqlalchemy.orm import relationship
7
+ from sqlalchemy.sql import func
8
+
9
+ from .base import Base
10
+
11
+
12
+ class MessageModel(Base):
13
+ """SQLAlchemy model for messages."""
14
+
15
+ __tablename__ = "chat_messages"
16
+
17
+ id = Column(String, primary_key=True)
18
+ session_id = Column(
19
+ String, ForeignKey("sessions.id", ondelete="CASCADE"), nullable=False
20
+ )
21
+ message = Column(Text, nullable=False)
22
+ created_at = Column(DateTime, default=func.now())
23
+ sender_type = Column(String(50))
24
+ sender_name = Column(String(255))
25
+
26
+ # Relationship to session
27
+ session = relationship("SessionModel", back_populates="messages")
@@ -0,0 +1,27 @@
1
+ """
2
+ Session SQLAlchemy model.
3
+ """
4
+
5
+ from sqlalchemy import Column, DateTime, String
6
+ from sqlalchemy.orm import relationship
7
+ from sqlalchemy.sql import func
8
+
9
+ from .base import Base
10
+
11
+
12
+ class SessionModel(Base):
13
+ """SQLAlchemy model for sessions."""
14
+
15
+ __tablename__ = "sessions"
16
+
17
+ id = Column(String, primary_key=True)
18
+ name = Column(String, nullable=True)
19
+ user_id = Column(String, nullable=False)
20
+ agent_id = Column(String, nullable=True)
21
+ created_at = Column(DateTime, default=func.now())
22
+ updated_at = Column(DateTime, default=func.now(), onupdate=func.now())
23
+
24
+ # Relationship to messages
25
+ messages = relationship(
26
+ "MessageModel", back_populates="session", cascade="all, delete-orphan"
27
+ )
@@ -0,0 +1,139 @@
1
+ """
2
+ Session repository implementation using SQLAlchemy.
3
+ """
4
+
5
+ from sqlalchemy.orm import Session as DBSession
6
+
7
+ from ..shared.types import PaginationInfo, SessionId, UserId
8
+ from .entities import Message, Session
9
+ from .interfaces import ISessionRepository
10
+ from .models import MessageModel, SessionModel
11
+
12
+
13
+ class SessionRepository(ISessionRepository):
14
+ """SQLAlchemy implementation of session repository."""
15
+
16
+ def __init__(self, db: DBSession):
17
+ self.db = db
18
+
19
+ def find_by_user(
20
+ self, user_id: UserId, pagination: PaginationInfo | None = None
21
+ ) -> list[Session]:
22
+ """Find all sessions for a specific user."""
23
+ query = self.db.query(SessionModel).filter(SessionModel.user_id == user_id)
24
+
25
+ if pagination:
26
+ offset = (pagination.page - 1) * pagination.page_size
27
+ query = query.offset(offset).limit(pagination.page_size)
28
+
29
+ models = query.order_by(SessionModel.updated_at.desc()).all()
30
+ return [self._model_to_entity(model) for model in models]
31
+
32
+ def find_user_session(
33
+ self, session_id: SessionId, user_id: UserId
34
+ ) -> Session | None:
35
+ """Find a specific session belonging to a user."""
36
+ model = (
37
+ self.db.query(SessionModel)
38
+ .filter(
39
+ SessionModel.id == session_id,
40
+ SessionModel.user_id == user_id,
41
+ )
42
+ .first()
43
+ )
44
+ return self._model_to_entity(model) if model else None
45
+
46
+ def save(self, session: Session) -> Session:
47
+ """Save or update a session."""
48
+ model = self.db.query(SessionModel).filter(SessionModel.id == session.id).first()
49
+
50
+ if model:
51
+ # Update existing
52
+ model.name = session.name
53
+ model.agent_id = session.agent_id
54
+ model.updated_at = session.updated_at
55
+ else:
56
+ # Create new
57
+ model = SessionModel(
58
+ id=session.id,
59
+ name=session.name,
60
+ user_id=session.user_id,
61
+ agent_id=session.agent_id,
62
+ created_at=session.created_at,
63
+ updated_at=session.updated_at,
64
+ )
65
+ self.db.add(model)
66
+
67
+ self.db.commit()
68
+ self.db.refresh(model)
69
+ return self._model_to_entity(model)
70
+
71
+ def delete(self, session_id: SessionId, user_id: UserId) -> bool:
72
+ """Delete a session belonging to a user."""
73
+ result = (
74
+ self.db.query(SessionModel)
75
+ .filter(
76
+ SessionModel.id == session_id,
77
+ SessionModel.user_id == user_id,
78
+ )
79
+ .delete()
80
+ )
81
+ self.db.commit()
82
+ return result > 0
83
+
84
+ def find_user_session_with_messages(
85
+ self, session_id: SessionId, user_id: UserId, pagination: PaginationInfo | None = None
86
+ ) -> tuple[Session, list[Message]] | None:
87
+ """Find a session with its messages."""
88
+ session_model = (
89
+ self.db.query(SessionModel)
90
+ .filter(
91
+ SessionModel.id == session_id,
92
+ SessionModel.user_id == user_id,
93
+ )
94
+ .first()
95
+ )
96
+
97
+ if not session_model:
98
+ return None
99
+
100
+ message_query = self.db.query(MessageModel).filter(
101
+ MessageModel.session_id == session_id
102
+ )
103
+
104
+ if pagination:
105
+ offset = (pagination.page - 1) * pagination.page_size
106
+ message_query = message_query.offset(offset).limit(pagination.page_size)
107
+
108
+ message_models = message_query.order_by(MessageModel.created_at.asc()).all()
109
+
110
+ session = self._model_to_entity(session_model)
111
+ messages = [self._message_model_to_entity(model) for model in message_models]
112
+
113
+ return session, messages
114
+
115
+ def _model_to_entity(self, model: SessionModel) -> Session:
116
+ """Convert SQLAlchemy model to domain entity."""
117
+ return Session(
118
+ id=model.id,
119
+ user_id=model.user_id,
120
+ name=model.name,
121
+ agent_id=model.agent_id,
122
+ created_at=model.created_at,
123
+ updated_at=model.updated_at,
124
+ last_activity=model.updated_at,
125
+ )
126
+
127
+ def _message_model_to_entity(self, model: MessageModel) -> Message:
128
+ """Convert SQLAlchemy message model to domain entity."""
129
+ from ..shared.enums import MessageType, SenderType
130
+
131
+ return Message(
132
+ id=model.id,
133
+ session_id=model.session_id,
134
+ message=model.message,
135
+ sender_type=SenderType(model.sender_type),
136
+ sender_name=model.sender_name,
137
+ message_type=MessageType.TEXT, # Default for now
138
+ created_at=model.created_at,
139
+ )
@@ -9,19 +9,19 @@ from solace_ai_connector.common.log import log
9
9
 
10
10
  from ....common.agent_registry import AgentRegistry
11
11
  from a2a.types import AgentCard
12
- from ....gateway.http_sse.dependencies import get_agent_registry
12
+ from ..dependencies import get_agent_registry
13
13
 
14
14
  router = APIRouter()
15
15
 
16
16
 
17
- @router.get("/agents", response_model=List[AgentCard])
18
- async def get_discovered_agents(
17
+ @router.get("/agentCards", response_model=List[AgentCard])
18
+ async def get_discovered_agent_cards(
19
19
  agent_registry: AgentRegistry = Depends(get_agent_registry),
20
20
  ):
21
21
  """
22
- Retrieves a list of all currently discovered A2A agents.
22
+ Retrieves a list of all currently discovered A2A agents' cards.
23
23
  """
24
- log_prefix = "[GET /api/v1/agents] "
24
+ log_prefix = "[GET /api/v1/agentCards] "
25
25
  log.info("%sRequest received.", log_prefix)
26
26
  try:
27
27
  agent_names = agent_registry.get_agent_names()
@@ -31,10 +31,10 @@ async def get_discovered_agents(
31
31
  if agent_registry.get_agent(name)
32
32
  ]
33
33
 
34
- log.info("%sReturning %d discovered agents.", log_prefix, len(agents))
34
+ log.info("%sReturning %d discovered agent cards.", log_prefix, len(agents))
35
35
  return agents
36
36
  except Exception as e:
37
- log.exception("%sError retrieving discovered agents: %s", log_prefix, e)
37
+ log.exception("%sError retrieving discovered agent cards: %s", log_prefix, e)
38
38
  raise HTTPException(
39
39
  status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
40
40
  detail="Internal server error retrieving agent list.",
@@ -43,6 +43,7 @@ async def get_app_config(
43
43
  "frontend_collect_feedback", False
44
44
  ),
45
45
  "frontend_bot_name": component.get_config("frontend_bot_name", "A2A Agent"),
46
+ "frontend_feature_enablement": component.get_config("frontend_feature_enablement", {}),
46
47
  "persistence_enabled": api_config.get("persistence_enabled", False),
47
48
  }
48
49
  log.info("%sReturning frontend configuration.", log_prefix)
@@ -0,0 +1,20 @@
1
+ """
2
+ Request DTOs for API endpoints.
3
+ """
4
+
5
+ from .session_requests import (
6
+ GetSessionsRequest,
7
+ GetSessionRequest,
8
+ GetSessionHistoryRequest,
9
+ UpdateSessionRequest,
10
+ DeleteSessionRequest,
11
+ )
12
+
13
+ __all__ = [
14
+ # Session requests
15
+ "GetSessionsRequest",
16
+ "GetSessionRequest",
17
+ "GetSessionHistoryRequest",
18
+ "UpdateSessionRequest",
19
+ "DeleteSessionRequest",
20
+ ]
@@ -39,11 +39,4 @@ class UpdateSessionRequest(BaseModel):
39
39
  class DeleteSessionRequest(BaseModel):
40
40
  """Request DTO for deleting a session."""
41
41
  session_id: SessionId
42
- user_id: UserId
43
-
44
-
45
- class CreateSessionRequest(BaseModel):
46
- """Request DTO for creating a new session."""
47
- user_id: UserId
48
- name: Optional[str] = Field(None, max_length=255, description="Session name")
49
- agent_id: Optional[str] = Field(None, description="Associated agent ID")
42
+ user_id: UserId
@@ -0,0 +1,16 @@
1
+ """
2
+ Response DTOs for API endpoints.
3
+ """
4
+
5
+ from .session_responses import (
6
+ MessageResponse,
7
+ SessionResponse,
8
+ SessionListResponse,
9
+ )
10
+
11
+ __all__ = [
12
+ # Session responses
13
+ "MessageResponse",
14
+ "SessionResponse",
15
+ "SessionListResponse",
16
+ ]
@@ -4,10 +4,10 @@ Session-related response DTOs.
4
4
 
5
5
  from typing import List, Optional
6
6
  from datetime import datetime
7
- from pydantic import BaseModel, Field
7
+ from pydantic import BaseModel
8
8
 
9
- from ....shared.types import SessionId, UserId, MessageId, PaginationInfo, Timestamp
10
- from ....shared.enums import SessionStatus, SenderType, MessageType
9
+ from ....shared.types import SessionId, UserId, MessageId, PaginationInfo
10
+ from ....shared.enums import SenderType, MessageType
11
11
 
12
12
 
13
13
  class MessageResponse(BaseModel):
@@ -29,7 +29,6 @@ class SessionResponse(BaseModel):
29
29
  user_id: UserId
30
30
  name: Optional[str] = None
31
31
  agent_id: Optional[str] = None
32
- status: SessionStatus = SessionStatus.ACTIVE
33
32
  created_at: datetime
34
33
  updated_at: Optional[datetime] = None
35
34
  last_activity: Optional[datetime] = None
@@ -40,29 +39,3 @@ class SessionListResponse(BaseModel):
40
39
  sessions: List[SessionResponse]
41
40
  pagination: Optional[PaginationInfo] = None
42
41
  total_count: int
43
-
44
-
45
- class SessionHistoryResponse(BaseModel):
46
- """Response DTO for session message history."""
47
- session_id: SessionId
48
- messages: List[MessageResponse]
49
- pagination: Optional[PaginationInfo] = None
50
- total_count: int
51
-
52
-
53
- class SessionCreatedResponse(BaseModel):
54
- """Response DTO for session creation."""
55
- session: SessionResponse
56
- message: str = "Session created successfully"
57
-
58
-
59
- class SessionUpdatedResponse(BaseModel):
60
- """Response DTO for session update."""
61
- session: SessionResponse
62
- message: str = "Session updated successfully"
63
-
64
-
65
- class SessionDeletedResponse(BaseModel):
66
- """Response DTO for session deletion."""
67
- session_id: SessionId
68
- message: str = "Session deleted successfully"
@@ -1,22 +1,19 @@
1
- from fastapi import APIRouter, Body, Depends, HTTPException, status
1
+ from fastapi import APIRouter, Body, Depends, HTTPException, status, BackgroundTasks
2
2
  from solace_ai_connector.common.log import log
3
3
 
4
- from ...application.services.session_service import SessionService
5
- from ...dependencies import (
6
- PublishFunc,
7
- get_namespace,
8
- get_publish_a2a_func,
9
- get_session_service,
4
+ from ..services.session_service import SessionService
5
+ from ..dependencies import (
6
+ get_session_business_service,
10
7
  )
11
- from ...shared.auth_utils import get_current_user
12
- from ..dto.requests.session_requests import (
8
+ from ..shared.auth_utils import get_current_user
9
+ from .dto.requests.session_requests import (
13
10
  DeleteSessionRequest,
14
11
  GetSessionHistoryRequest,
15
12
  GetSessionRequest,
16
13
  GetSessionsRequest,
17
14
  UpdateSessionRequest,
18
15
  )
19
- from ..dto.responses.session_responses import (
16
+ from .dto.responses.session_responses import (
20
17
  MessageResponse,
21
18
  SessionListResponse,
22
19
  SessionResponse,
@@ -28,7 +25,7 @@ router = APIRouter()
28
25
  @router.get("/sessions", response_model=SessionListResponse)
29
26
  async def get_all_sessions(
30
27
  user: dict = Depends(get_current_user),
31
- session_service: SessionService = Depends(get_session_service),
28
+ session_service: SessionService = Depends(get_session_business_service),
32
29
  ):
33
30
  user_id = user.get("id")
34
31
  log.info("Fetching sessions for user_id: %s", user_id)
@@ -47,7 +44,6 @@ async def get_all_sessions(
47
44
  user_id=domain.user_id,
48
45
  name=domain.name,
49
46
  agent_id=domain.agent_id,
50
- status=domain.status,
51
47
  created_at=domain.created_at,
52
48
  updated_at=domain.updated_at,
53
49
  last_activity=domain.last_activity,
@@ -72,7 +68,7 @@ async def get_all_sessions(
72
68
  async def get_session(
73
69
  session_id: str,
74
70
  user: dict = Depends(get_current_user),
75
- session_service: SessionService = Depends(get_session_service),
71
+ session_service: SessionService = Depends(get_session_business_service),
76
72
  ):
77
73
  user_id = user.get("id")
78
74
  log.info("User %s attempting to fetch session_id: %s", user_id, session_id)
@@ -89,7 +85,7 @@ async def get_session(
89
85
 
90
86
  request_dto = GetSessionRequest(session_id=session_id, user_id=user_id)
91
87
 
92
- session_domain = session_service.get_session(
88
+ session_domain = session_service.get_session_details(
93
89
  session_id=request_dto.session_id, user_id=request_dto.user_id
94
90
  )
95
91
 
@@ -105,7 +101,6 @@ async def get_session(
105
101
  user_id=session_domain.user_id,
106
102
  name=session_domain.name,
107
103
  agent_id=session_domain.agent_id,
108
- status=session_domain.status,
109
104
  created_at=session_domain.created_at,
110
105
  updated_at=session_domain.updated_at,
111
106
  last_activity=session_domain.last_activity,
@@ -130,7 +125,7 @@ async def get_session(
130
125
  async def get_session_history(
131
126
  session_id: str,
132
127
  user: dict = Depends(get_current_user),
133
- session_service: SessionService = Depends(get_session_service),
128
+ session_service: SessionService = Depends(get_session_business_service),
134
129
  ):
135
130
  user_id = user.get("id")
136
131
  log.info(
@@ -202,7 +197,7 @@ async def update_session_name(
202
197
  session_id: str,
203
198
  name: str = Body(..., embed=True),
204
199
  user: dict = Depends(get_current_user),
205
- session_service: SessionService = Depends(get_session_service),
200
+ session_service: SessionService = Depends(get_session_business_service),
206
201
  ):
207
202
  user_id = user.get("id")
208
203
  log.info("User %s attempting to update session %s", user_id, session_id)
@@ -239,7 +234,6 @@ async def update_session_name(
239
234
  user_id=updated_domain.user_id,
240
235
  name=updated_domain.name,
241
236
  agent_id=updated_domain.agent_id,
242
- status=updated_domain.status,
243
237
  created_at=updated_domain.created_at,
244
238
  updated_at=updated_domain.updated_at,
245
239
  last_activity=updated_domain.last_activity,
@@ -269,36 +263,14 @@ async def update_session_name(
269
263
  async def delete_session(
270
264
  session_id: str,
271
265
  user: dict = Depends(get_current_user),
272
- session_service: SessionService = Depends(get_session_service),
273
- publish_func: PublishFunc = Depends(get_publish_a2a_func),
274
- namespace: str = Depends(get_namespace),
266
+ session_service: SessionService = Depends(get_session_business_service),
275
267
  ):
276
268
  user_id = user.get("id")
277
269
  log.info("User %s attempting to delete session %s", user_id, session_id)
278
270
 
279
271
  try:
280
- if (
281
- not session_id
282
- or session_id.strip() == ""
283
- or session_id in ["null", "undefined"]
284
- ):
285
- raise HTTPException(
286
- status_code=status.HTTP_404_NOT_FOUND, detail="Session not found."
287
- )
288
-
289
- request_dto = DeleteSessionRequest(session_id=session_id, user_id=user_id)
290
-
291
- # Get session details before deletion to find the agent_id
292
- session = session_service.get_session(session_id=session_id, user_id=user_id)
293
- if not session:
294
- raise HTTPException(
295
- status_code=status.HTTP_404_NOT_FOUND, detail="Session not found."
296
- )
297
-
298
- agent_id = session.agent_id
299
-
300
- deleted = session_service.delete_session(
301
- session_id=request_dto.session_id, user_id=request_dto.user_id
272
+ deleted = session_service.delete_session_with_notifications(
273
+ session_id=session_id, user_id=user_id
302
274
  )
303
275
 
304
276
  if not deleted:
@@ -308,40 +280,11 @@ async def delete_session(
308
280
 
309
281
  log.info("Session %s deleted successfully", session_id)
310
282
 
311
- if agent_id:
312
- try:
313
- from solace_agent_mesh.common.a2a.protocol import (
314
- get_agent_request_topic,
315
- )
316
-
317
- control_message = {
318
- "control": {
319
- "action": "delete_session",
320
- "session_id": session_id,
321
- "user_id": user_id,
322
- }
323
- }
324
-
325
- target_topic = get_agent_request_topic(namespace, agent_id)
326
-
327
- log.info(
328
- "Sending session deletion notification to agent %s for session %s",
329
- agent_id,
330
- session_id,
331
- )
332
-
333
- publish_func(target_topic, control_message, None)
334
-
335
- except Exception as e:
336
- log.warning(
337
- "Failed to notify agent %s about session %s deletion: %s",
338
- agent_id,
339
- session_id,
340
- e,
341
- )
342
-
343
- except HTTPException:
344
- raise
283
+ except ValueError as e:
284
+ log.warning("Validation error deleting session %s: %s", session_id, e)
285
+ raise HTTPException(
286
+ status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)
287
+ )
345
288
  except Exception as e:
346
289
  log.error(
347
290
  "Error deleting session %s for user %s: %s",
@@ -105,60 +105,53 @@ async def _submit_task(
105
105
 
106
106
  # Store message in persistence layer if available
107
107
  user_id = user_identity.get("id")
108
- if is_streaming and hasattr(component, "persistence_service") and component.persistence_service:
108
+ from ....gateway.http_sse.dependencies import SessionLocal
109
+ if is_streaming and SessionLocal is not None:
109
110
  try:
110
- from ....gateway.http_sse.dependencies import get_session_service
111
+ from ....gateway.http_sse.dependencies import create_session_service_with_transaction
111
112
  from ....gateway.http_sse.shared.enums import SenderType
112
113
 
113
- session_service = get_session_service(component)
114
-
115
- # First ensure session exists in database - create it with the SessionManager's ID
116
- # Handle race condition where multiple requests might try to create the same session
117
- existing_session = session_service.get_session(session_id=session_id, user_id=user_id)
118
- if not existing_session:
119
- log.info("%sCreating new session in database: %s", log_prefix, session_id)
120
- try:
121
- session_service.create_session(
122
- user_id=user_id,
123
- agent_id=agent_name,
124
- name=None, # Will be auto-generated if needed
125
- session_id=session_id # Use the SessionManager's session ID
126
- )
127
- except Exception as create_error:
128
- # Another request may have created the session concurrently
129
- log.warning("%sSession creation failed, checking if session exists: %s", log_prefix, create_error)
130
- existing_session = session_service.get_session(session_id=session_id, user_id=user_id)
131
- if not existing_session:
132
- # If session still doesn't exist, re-raise the original error
133
- raise create_error
134
- log.info("%sSession was created by another request: %s", log_prefix, session_id)
135
-
136
- # Extract text content from the message for storage
137
- message_text = ""
138
- if payload.params and payload.params.message:
139
- parts = a2a.get_parts_from_message(payload.params.message)
140
- for part in parts:
141
- if hasattr(part, 'text'):
142
- message_text = part.text
143
- break
144
-
145
- # Now store the message in the existing session
146
- message_domain = session_service.add_message_to_session(
147
- session_id=session_id,
148
- user_id=user_id,
149
- message=message_text or "Task submitted",
150
- sender_type=SenderType.USER,
151
- sender_name=user_id or "user",
152
- agent_id=agent_name,
153
- )
154
-
155
- if message_domain:
156
- log.info("%sMessage stored in session %s", log_prefix, session_id)
157
- else:
158
- log.warning("%sFailed to store message in session %s", log_prefix, session_id)
114
+ with create_session_service_with_transaction() as (session_service, db):
115
+ existing_session = session_service.get_session(session_id=session_id, user_id=user_id)
116
+ if not existing_session:
117
+ log.info("%sCreating new session in database: %s", log_prefix, session_id)
118
+ try:
119
+ session_service.create_session(
120
+ user_id=user_id,
121
+ agent_id=agent_name,
122
+ name=None,
123
+ session_id=session_id
124
+ )
125
+ except Exception as create_error:
126
+ log.warning("%sSession creation failed, checking if session exists: %s", log_prefix, create_error)
127
+ existing_session = session_service.get_session(session_id=session_id, user_id=user_id)
128
+ if not existing_session:
129
+ raise create_error
130
+ log.info("%sSession was created by another request: %s", log_prefix, session_id)
131
+
132
+ message_text = ""
133
+ if payload.params and payload.params.message:
134
+ parts = a2a.get_parts_from_message(payload.params.message)
135
+ for part in parts:
136
+ if hasattr(part, 'text'):
137
+ message_text = part.text
138
+ break
139
+
140
+ message_domain = session_service.add_message_to_session(
141
+ session_id=session_id,
142
+ user_id=user_id,
143
+ message=message_text or "Task submitted",
144
+ sender_type=SenderType.USER,
145
+ sender_name=user_id or "user",
146
+ agent_id=agent_name,
147
+ )
148
+
149
+ if message_domain:
150
+ log.info("%sMessage stored in session %s", log_prefix, session_id)
151
+ else:
152
+ log.warning("%sFailed to store message in session %s", log_prefix, session_id)
159
153
  except Exception as e:
160
154
  log.error("%sFailed to store message in session service: %s", log_prefix, e)
161
- # Don't fail the request, just log the error
162
155
  else:
163
156
  log.debug("%sNo persistence available or non-streaming - skipping message storage", log_prefix)
164
157
 
@@ -8,7 +8,7 @@ from typing import Any
8
8
  from fastapi import APIRouter, Depends
9
9
  from solace_ai_connector.common.log import log
10
10
 
11
- from ...shared.auth_utils import get_current_user
11
+ from ..shared.auth_utils import get_current_user
12
12
 
13
13
  router = APIRouter()
14
14