solace-agent-mesh 1.5.1__py3-none-any.whl → 1.6.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.

Files changed (180) hide show
  1. solace_agent_mesh/agent/adk/callbacks.py +0 -5
  2. solace_agent_mesh/agent/adk/models/lite_llm.py +123 -8
  3. solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +245 -0
  4. solace_agent_mesh/agent/protocol/event_handlers.py +40 -1
  5. solace_agent_mesh/agent/proxies/__init__.py +0 -0
  6. solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
  7. solace_agent_mesh/agent/proxies/a2a/app.py +55 -0
  8. solace_agent_mesh/agent/proxies/a2a/component.py +1115 -0
  9. solace_agent_mesh/agent/proxies/a2a/config.py +140 -0
  10. solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
  11. solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
  12. solace_agent_mesh/agent/proxies/base/app.py +99 -0
  13. solace_agent_mesh/agent/proxies/base/component.py +619 -0
  14. solace_agent_mesh/agent/proxies/base/config.py +85 -0
  15. solace_agent_mesh/agent/proxies/base/proxy_task_context.py +17 -0
  16. solace_agent_mesh/agent/sac/app.py +9 -3
  17. solace_agent_mesh/agent/sac/component.py +160 -8
  18. solace_agent_mesh/agent/tools/audio_tools.py +125 -8
  19. solace_agent_mesh/agent/tools/web_tools.py +10 -5
  20. solace_agent_mesh/agent/utils/artifact_helpers.py +141 -3
  21. solace_agent_mesh/assets/docs/404.html +3 -3
  22. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +1 -0
  23. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +1 -0
  24. solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +1 -0
  25. solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
  26. solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +1 -0
  27. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
  28. solace_agent_mesh/assets/docs/assets/js/{ad71b5ed.60668e9e.js → ad71b5ed.af3ecfd1.js} +1 -1
  29. solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
  30. solace_agent_mesh/assets/docs/assets/js/{da0b5bad.9d369087.js → da0b5bad.d08a9466.js} +1 -1
  31. solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
  32. solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +1 -0
  33. solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +1 -0
  34. solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +1 -0
  35. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +1 -0
  36. solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js → main.20feee82.js} +2 -2
  37. solace_agent_mesh/assets/docs/assets/js/runtime~main.0d198646.js +1 -0
  38. solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +15 -4
  39. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +4 -4
  40. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +4 -4
  41. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +4 -4
  42. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +4 -4
  43. solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +4 -4
  44. solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +4 -4
  45. solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +4 -4
  46. solace_agent_mesh/assets/docs/docs/documentation/components/index.html +4 -4
  47. solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +4 -4
  48. solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +4 -4
  49. solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +262 -0
  50. solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +3 -3
  51. solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +31 -3
  52. solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +3 -3
  53. solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +3 -3
  54. solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +4 -4
  55. solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +5 -5
  56. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +4 -4
  57. solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +4 -4
  58. solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
  59. solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +6 -4
  60. solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +4 -4
  61. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
  62. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +4 -4
  63. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +5 -5
  64. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
  65. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
  66. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
  67. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
  68. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
  69. solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
  70. solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +3 -3
  71. solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
  72. solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +3 -3
  73. solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
  74. solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
  75. solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
  76. solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
  77. solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
  78. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +6 -5
  79. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
  80. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
  81. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +100 -3
  82. solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
  83. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
  84. solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
  85. solace_agent_mesh/assets/docs/lunr-index-1761165361160.json +1 -0
  86. solace_agent_mesh/assets/docs/lunr-index.json +1 -1
  87. solace_agent_mesh/assets/docs/search-doc-1761165361160.json +1 -0
  88. solace_agent_mesh/assets/docs/search-doc.json +1 -1
  89. solace_agent_mesh/assets/docs/sitemap.xml +1 -1
  90. solace_agent_mesh/cli/__init__.py +1 -1
  91. solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +2 -69
  92. solace_agent_mesh/cli/commands/eval_cmd.py +11 -49
  93. solace_agent_mesh/cli/commands/init_cmd/__init__.py +0 -5
  94. solace_agent_mesh/cli/commands/init_cmd/env_step.py +10 -12
  95. solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +9 -61
  96. solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +9 -49
  97. solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +1 -2
  98. solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DwrxZE0E.js → authCallback-BTf6dqwp.js} +1 -1
  99. solace_agent_mesh/client/webui/frontend/static/assets/{client-DarGQzyw.js → client-CaY59VuC.js} +1 -1
  100. solace_agent_mesh/client/webui/frontend/static/assets/main-BGTaW0uv.js +342 -0
  101. solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +1 -0
  102. solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BKIeiHj_.js → vendor-BEmvJSYz.js} +1 -1
  103. solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
  104. solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
  105. solace_agent_mesh/common/a2a/__init__.py +24 -0
  106. solace_agent_mesh/common/a2a/artifact.py +39 -0
  107. solace_agent_mesh/common/a2a/events.py +29 -0
  108. solace_agent_mesh/common/a2a/message.py +68 -0
  109. solace_agent_mesh/common/a2a/protocol.py +73 -1
  110. solace_agent_mesh/common/agent_registry.py +83 -3
  111. solace_agent_mesh/common/constants.py +3 -1
  112. solace_agent_mesh/common/utils/pydantic_utils.py +12 -0
  113. solace_agent_mesh/config_portal/backend/common.py +1 -1
  114. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +98 -0
  115. solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-44d62be6.js → manifest-61038fc6.js} +1 -1
  116. solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
  117. solace_agent_mesh/evaluation/evaluator.py +128 -104
  118. solace_agent_mesh/evaluation/message_organizer.py +116 -110
  119. solace_agent_mesh/evaluation/report_data_processor.py +84 -86
  120. solace_agent_mesh/evaluation/report_generator.py +73 -79
  121. solace_agent_mesh/evaluation/run.py +421 -235
  122. solace_agent_mesh/evaluation/shared/__init__.py +92 -0
  123. solace_agent_mesh/evaluation/shared/constants.py +47 -0
  124. solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
  125. solace_agent_mesh/evaluation/shared/helpers.py +35 -0
  126. solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
  127. solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
  128. solace_agent_mesh/evaluation/subscriber.py +111 -232
  129. solace_agent_mesh/evaluation/summary_builder.py +227 -117
  130. solace_agent_mesh/gateway/base/app.py +1 -1
  131. solace_agent_mesh/gateway/base/component.py +8 -1
  132. solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
  133. solace_agent_mesh/gateway/http_sse/component.py +98 -2
  134. solace_agent_mesh/gateway/http_sse/dependencies.py +4 -4
  135. solace_agent_mesh/gateway/http_sse/main.py +2 -1
  136. solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +12 -13
  137. solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +15 -18
  138. solace_agent_mesh/gateway/http_sse/repository/interfaces.py +25 -18
  139. solace_agent_mesh/gateway/http_sse/repository/session_repository.py +30 -26
  140. solace_agent_mesh/gateway/http_sse/repository/task_repository.py +35 -44
  141. solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +4 -3
  142. solace_agent_mesh/gateway/http_sse/routers/artifacts.py +95 -203
  143. solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +4 -3
  144. solace_agent_mesh/gateway/http_sse/routers/sessions.py +2 -2
  145. solace_agent_mesh/gateway/http_sse/routers/tasks.py +33 -41
  146. solace_agent_mesh/gateway/http_sse/routers/visualization.py +17 -11
  147. solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +4 -4
  148. solace_agent_mesh/gateway/http_sse/services/feedback_service.py +51 -43
  149. solace_agent_mesh/gateway/http_sse/services/session_service.py +20 -20
  150. solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +8 -8
  151. solace_agent_mesh/gateway/http_sse/shared/base_repository.py +45 -71
  152. solace_agent_mesh/gateway/http_sse/shared/types.py +0 -18
  153. solace_agent_mesh/templates/gateway_config_template.yaml +0 -5
  154. solace_agent_mesh/templates/logging_config_template.ini +10 -6
  155. solace_agent_mesh/templates/plugin_gateway_config_template.yaml +0 -3
  156. solace_agent_mesh/templates/shared_config.yaml +40 -0
  157. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/METADATA +47 -21
  158. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/RECORD +162 -141
  159. solace_agent_mesh/assets/docs/assets/js/5c2bd65f.e49689dd.js +0 -1
  160. solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.39d5851d.js +0 -1
  161. solace_agent_mesh/assets/docs/assets/js/71da7b71.804d6567.js +0 -1
  162. solace_agent_mesh/assets/docs/assets/js/77cf947d.64c9bd6c.js +0 -1
  163. solace_agent_mesh/assets/docs/assets/js/9e9d0a82.dd810042.js +0 -1
  164. solace_agent_mesh/assets/docs/assets/js/db924877.cbc66f02.js +0 -1
  165. solace_agent_mesh/assets/docs/assets/js/de915948.139b4b9c.js +0 -1
  166. solace_agent_mesh/assets/docs/assets/js/e6f9706b.582a78ca.js +0 -1
  167. solace_agent_mesh/assets/docs/assets/js/f284c35a.5766a13d.js +0 -1
  168. solace_agent_mesh/assets/docs/assets/js/ff4d71f2.9c0297a6.js +0 -1
  169. solace_agent_mesh/assets/docs/assets/js/runtime~main.18dc45dd.js +0 -1
  170. solace_agent_mesh/assets/docs/lunr-index-1760121512891.json +0 -1
  171. solace_agent_mesh/assets/docs/search-doc-1760121512891.json +0 -1
  172. solace_agent_mesh/client/webui/frontend/static/assets/main-2nd1gbaH.js +0 -339
  173. solace_agent_mesh/client/webui/frontend/static/assets/main-DoKXctCM.css +0 -1
  174. solace_agent_mesh/config_portal/frontend/static/client/assets/_index-BNuqpWDc.js +0 -98
  175. solace_agent_mesh/evaluation/config_loader.py +0 -657
  176. solace_agent_mesh/evaluation/test_case_loader.py +0 -714
  177. /solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js.LICENSE.txt → main.20feee82.js.LICENSE.txt} +0 -0
  178. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/WHEEL +0 -0
  179. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/entry_points.txt +0 -0
  180. {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,140 @@
1
+ """
2
+ Pydantic configuration models for A2A proxy applications.
3
+ """
4
+
5
+ from typing import List, Literal, Optional
6
+ from urllib.parse import urlparse
7
+
8
+ from pydantic import Field, model_validator
9
+
10
+ from ..base.config import BaseProxyAppConfig, ProxiedAgentConfig
11
+ from ....common.utils.pydantic_utils import SamConfigBase
12
+
13
+
14
+ class AuthenticationConfig(SamConfigBase):
15
+ """Authentication configuration for downstream A2A agents."""
16
+
17
+ type: Optional[
18
+ Literal["static_bearer", "static_apikey", "oauth2_client_credentials"]
19
+ ] = Field(
20
+ default=None,
21
+ description="Authentication type. If not specified, inferred from 'scheme' for backward compatibility.",
22
+ )
23
+ scheme: Optional[str] = Field(
24
+ default=None,
25
+ description="(Legacy) The authentication scheme (e.g., 'bearer', 'apikey'). Use 'type' field instead.",
26
+ )
27
+ token: Optional[str] = Field(
28
+ default=None,
29
+ description="The authentication token or API key (for static_bearer and static_apikey types).",
30
+ )
31
+ token_url: Optional[str] = Field(
32
+ default=None,
33
+ description="OAuth 2.0 token endpoint URL (required for oauth2_client_credentials type).",
34
+ )
35
+ client_id: Optional[str] = Field(
36
+ default=None,
37
+ description="OAuth 2.0 client identifier (required for oauth2_client_credentials type).",
38
+ )
39
+ client_secret: Optional[str] = Field(
40
+ default=None,
41
+ description="OAuth 2.0 client secret (required for oauth2_client_credentials type).",
42
+ )
43
+ scope: Optional[str] = Field(
44
+ default=None,
45
+ description="OAuth 2.0 scope as a space-separated string (optional for oauth2_client_credentials type).",
46
+ )
47
+ token_cache_duration_seconds: int = Field(
48
+ default=3300,
49
+ gt=0,
50
+ description="How long to cache OAuth 2.0 tokens before refresh, in seconds (default: 3300 = 55 minutes).",
51
+ )
52
+
53
+ @model_validator(mode="after")
54
+ def validate_auth_config(self) -> "AuthenticationConfig":
55
+ """Validates authentication configuration based on type."""
56
+ # Determine effective auth type (with backward compatibility)
57
+ auth_type = self.type
58
+ if not auth_type and self.scheme:
59
+ # Legacy config: infer type from scheme
60
+ if self.scheme == "bearer":
61
+ auth_type = "static_bearer"
62
+ elif self.scheme == "apikey":
63
+ auth_type = "static_apikey"
64
+ else:
65
+ raise ValueError(
66
+ f"Unknown legacy authentication scheme '{self.scheme}'. "
67
+ f"Supported schemes: 'bearer', 'apikey'."
68
+ )
69
+
70
+ if not auth_type:
71
+ # No authentication configured
72
+ return self
73
+
74
+ # Validate based on auth type
75
+ if auth_type in ["static_bearer", "static_apikey"]:
76
+ if not self.token:
77
+ raise ValueError(
78
+ f"Authentication type '{auth_type}' requires 'token' field."
79
+ )
80
+
81
+ elif auth_type == "oauth2_client_credentials":
82
+ # Validate token_url
83
+ if not self.token_url:
84
+ raise ValueError(
85
+ "OAuth 2.0 client credentials flow requires 'token_url'."
86
+ )
87
+
88
+ # Validate token_url is HTTPS
89
+ try:
90
+ parsed_url = urlparse(self.token_url)
91
+ if parsed_url.scheme != "https":
92
+ raise ValueError(
93
+ f"OAuth 2.0 'token_url' must use HTTPS for security. "
94
+ f"Got scheme: '{parsed_url.scheme}'"
95
+ )
96
+ except Exception as e:
97
+ raise ValueError(f"Failed to parse 'token_url': {e}")
98
+
99
+ # Validate client_id
100
+ if not self.client_id:
101
+ raise ValueError(
102
+ "OAuth 2.0 client credentials flow requires 'client_id'."
103
+ )
104
+
105
+ # Validate client_secret
106
+ if not self.client_secret:
107
+ raise ValueError(
108
+ "OAuth 2.0 client credentials flow requires 'client_secret'."
109
+ )
110
+
111
+ else:
112
+ raise ValueError(
113
+ f"Unsupported authentication type '{auth_type}'. "
114
+ f"Supported types: static_bearer, static_apikey, oauth2_client_credentials."
115
+ )
116
+
117
+ return self
118
+
119
+
120
+ class A2AProxiedAgentConfig(ProxiedAgentConfig):
121
+ """Configuration for an A2A-over-HTTPS proxied agent."""
122
+
123
+ url: str = Field(
124
+ ...,
125
+ description="The base URL of the downstream A2A agent's HTTP endpoint.",
126
+ )
127
+ authentication: Optional[AuthenticationConfig] = Field(
128
+ default=None,
129
+ description="Authentication details for the downstream agent.",
130
+ )
131
+
132
+
133
+ class A2AProxyAppConfig(BaseProxyAppConfig):
134
+ """Complete configuration for an A2A proxy application."""
135
+
136
+ proxied_agents: List[A2AProxiedAgentConfig] = Field(
137
+ ...,
138
+ min_length=1,
139
+ description="A list of downstream A2A agents to be proxied.",
140
+ )
@@ -0,0 +1,104 @@
1
+ """
2
+ OAuth 2.0 token caching for A2A proxy authentication.
3
+
4
+ This module provides an in-memory cache for OAuth 2.0 access tokens
5
+ with automatic expiration. Tokens are cached per agent to minimize
6
+ token acquisition overhead and reduce load on authorization servers.
7
+
8
+ The cache is thread-safe using asyncio.Lock and implements lazy
9
+ expiration (tokens are checked for expiration on retrieval).
10
+ """
11
+
12
+ import asyncio
13
+ import time
14
+ from dataclasses import dataclass
15
+ from typing import Dict, Optional
16
+
17
+ from solace_ai_connector.common.log import log
18
+
19
+
20
+ @dataclass
21
+ class CachedToken:
22
+ """Represents a cached OAuth token with expiration."""
23
+
24
+ access_token: str
25
+ expires_at: float # Unix timestamp when token expires (time.time() + cache_duration)
26
+
27
+
28
+ class OAuth2TokenCache:
29
+ """
30
+ Thread-safe in-memory cache for OAuth 2.0 access tokens.
31
+
32
+ Tokens are cached per agent and automatically expire based on
33
+ the configured cache duration.
34
+ """
35
+
36
+ def __init__(self):
37
+ """Initialize the token cache with an empty dictionary and lock."""
38
+ self._cache: Dict[str, CachedToken] = {}
39
+ self._lock = asyncio.Lock()
40
+
41
+ async def get(self, agent_name: str) -> Optional[str]:
42
+ """
43
+ Retrieves a cached token if it exists and hasn't expired.
44
+
45
+ Args:
46
+ agent_name: The name of the agent to get the token for.
47
+
48
+ Returns:
49
+ The access token if cached and valid, None otherwise.
50
+ """
51
+ async with self._lock:
52
+ cached = self._cache.get(agent_name)
53
+ if not cached:
54
+ return None
55
+
56
+ # Check if token has expired
57
+ if time.time() >= cached.expires_at:
58
+ log.debug(
59
+ "Cached token for '%s' has expired. Removing from cache.",
60
+ agent_name,
61
+ )
62
+ del self._cache[agent_name]
63
+ return None
64
+
65
+ log.debug(
66
+ "Using cached OAuth token for '%s' (expires in %.0fs)",
67
+ agent_name,
68
+ cached.expires_at - time.time(),
69
+ )
70
+ return cached.access_token
71
+
72
+ async def set(
73
+ self, agent_name: str, access_token: str, cache_duration_seconds: int
74
+ ):
75
+ """
76
+ Caches a token with an expiration time.
77
+
78
+ Args:
79
+ agent_name: The name of the agent.
80
+ access_token: The OAuth 2.0 access token.
81
+ cache_duration_seconds: How long the token should be cached.
82
+ """
83
+ async with self._lock:
84
+ expires_at = time.time() + cache_duration_seconds
85
+ self._cache[agent_name] = CachedToken(
86
+ access_token=access_token, expires_at=expires_at
87
+ )
88
+ log.debug(
89
+ "Cached token for '%s' (expires in %ds)",
90
+ agent_name,
91
+ cache_duration_seconds,
92
+ )
93
+
94
+ async def invalidate(self, agent_name: str):
95
+ """
96
+ Removes a token from the cache.
97
+
98
+ Args:
99
+ agent_name: The name of the agent.
100
+ """
101
+ async with self._lock:
102
+ if agent_name in self._cache:
103
+ del self._cache[agent_name]
104
+ log.info("Invalidated cached token for '%s'", agent_name)
@@ -0,0 +1,3 @@
1
+ """
2
+ This package contains the protocol-agnostic base classes for all proxy implementations.
3
+ """
@@ -0,0 +1,99 @@
1
+ """
2
+ Abstract base class for proxy apps.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from abc import ABC, abstractmethod
8
+ from typing import Any, Dict, Type
9
+
10
+ from pydantic import ValidationError
11
+ from solace_ai_connector.common.log import log
12
+ from solace_ai_connector.flow.app import App
13
+
14
+ from ....common.a2a import get_agent_request_topic
15
+ from .component import BaseProxyComponent
16
+ from .config import BaseProxyAppConfig
17
+
18
+
19
+ info = {
20
+ "class_name": "BaseProxyApp",
21
+ "description": "Abstract base class for proxy apps. Handles common configuration and subscription generation.",
22
+ }
23
+
24
+
25
+ class BaseProxyApp(App, ABC):
26
+ """
27
+ Abstract base class for proxy apps.
28
+
29
+ Handles common configuration schema, generates Solace topic subscriptions for all
30
+ proxied agents, and programmatically defines the single proxy component instance.
31
+ """
32
+
33
+ app_schema = {}
34
+
35
+ def __init__(self, app_info: Dict[str, Any], **kwargs):
36
+ log.debug("Initializing BaseProxyApp...")
37
+
38
+ app_config_dict = app_info.get("app_config", {})
39
+
40
+ try:
41
+ # Validate the raw dict, cleaning None values to allow defaults to apply
42
+ app_config = BaseProxyAppConfig.model_validate_and_clean(app_config_dict)
43
+ # Overwrite the raw dict with the validated object for downstream use
44
+ app_info["app_config"] = app_config
45
+ except ValidationError as e:
46
+ log.error("Proxy configuration validation failed:\n%s", e)
47
+ raise ValueError(f"Invalid proxy configuration: {e}") from e
48
+
49
+ namespace = app_config.get("namespace")
50
+ proxied_agents = app_config.get("proxied_agents", [])
51
+
52
+ # Generate subscriptions for each proxied agent
53
+ required_topics = [
54
+ get_agent_request_topic(namespace, agent["name"])
55
+ for agent in proxied_agents
56
+ if "name" in agent
57
+ ]
58
+ generated_subs = [{"topic": topic} for topic in required_topics]
59
+ log.info(
60
+ "Automatically generated subscriptions for proxy: %s",
61
+ generated_subs,
62
+ )
63
+
64
+ # Programmatically define the component
65
+ component_class = self._get_component_class()
66
+ component_definition = {
67
+ "name": f"{app_info.get('name', 'proxy')}_component",
68
+ "component_class": component_class,
69
+ "component_config": {}, # Component will get config from app_config
70
+ "subscriptions": generated_subs,
71
+ }
72
+ app_info["components"] = [component_definition]
73
+ log.debug("Replaced 'components' in app_info with programmatic definition.")
74
+
75
+ # Ensure broker is configured for input/output
76
+ broker_config = app_info.setdefault("broker", {})
77
+ broker_config["input_enabled"] = True
78
+ broker_config["output_enabled"] = True
79
+ log.debug("Injected broker.input_enabled=True and broker.output_enabled=True")
80
+
81
+ # Generate a unique queue name
82
+ app_name = app_info.get("name", "proxy-app")
83
+ generated_queue_name = f"{namespace.strip('/')}/q/proxy/{app_name}"
84
+ broker_config["queue_name"] = generated_queue_name
85
+ log.debug("Injected generated broker.queue_name: %s", generated_queue_name)
86
+
87
+ broker_config["temporary_queue"] = True
88
+ log.debug("Set broker_config.temporary_queue = True")
89
+
90
+ super().__init__(app_info, **kwargs)
91
+ log.debug("BaseProxyApp initialization complete.")
92
+
93
+ @abstractmethod
94
+ def _get_component_class(self) -> Type[BaseProxyComponent]:
95
+ """
96
+ Abstract method to be implemented by concrete proxy apps.
97
+ Must return the specific proxy component class to be instantiated.
98
+ """
99
+ raise NotImplementedError