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.
- solace_agent_mesh/agent/adk/callbacks.py +0 -5
- solace_agent_mesh/agent/adk/models/lite_llm.py +123 -8
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +245 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +40 -1
- solace_agent_mesh/agent/proxies/__init__.py +0 -0
- solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/a2a/app.py +55 -0
- solace_agent_mesh/agent/proxies/a2a/component.py +1115 -0
- solace_agent_mesh/agent/proxies/a2a/config.py +140 -0
- solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
- solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/base/app.py +99 -0
- solace_agent_mesh/agent/proxies/base/component.py +619 -0
- solace_agent_mesh/agent/proxies/base/config.py +85 -0
- solace_agent_mesh/agent/proxies/base/proxy_task_context.py +17 -0
- solace_agent_mesh/agent/sac/app.py +9 -3
- solace_agent_mesh/agent/sac/component.py +160 -8
- solace_agent_mesh/agent/tools/audio_tools.py +125 -8
- solace_agent_mesh/agent/tools/web_tools.py +10 -5
- solace_agent_mesh/agent/utils/artifact_helpers.py +141 -3
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{ad71b5ed.60668e9e.js → ad71b5ed.af3ecfd1.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{da0b5bad.9d369087.js → da0b5bad.d08a9466.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js → main.20feee82.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.0d198646.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +15 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +262 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +31 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +135 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +6 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +6 -5
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +100 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
- solace_agent_mesh/assets/docs/lunr-index-1761165361160.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1761165361160.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 +2 -69
- solace_agent_mesh/cli/commands/eval_cmd.py +11 -49
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +0 -5
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +10 -12
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +9 -61
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +9 -49
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +1 -2
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DwrxZE0E.js → authCallback-BTf6dqwp.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-DarGQzyw.js → client-CaY59VuC.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BGTaW0uv.js +342 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BKIeiHj_.js → vendor-BEmvJSYz.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
- solace_agent_mesh/common/a2a/__init__.py +24 -0
- solace_agent_mesh/common/a2a/artifact.py +39 -0
- solace_agent_mesh/common/a2a/events.py +29 -0
- solace_agent_mesh/common/a2a/message.py +68 -0
- solace_agent_mesh/common/a2a/protocol.py +73 -1
- solace_agent_mesh/common/agent_registry.py +83 -3
- solace_agent_mesh/common/constants.py +3 -1
- solace_agent_mesh/common/utils/pydantic_utils.py +12 -0
- solace_agent_mesh/config_portal/backend/common.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +98 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-44d62be6.js → manifest-61038fc6.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
- solace_agent_mesh/evaluation/evaluator.py +128 -104
- solace_agent_mesh/evaluation/message_organizer.py +116 -110
- solace_agent_mesh/evaluation/report_data_processor.py +84 -86
- solace_agent_mesh/evaluation/report_generator.py +73 -79
- solace_agent_mesh/evaluation/run.py +421 -235
- solace_agent_mesh/evaluation/shared/__init__.py +92 -0
- solace_agent_mesh/evaluation/shared/constants.py +47 -0
- solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
- solace_agent_mesh/evaluation/shared/helpers.py +35 -0
- solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
- solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
- solace_agent_mesh/evaluation/subscriber.py +111 -232
- solace_agent_mesh/evaluation/summary_builder.py +227 -117
- solace_agent_mesh/gateway/base/app.py +1 -1
- solace_agent_mesh/gateway/base/component.py +8 -1
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
- solace_agent_mesh/gateway/http_sse/component.py +98 -2
- solace_agent_mesh/gateway/http_sse/dependencies.py +4 -4
- solace_agent_mesh/gateway/http_sse/main.py +2 -1
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +12 -13
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +15 -18
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +25 -18
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +30 -26
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +35 -44
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +4 -3
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +95 -203
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +4 -3
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +33 -41
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +17 -11
- solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +4 -4
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +51 -43
- solace_agent_mesh/gateway/http_sse/services/session_service.py +20 -20
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +8 -8
- solace_agent_mesh/gateway/http_sse/shared/base_repository.py +45 -71
- solace_agent_mesh/gateway/http_sse/shared/types.py +0 -18
- solace_agent_mesh/templates/gateway_config_template.yaml +0 -5
- solace_agent_mesh/templates/logging_config_template.ini +10 -6
- solace_agent_mesh/templates/plugin_gateway_config_template.yaml +0 -3
- solace_agent_mesh/templates/shared_config.yaml +40 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/METADATA +47 -21
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/RECORD +162 -141
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.e49689dd.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.39d5851d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/71da7b71.804d6567.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/77cf947d.64c9bd6c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.dd810042.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/db924877.cbc66f02.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/de915948.139b4b9c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.582a78ca.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.5766a13d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.9c0297a6.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/runtime~main.18dc45dd.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1760121512891.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1760121512891.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-2nd1gbaH.js +0 -339
- solace_agent_mesh/client/webui/frontend/static/assets/main-DoKXctCM.css +0 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-BNuqpWDc.js +0 -98
- solace_agent_mesh/evaluation/config_loader.py +0 -657
- solace_agent_mesh/evaluation/test_case_loader.py +0 -714
- /solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js.LICENSE.txt → main.20feee82.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,129 +3,47 @@ Refactored message subscriber with improved structure and readability.
|
|
|
3
3
|
This module handles Solace message subscription and processing for evaluation.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
-
import os
|
|
7
6
|
import json
|
|
7
|
+
import logging
|
|
8
|
+
import os
|
|
8
9
|
import threading
|
|
9
10
|
import time
|
|
10
|
-
from typing import Dict, List, Optional, Any, Set, Callable
|
|
11
11
|
from dataclasses import dataclass, field
|
|
12
|
-
from
|
|
13
|
-
import logging
|
|
14
|
-
from dotenv import load_dotenv
|
|
12
|
+
from pathlib import Path
|
|
15
13
|
|
|
14
|
+
from dotenv import load_dotenv
|
|
16
15
|
from solace.messaging.messaging_service import MessagingService
|
|
17
16
|
from solace.messaging.resources.topic_subscription import TopicSubscription
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
|
|
18
|
+
from .shared import (
|
|
19
|
+
ALLOWED_TOPIC_INFIXES,
|
|
20
|
+
BLOCKED_TOPIC_INFIXES,
|
|
21
|
+
MESSAGE_TIMEOUT,
|
|
22
|
+
BrokerConfig,
|
|
23
|
+
BrokerConnectionError,
|
|
24
|
+
ConfigurationError,
|
|
25
|
+
ConnectionState,
|
|
26
|
+
MessageProcessingError,
|
|
23
27
|
)
|
|
24
28
|
|
|
25
|
-
|
|
26
|
-
logging.basicConfig(level=logging.INFO)
|
|
27
|
-
logger = logging.getLogger(__name__)
|
|
29
|
+
log = logging.getLogger(__name__)
|
|
28
30
|
|
|
29
31
|
# Load environment variables
|
|
30
32
|
load_dotenv()
|
|
31
33
|
|
|
32
34
|
|
|
33
|
-
class ConnectionState(Enum):
|
|
34
|
-
"""Represents the connection state of the subscriber."""
|
|
35
|
-
|
|
36
|
-
DISCONNECTED = "disconnected"
|
|
37
|
-
CONNECTING = "connecting"
|
|
38
|
-
CONNECTED = "connected"
|
|
39
|
-
DISCONNECTING = "disconnecting"
|
|
40
|
-
ERROR = "error"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
class SubscriberError(Exception):
|
|
44
|
-
"""Base exception for subscriber-related errors."""
|
|
45
|
-
|
|
46
|
-
pass
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
class BrokerConnectionError(SubscriberError):
|
|
50
|
-
"""Raised when broker connection fails."""
|
|
51
|
-
|
|
52
|
-
pass
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class MessageProcessingError(SubscriberError):
|
|
56
|
-
"""Raised when message processing fails."""
|
|
57
|
-
|
|
58
|
-
pass
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class ConfigurationError(SubscriberError):
|
|
62
|
-
"""Raised when configuration is invalid."""
|
|
63
|
-
|
|
64
|
-
pass
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
@dataclass
|
|
68
|
-
class BrokerConfig:
|
|
69
|
-
"""Broker connection configuration with validation."""
|
|
70
|
-
|
|
71
|
-
host: str
|
|
72
|
-
vpn_name: str
|
|
73
|
-
username: str
|
|
74
|
-
password: str
|
|
75
|
-
cert_validated: bool = False
|
|
76
|
-
connection_timeout: int = 30
|
|
77
|
-
reconnect_attempts: int = 3
|
|
78
|
-
reconnect_delay: float = 1.0
|
|
79
|
-
|
|
80
|
-
def __post_init__(self):
|
|
81
|
-
"""Validate broker configuration after initialization."""
|
|
82
|
-
if not self.host or not self.host.strip():
|
|
83
|
-
raise ConfigurationError("Broker host cannot be empty")
|
|
84
|
-
|
|
85
|
-
if not self.vpn_name or not self.vpn_name.strip():
|
|
86
|
-
raise ConfigurationError("VPN name cannot be empty")
|
|
87
|
-
|
|
88
|
-
if not self.username or not self.username.strip():
|
|
89
|
-
raise ConfigurationError("Username cannot be empty")
|
|
90
|
-
|
|
91
|
-
if not self.password or not self.password.strip():
|
|
92
|
-
raise ConfigurationError("Password cannot be empty")
|
|
93
|
-
|
|
94
|
-
if self.connection_timeout <= 0:
|
|
95
|
-
raise ConfigurationError("Connection timeout must be positive")
|
|
96
|
-
|
|
97
|
-
if self.reconnect_attempts < 0:
|
|
98
|
-
raise ConfigurationError("Reconnect attempts cannot be negative")
|
|
99
|
-
|
|
100
|
-
def to_solace_properties(self) -> Dict[str, Any]:
|
|
101
|
-
"""Convert to Solace messaging properties."""
|
|
102
|
-
return {
|
|
103
|
-
transport_layer_properties.HOST: self.host,
|
|
104
|
-
service_properties.VPN_NAME: self.vpn_name,
|
|
105
|
-
authentication_properties.SCHEME_BASIC_USER_NAME: self.username,
|
|
106
|
-
authentication_properties.SCHEME_BASIC_PASSWORD: self.password,
|
|
107
|
-
transport_layer_security_properties.CERT_VALIDATED: self.cert_validated,
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
|
|
111
35
|
@dataclass
|
|
112
36
|
class SubscriptionConfig:
|
|
113
37
|
"""Subscription configuration and topic filters."""
|
|
114
38
|
|
|
115
39
|
namespace: str
|
|
116
|
-
allowed_topic_infixes:
|
|
117
|
-
default_factory=lambda:
|
|
118
|
-
"/agent/request/",
|
|
119
|
-
"/gateway/status/",
|
|
120
|
-
"/gateway/response/",
|
|
121
|
-
]
|
|
40
|
+
allowed_topic_infixes: list[str] = field(
|
|
41
|
+
default_factory=lambda: ALLOWED_TOPIC_INFIXES
|
|
122
42
|
)
|
|
123
|
-
blocked_topic_infixes:
|
|
124
|
-
default_factory=lambda:
|
|
125
|
-
"/discovery/"
|
|
126
|
-
]
|
|
43
|
+
blocked_topic_infixes: list[str] = field(
|
|
44
|
+
default_factory=lambda: BLOCKED_TOPIC_INFIXES
|
|
127
45
|
)
|
|
128
|
-
message_timeout: int =
|
|
46
|
+
message_timeout: int = MESSAGE_TIMEOUT
|
|
129
47
|
filter_non_final_status: bool = True
|
|
130
48
|
remove_config_keys: bool = False
|
|
131
49
|
|
|
@@ -148,9 +66,7 @@ class SubscriptionConfig:
|
|
|
148
66
|
def is_topic_allowed(self, topic: str) -> bool:
|
|
149
67
|
"""Check if a topic is allowed based on configured infixes."""
|
|
150
68
|
# return any(infix in topic for infix in self.allowed_topic_infixes)
|
|
151
|
-
|
|
152
|
-
return False
|
|
153
|
-
return True
|
|
69
|
+
return not any(infix in topic for infix in self.blocked_topic_infixes)
|
|
154
70
|
|
|
155
71
|
|
|
156
72
|
@dataclass
|
|
@@ -158,11 +74,11 @@ class ProcessedMessage:
|
|
|
158
74
|
"""Structured representation of a processed message."""
|
|
159
75
|
|
|
160
76
|
topic: str
|
|
161
|
-
payload:
|
|
77
|
+
payload: any
|
|
162
78
|
timestamp: float = field(default_factory=time.time)
|
|
163
|
-
message_type:
|
|
79
|
+
message_type: str | None = None
|
|
164
80
|
|
|
165
|
-
def to_dict(self) ->
|
|
81
|
+
def to_dict(self) -> dict[str, any]:
|
|
166
82
|
"""Convert to dictionary for JSON serialization."""
|
|
167
83
|
return {
|
|
168
84
|
"topic": self.topic,
|
|
@@ -185,7 +101,7 @@ class MessageSanitizer:
|
|
|
185
101
|
"""Handles message sanitization and cleaning."""
|
|
186
102
|
|
|
187
103
|
@staticmethod
|
|
188
|
-
def remove_key_recursive(obj:
|
|
104
|
+
def remove_key_recursive(obj: any, key_to_remove: str) -> None:
|
|
189
105
|
"""
|
|
190
106
|
Recursively remove a key from nested dictionaries and lists.
|
|
191
107
|
|
|
@@ -206,10 +122,10 @@ class MessageSanitizer:
|
|
|
206
122
|
for item in obj:
|
|
207
123
|
MessageSanitizer.remove_key_recursive(item, key_to_remove)
|
|
208
124
|
except Exception as e:
|
|
209
|
-
|
|
125
|
+
log.warning(f"Error during key removal: {e}")
|
|
210
126
|
|
|
211
127
|
@staticmethod
|
|
212
|
-
def sanitize_message(payload:
|
|
128
|
+
def sanitize_message(payload: any, remove_config: bool = True) -> any:
|
|
213
129
|
"""
|
|
214
130
|
Sanitize message payload by removing unwanted keys.
|
|
215
131
|
|
|
@@ -239,7 +155,7 @@ class MessageProcessor:
|
|
|
239
155
|
self.processed_count = 0
|
|
240
156
|
self.error_count = 0
|
|
241
157
|
|
|
242
|
-
def process_message(self, inbound_message) ->
|
|
158
|
+
def process_message(self, inbound_message) -> ProcessedMessage | None:
|
|
243
159
|
"""
|
|
244
160
|
Process an inbound message and return a ProcessedMessage if valid.
|
|
245
161
|
|
|
@@ -280,10 +196,10 @@ class MessageProcessor:
|
|
|
280
196
|
|
|
281
197
|
except Exception as e:
|
|
282
198
|
self.error_count += 1
|
|
283
|
-
|
|
199
|
+
log.warning(f"Error processing message: {e}")
|
|
284
200
|
return None
|
|
285
201
|
|
|
286
|
-
def _extract_payload(self, inbound_message)
|
|
202
|
+
def _extract_payload(self, inbound_message):
|
|
287
203
|
"""Extract and parse payload from inbound message."""
|
|
288
204
|
try:
|
|
289
205
|
payload_bytes = inbound_message.get_payload_as_bytes()
|
|
@@ -300,10 +216,10 @@ class MessageProcessor:
|
|
|
300
216
|
return payload_str
|
|
301
217
|
|
|
302
218
|
except Exception as e:
|
|
303
|
-
|
|
219
|
+
log.warning(f"Error extracting payload: {e}")
|
|
304
220
|
return None
|
|
305
221
|
|
|
306
|
-
def _should_filter_status_message(self, topic: str, payload:
|
|
222
|
+
def _should_filter_status_message(self, topic: str, payload: any) -> bool:
|
|
307
223
|
"""Check if a status message should be filtered out."""
|
|
308
224
|
if not self.config.filter_non_final_status:
|
|
309
225
|
return False
|
|
@@ -325,12 +241,12 @@ class MessageProcessor:
|
|
|
325
241
|
|
|
326
242
|
return False
|
|
327
243
|
|
|
328
|
-
def _find_part_type(self, data:
|
|
244
|
+
def _find_part_type(self, data: any, type_to_find: str) -> bool:
|
|
329
245
|
"""Recursively search for a part with a specific type."""
|
|
330
246
|
if isinstance(data, dict):
|
|
331
247
|
if data.get("type") == type_to_find:
|
|
332
248
|
return True
|
|
333
|
-
for
|
|
249
|
+
for _key, value in data.items():
|
|
334
250
|
if self._find_part_type(value, type_to_find):
|
|
335
251
|
return True
|
|
336
252
|
elif isinstance(data, list):
|
|
@@ -350,7 +266,7 @@ class MessageProcessor:
|
|
|
350
266
|
else:
|
|
351
267
|
return "unknown"
|
|
352
268
|
|
|
353
|
-
def get_stats(self) ->
|
|
269
|
+
def get_stats(self) -> dict[str, int]:
|
|
354
270
|
"""Get processing statistics."""
|
|
355
271
|
return {
|
|
356
272
|
"processed_count": self.processed_count,
|
|
@@ -362,14 +278,14 @@ class TaskTracker:
|
|
|
362
278
|
"""Tracks task completion and manages active tasks."""
|
|
363
279
|
|
|
364
280
|
def __init__(
|
|
365
|
-
self, active_tasks:
|
|
281
|
+
self, active_tasks: set[str], wave_complete_event: threading.Event | None
|
|
366
282
|
):
|
|
367
283
|
self.active_tasks = active_tasks
|
|
368
284
|
self.wave_complete_event = wave_complete_event
|
|
369
|
-
self.completed_tasks:
|
|
285
|
+
self.completed_tasks: list[TaskCompletionEvent] = []
|
|
370
286
|
self._lock = threading.Lock()
|
|
371
287
|
|
|
372
|
-
def handle_task_completion(self, topic: str) ->
|
|
288
|
+
def handle_task_completion(self, topic: str) -> TaskCompletionEvent | None:
|
|
373
289
|
"""
|
|
374
290
|
Handle task completion based on topic.
|
|
375
291
|
|
|
@@ -389,7 +305,7 @@ class TaskTracker:
|
|
|
389
305
|
|
|
390
306
|
with self._lock:
|
|
391
307
|
if task_id in self.active_tasks:
|
|
392
|
-
|
|
308
|
+
log.info(f"Task {task_id} completed")
|
|
393
309
|
self.active_tasks.remove(task_id)
|
|
394
310
|
|
|
395
311
|
completion_event = TaskCompletionEvent(task_id=task_id, topic=topic)
|
|
@@ -397,17 +313,17 @@ class TaskTracker:
|
|
|
397
313
|
|
|
398
314
|
# Check if all tasks are complete
|
|
399
315
|
if not self.active_tasks and self.wave_complete_event:
|
|
400
|
-
|
|
316
|
+
log.info("All tasks completed, setting wave complete event")
|
|
401
317
|
self.wave_complete_event.set()
|
|
402
318
|
|
|
403
319
|
return completion_event
|
|
404
320
|
|
|
405
321
|
except Exception as e:
|
|
406
|
-
|
|
322
|
+
log.error(f"Error handling task completion: {e}")
|
|
407
323
|
|
|
408
324
|
return None
|
|
409
325
|
|
|
410
|
-
def _extract_task_id(self, topic: str) ->
|
|
326
|
+
def _extract_task_id(self, topic: str) -> str | None:
|
|
411
327
|
"""Extract task ID from topic."""
|
|
412
328
|
try:
|
|
413
329
|
return topic.split("/")[-1]
|
|
@@ -428,9 +344,9 @@ class TaskTracker:
|
|
|
428
344
|
class MessageStorage:
|
|
429
345
|
"""Handles message storage and file operations."""
|
|
430
346
|
|
|
431
|
-
def __init__(self, results_path: str):
|
|
432
|
-
self.results_path = results_path
|
|
433
|
-
self.messages:
|
|
347
|
+
def __init__(self, results_path: str | Path):
|
|
348
|
+
self.results_path = Path(results_path)
|
|
349
|
+
self.messages: list[ProcessedMessage] = []
|
|
434
350
|
self._lock = threading.Lock()
|
|
435
351
|
|
|
436
352
|
def add_message(self, message: ProcessedMessage) -> None:
|
|
@@ -453,25 +369,25 @@ class MessageStorage:
|
|
|
453
369
|
Returns:
|
|
454
370
|
The full path to the saved file
|
|
455
371
|
"""
|
|
456
|
-
output_file =
|
|
372
|
+
output_file = self.results_path / filename
|
|
457
373
|
|
|
458
374
|
try:
|
|
459
375
|
# Ensure directory exists
|
|
460
|
-
|
|
376
|
+
output_file.parent.mkdir(parents=True, exist_ok=True)
|
|
461
377
|
|
|
462
378
|
with self._lock:
|
|
463
379
|
# Convert messages to dictionaries for JSON serialization
|
|
464
380
|
message_dicts = [msg.to_dict() for msg in self.messages]
|
|
465
381
|
|
|
466
|
-
with open(
|
|
382
|
+
with output_file.open("w") as f:
|
|
467
383
|
json.dump(message_dicts, f, indent=4)
|
|
468
384
|
|
|
469
|
-
|
|
470
|
-
return output_file
|
|
385
|
+
log.info(f"Saved {len(message_dicts)} messages to {output_file}")
|
|
386
|
+
return str(output_file)
|
|
471
387
|
|
|
472
388
|
except Exception as e:
|
|
473
|
-
|
|
474
|
-
raise MessageProcessingError(f"Failed to save messages: {e}")
|
|
389
|
+
log.error(f"Error saving messages: {e}")
|
|
390
|
+
raise MessageProcessingError(f"Failed to save messages: {e}") from e
|
|
475
391
|
|
|
476
392
|
def clear_messages(self) -> None:
|
|
477
393
|
"""Clear all stored messages."""
|
|
@@ -484,7 +400,7 @@ class BrokerConnectionService:
|
|
|
484
400
|
|
|
485
401
|
def __init__(self, config: BrokerConfig):
|
|
486
402
|
self.config = config
|
|
487
|
-
self.messaging_service:
|
|
403
|
+
self.messaging_service: MessagingService | None = None
|
|
488
404
|
self.connection_state = ConnectionState.DISCONNECTED
|
|
489
405
|
self._connection_lock = threading.Lock()
|
|
490
406
|
|
|
@@ -492,13 +408,13 @@ class BrokerConnectionService:
|
|
|
492
408
|
"""Connect to the Solace broker."""
|
|
493
409
|
with self._connection_lock:
|
|
494
410
|
if self.connection_state == ConnectionState.CONNECTED:
|
|
495
|
-
|
|
411
|
+
log.warning("Already connected to broker")
|
|
496
412
|
return
|
|
497
413
|
|
|
498
414
|
self.connection_state = ConnectionState.CONNECTING
|
|
499
415
|
|
|
500
416
|
try:
|
|
501
|
-
|
|
417
|
+
log.info("Connecting to Solace PubSub+ Broker...")
|
|
502
418
|
|
|
503
419
|
broker_props = self.config.to_solace_properties()
|
|
504
420
|
self.messaging_service = (
|
|
@@ -507,37 +423,37 @@ class BrokerConnectionService:
|
|
|
507
423
|
self.messaging_service.connect()
|
|
508
424
|
|
|
509
425
|
self.connection_state = ConnectionState.CONNECTED
|
|
510
|
-
|
|
426
|
+
log.info("Successfully connected to broker")
|
|
511
427
|
|
|
512
428
|
except Exception as e:
|
|
513
429
|
self.connection_state = ConnectionState.ERROR
|
|
514
|
-
|
|
515
|
-
raise BrokerConnectionError(f"Connection failed: {e}")
|
|
430
|
+
log.error(f"Failed to connect to broker: {e}")
|
|
431
|
+
raise BrokerConnectionError(f"Connection failed: {e}") from e
|
|
516
432
|
|
|
517
433
|
def disconnect(self) -> None:
|
|
518
434
|
"""Disconnect from the Solace broker."""
|
|
519
435
|
with self._connection_lock:
|
|
520
436
|
if self.connection_state == ConnectionState.DISCONNECTED:
|
|
521
|
-
|
|
437
|
+
log.warning("Already disconnected from broker")
|
|
522
438
|
return
|
|
523
439
|
|
|
524
440
|
self.connection_state = ConnectionState.DISCONNECTING
|
|
525
441
|
|
|
526
442
|
try:
|
|
527
443
|
if self.messaging_service:
|
|
528
|
-
|
|
444
|
+
log.info("Disconnecting from broker...")
|
|
529
445
|
self.messaging_service.disconnect()
|
|
530
446
|
self.messaging_service = None
|
|
531
447
|
|
|
532
448
|
self.connection_state = ConnectionState.DISCONNECTED
|
|
533
|
-
|
|
449
|
+
log.info("Successfully disconnected from broker")
|
|
534
450
|
|
|
535
451
|
except Exception as e:
|
|
536
452
|
self.connection_state = ConnectionState.ERROR
|
|
537
|
-
|
|
538
|
-
raise BrokerConnectionError(f"Disconnect failed: {e}")
|
|
453
|
+
log.error(f"Error during disconnect: {e}")
|
|
454
|
+
raise BrokerConnectionError(f"Disconnect failed: {e}") from e
|
|
539
455
|
|
|
540
|
-
def get_messaging_service(self) ->
|
|
456
|
+
def get_messaging_service(self) -> MessagingService | None:
|
|
541
457
|
"""Get the messaging service instance."""
|
|
542
458
|
return self.messaging_service
|
|
543
459
|
|
|
@@ -563,12 +479,12 @@ class SubscriptionManager:
|
|
|
563
479
|
self._receiver_lock = threading.Lock()
|
|
564
480
|
|
|
565
481
|
def start_subscription(
|
|
566
|
-
self, subscription_ready_event:
|
|
482
|
+
self, subscription_ready_event: threading.Event | None = None
|
|
567
483
|
) -> None:
|
|
568
484
|
"""Start message subscription."""
|
|
569
485
|
with self._receiver_lock:
|
|
570
486
|
if self.subscription_active:
|
|
571
|
-
|
|
487
|
+
log.warning("Subscription already active")
|
|
572
488
|
return
|
|
573
489
|
|
|
574
490
|
if not self.connection_service.is_connected():
|
|
@@ -592,17 +508,17 @@ class SubscriptionManager:
|
|
|
592
508
|
self.message_receiver.add_subscription(subscription)
|
|
593
509
|
|
|
594
510
|
self.subscription_active = True
|
|
595
|
-
|
|
511
|
+
log.info(f"Started subscription to: {self.config.topic_pattern}")
|
|
596
512
|
|
|
597
513
|
# Signal that subscription is ready
|
|
598
514
|
if subscription_ready_event:
|
|
599
515
|
subscription_ready_event.set()
|
|
600
516
|
|
|
601
517
|
except Exception as e:
|
|
602
|
-
|
|
603
|
-
raise BrokerConnectionError(f"Subscription failed: {e}")
|
|
518
|
+
log.error(f"Failed to start subscription: {e}")
|
|
519
|
+
raise BrokerConnectionError(f"Subscription failed: {e}") from e
|
|
604
520
|
|
|
605
|
-
def receive_message(self, timeout:
|
|
521
|
+
def receive_message(self, timeout: int | None = None):
|
|
606
522
|
"""
|
|
607
523
|
Receive a message from the subscription.
|
|
608
524
|
|
|
@@ -620,7 +536,7 @@ class SubscriptionManager:
|
|
|
620
536
|
try:
|
|
621
537
|
return self.message_receiver.receive_message(timeout=timeout_ms)
|
|
622
538
|
except Exception as e:
|
|
623
|
-
|
|
539
|
+
log.warning(f"Error receiving message: {e}")
|
|
624
540
|
return None
|
|
625
541
|
|
|
626
542
|
def stop_subscription(self) -> None:
|
|
@@ -631,51 +547,51 @@ class SubscriptionManager:
|
|
|
631
547
|
|
|
632
548
|
try:
|
|
633
549
|
if self.message_receiver:
|
|
634
|
-
|
|
550
|
+
log.info("Stopping message receiver...")
|
|
635
551
|
self.message_receiver.terminate()
|
|
636
552
|
self.message_receiver = None
|
|
637
553
|
|
|
638
554
|
self.subscription_active = False
|
|
639
|
-
|
|
555
|
+
log.info("Subscription stopped")
|
|
640
556
|
|
|
641
557
|
except Exception as e:
|
|
642
|
-
|
|
558
|
+
log.error(f"Error stopping subscription: {e}")
|
|
643
559
|
|
|
644
560
|
def is_active(self) -> bool:
|
|
645
561
|
"""Check if subscription is active."""
|
|
646
562
|
return self.subscription_active
|
|
647
563
|
|
|
648
564
|
|
|
649
|
-
class
|
|
565
|
+
class Subscriber(threading.Thread):
|
|
650
566
|
"""
|
|
651
567
|
Main message subscriber class that orchestrates all components.
|
|
652
|
-
|
|
653
568
|
This is the refactored version of the original Subscriber class,
|
|
654
569
|
maintaining the same interface while providing better structure.
|
|
655
570
|
"""
|
|
656
571
|
|
|
657
572
|
def __init__(
|
|
658
573
|
self,
|
|
574
|
+
broker_config: BrokerConfig,
|
|
659
575
|
namespace: str,
|
|
660
|
-
active_tasks:
|
|
661
|
-
wave_complete_event:
|
|
662
|
-
subscription_ready_event:
|
|
576
|
+
active_tasks: set[str],
|
|
577
|
+
wave_complete_event: threading.Event | None,
|
|
578
|
+
subscription_ready_event: threading.Event | None,
|
|
663
579
|
results_path: str,
|
|
664
580
|
):
|
|
665
581
|
"""
|
|
666
582
|
Initialize the message subscriber.
|
|
667
|
-
|
|
668
583
|
Args:
|
|
584
|
+
broker_config: The broker configuration object
|
|
669
585
|
namespace: The namespace for topic subscription
|
|
670
586
|
active_tasks: Set of active task IDs to track
|
|
671
587
|
wave_complete_event: Event to set when all tasks complete
|
|
672
588
|
subscription_ready_event: Event to set when subscription is ready
|
|
673
589
|
results_path: Path to save results
|
|
674
590
|
"""
|
|
675
|
-
super().__init__(name="
|
|
591
|
+
super().__init__(name="Subscriber")
|
|
676
592
|
|
|
677
593
|
# Initialize configuration
|
|
678
|
-
self.broker_config =
|
|
594
|
+
self.broker_config = broker_config
|
|
679
595
|
self.subscription_config = SubscriptionConfig(namespace=namespace)
|
|
680
596
|
|
|
681
597
|
# Initialize services
|
|
@@ -696,24 +612,11 @@ class MessageSubscriber(threading.Thread):
|
|
|
696
612
|
self.messages_received = 0
|
|
697
613
|
self.messages_processed = 0
|
|
698
614
|
|
|
699
|
-
def _create_broker_config(self) -> BrokerConfig:
|
|
700
|
-
"""Create broker configuration from environment variables."""
|
|
701
|
-
try:
|
|
702
|
-
return BrokerConfig(
|
|
703
|
-
host=os.environ.get("SOLACE_BROKER_URL", ""),
|
|
704
|
-
vpn_name=os.environ.get("SOLACE_BROKER_VPN", ""),
|
|
705
|
-
username=os.environ.get("SOLACE_BROKER_USERNAME", ""),
|
|
706
|
-
password=os.environ.get("SOLACE_BROKER_PASSWORD", ""),
|
|
707
|
-
)
|
|
708
|
-
except ConfigurationError as e:
|
|
709
|
-
logger.error(f"Invalid broker configuration: {e}")
|
|
710
|
-
raise
|
|
711
|
-
|
|
712
615
|
def run(self) -> None:
|
|
713
616
|
"""Main thread execution method."""
|
|
714
617
|
try:
|
|
715
618
|
self._running = True
|
|
716
|
-
|
|
619
|
+
log.info("Starting message subscriber...")
|
|
717
620
|
|
|
718
621
|
# Connect to broker
|
|
719
622
|
self.connection_service.connect()
|
|
@@ -725,13 +628,13 @@ class MessageSubscriber(threading.Thread):
|
|
|
725
628
|
self._message_processing_loop()
|
|
726
629
|
|
|
727
630
|
except Exception as e:
|
|
728
|
-
|
|
631
|
+
log.error(f"Error in subscriber thread: {e}")
|
|
729
632
|
finally:
|
|
730
633
|
self._cleanup()
|
|
731
634
|
|
|
732
635
|
def _message_processing_loop(self) -> None:
|
|
733
636
|
"""Main message processing loop."""
|
|
734
|
-
|
|
637
|
+
log.info("Starting message processing loop...")
|
|
735
638
|
|
|
736
639
|
while self._running:
|
|
737
640
|
try:
|
|
@@ -744,7 +647,7 @@ class MessageSubscriber(threading.Thread):
|
|
|
744
647
|
|
|
745
648
|
except Exception as e:
|
|
746
649
|
if self._running:
|
|
747
|
-
|
|
650
|
+
log.error(f"Error in message processing loop: {e}")
|
|
748
651
|
# Continue processing other messages
|
|
749
652
|
continue
|
|
750
653
|
|
|
@@ -764,11 +667,11 @@ class MessageSubscriber(threading.Thread):
|
|
|
764
667
|
self.task_tracker.handle_task_completion(processed_message.topic)
|
|
765
668
|
|
|
766
669
|
except Exception as e:
|
|
767
|
-
|
|
670
|
+
log.warning(f"Error handling message: {e}")
|
|
768
671
|
|
|
769
672
|
def stop(self) -> None:
|
|
770
673
|
"""Stop the subscriber and clean up resources."""
|
|
771
|
-
|
|
674
|
+
log.info("Stopping message subscriber...")
|
|
772
675
|
self._running = False
|
|
773
676
|
|
|
774
677
|
def _cleanup(self) -> None:
|
|
@@ -787,68 +690,37 @@ class MessageSubscriber(threading.Thread):
|
|
|
787
690
|
self._log_final_statistics()
|
|
788
691
|
|
|
789
692
|
except Exception as e:
|
|
790
|
-
|
|
693
|
+
log.error(f"Error during cleanup: {e}")
|
|
791
694
|
|
|
792
695
|
def _log_final_statistics(self) -> None:
|
|
793
696
|
"""Log final processing statistics."""
|
|
794
697
|
runtime = time.time() - self.start_time
|
|
795
698
|
processor_stats = self.message_processor.get_stats()
|
|
796
699
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
700
|
+
log.info("=== SUBSCRIBER STATISTICS ===")
|
|
701
|
+
log.info(f"Runtime: {runtime:.2f} seconds")
|
|
702
|
+
log.info(f"Messages received: {self.messages_received}")
|
|
703
|
+
log.info(f"Messages processed: {self.messages_processed}")
|
|
704
|
+
log.info(f"Messages stored: {self.message_storage.get_message_count()}")
|
|
705
|
+
log.info(f"Processing errors: {processor_stats['error_count']}")
|
|
706
|
+
log.info(
|
|
804
707
|
f"Active tasks remaining: {self.task_tracker.get_active_task_count()}"
|
|
805
708
|
)
|
|
806
|
-
|
|
807
|
-
|
|
709
|
+
log.info(f"Tasks completed: {self.task_tracker.get_completed_task_count()}")
|
|
710
|
+
log.info("=============================")
|
|
808
711
|
|
|
809
712
|
# Backward compatibility properties
|
|
810
713
|
@property
|
|
811
|
-
def active_tasks(self) ->
|
|
714
|
+
def active_tasks(self) -> set[str]:
|
|
812
715
|
"""Get active tasks set for backward compatibility."""
|
|
813
716
|
return self.task_tracker.active_tasks
|
|
814
717
|
|
|
815
718
|
@property
|
|
816
|
-
def messages(self) ->
|
|
719
|
+
def messages(self) -> list[dict[str, any]]:
|
|
817
720
|
"""Get messages list for backward compatibility."""
|
|
818
721
|
return [msg.to_dict() for msg in self.message_storage.messages]
|
|
819
722
|
|
|
820
723
|
|
|
821
|
-
# Backward compatibility alias
|
|
822
|
-
Subscriber = MessageSubscriber
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
def create_subscriber_from_env(
|
|
826
|
-
namespace: str,
|
|
827
|
-
active_tasks: Set[str],
|
|
828
|
-
wave_complete_event: Optional[threading.Event] = None,
|
|
829
|
-
subscription_ready_event: Optional[threading.Event] = None,
|
|
830
|
-
results_path: str = ".",
|
|
831
|
-
) -> MessageSubscriber:
|
|
832
|
-
"""
|
|
833
|
-
Factory function to create a subscriber with environment-based configuration.
|
|
834
|
-
|
|
835
|
-
Args:
|
|
836
|
-
namespace: The namespace for topic subscription
|
|
837
|
-
active_tasks: Set of active task IDs to track
|
|
838
|
-
wave_complete_event: Event to set when all tasks complete
|
|
839
|
-
subscription_ready_event: Event to set when subscription is ready
|
|
840
|
-
results_path: Path to save results
|
|
841
|
-
|
|
842
|
-
Returns:
|
|
843
|
-
Configured MessageSubscriber instance
|
|
844
|
-
"""
|
|
845
|
-
return MessageSubscriber(
|
|
846
|
-
namespace=namespace,
|
|
847
|
-
active_tasks=active_tasks,
|
|
848
|
-
wave_complete_event=wave_complete_event,
|
|
849
|
-
subscription_ready_event=subscription_ready_event,
|
|
850
|
-
results_path=results_path,
|
|
851
|
-
)
|
|
852
724
|
|
|
853
725
|
|
|
854
726
|
def main():
|
|
@@ -858,7 +730,7 @@ def main():
|
|
|
858
730
|
|
|
859
731
|
# Set up signal handling for graceful shutdown
|
|
860
732
|
def signal_handler(signum, frame):
|
|
861
|
-
|
|
733
|
+
log.info("\nShutting down subscriber...")
|
|
862
734
|
if "subscriber" in locals():
|
|
863
735
|
subscriber.stop()
|
|
864
736
|
subscriber.join()
|
|
@@ -872,7 +744,14 @@ def main():
|
|
|
872
744
|
subscription_ready = threading.Event()
|
|
873
745
|
|
|
874
746
|
try:
|
|
875
|
-
|
|
747
|
+
broker_config = BrokerConfig(
|
|
748
|
+
host=os.environ.get("SOLACE_BROKER_URL", ""),
|
|
749
|
+
vpn_name=os.environ.get("SOLACE_BROKER_VPN", ""),
|
|
750
|
+
username=os.environ.get("SOLACE_BROKER_USERNAME", ""),
|
|
751
|
+
password=os.environ.get("SOLACE_BROKER_PASSWORD", ""),
|
|
752
|
+
)
|
|
753
|
+
subscriber = Subscriber(
|
|
754
|
+
broker_config=broker_config,
|
|
876
755
|
namespace="test",
|
|
877
756
|
active_tasks=active_tasks,
|
|
878
757
|
subscription_ready_event=subscription_ready,
|
|
@@ -883,13 +762,13 @@ def main():
|
|
|
883
762
|
|
|
884
763
|
# Wait for subscription to be ready
|
|
885
764
|
subscription_ready.wait(timeout=30)
|
|
886
|
-
|
|
765
|
+
log.info("Subscriber is ready and running...")
|
|
887
766
|
|
|
888
767
|
# Keep running until interrupted
|
|
889
768
|
subscriber.join()
|
|
890
769
|
|
|
891
770
|
except Exception as e:
|
|
892
|
-
|
|
771
|
+
log.error(f"Error running subscriber: {e}")
|
|
893
772
|
sys.exit(1)
|
|
894
773
|
|
|
895
774
|
|