stinger-python-utils 0.1.9__tar.gz → 0.2.0__tar.gz

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.
Files changed (25) hide show
  1. stinger_python_utils-0.2.0/.agents/skills/release/SKILL.md +67 -0
  2. stinger_python_utils-0.2.0/.github/workflows/python-tests.yml +30 -0
  3. stinger_python_utils-0.2.0/.vscode/settings.json +10 -0
  4. stinger_python_utils-0.2.0/PKG-INFO +84 -0
  5. stinger_python_utils-0.2.0/README.md +73 -0
  6. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/pyproject.toml +2 -12
  7. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/src/stinger_python_utils/message_creator.py +152 -23
  8. stinger_python_utils-0.2.0/tests/test_message_creator.py +611 -0
  9. stinger_python_utils-0.2.0/uv.lock +1603 -0
  10. stinger_python_utils-0.1.9/.github/workflows/python-tests.yml +0 -51
  11. stinger_python_utils-0.1.9/.vscode/settings.json +0 -4
  12. stinger_python_utils-0.1.9/PKG-INFO +0 -276
  13. stinger_python_utils-0.1.9/README.md +0 -261
  14. stinger_python_utils-0.1.9/src/stinger_python_utils/mcp/__init__.py +0 -20
  15. stinger_python_utils-0.1.9/src/stinger_python_utils/mcp/__main__.py +0 -58
  16. stinger_python_utils-0.1.9/src/stinger_python_utils/mcp/plugin.py +0 -220
  17. stinger_python_utils-0.1.9/src/stinger_python_utils/mcp/server.py +0 -633
  18. stinger_python_utils-0.1.9/uv.lock +0 -2052
  19. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/.github/workflows/python37.yml +0 -0
  20. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/.gitignore +0 -0
  21. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/LICENSE +0 -0
  22. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/src/stinger_python_utils/__init__.py +0 -0
  23. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/src/stinger_python_utils/return_codes.py +0 -0
  24. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/test/__init__.py +0 -0
  25. {stinger_python_utils-0.1.9 → stinger_python_utils-0.2.0}/test/test_message_creator.py +0 -0
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: release
3
+ description: "Release stinger-python-utils. Use when: releasing a new version, cutting a release, bumping the version, publishing, shipping, preparing a release, running pre-release checks."
4
+ argument-hint: "optional notes or release context"
5
+ ---
6
+
7
+ # Release stinger-python-utils
8
+
9
+ Runs pre-release checks, bumps the version in `pyproject.toml`, then offers to commit and push.
10
+
11
+ ## Procedure
12
+
13
+ ### 1. Run mypy
14
+
15
+ ```
16
+ uv run mypy src/ tests/
17
+ ```
18
+
19
+ If mypy reports errors, fix them before continuing. Do not proceed until mypy is clean.
20
+
21
+ ### 2. Run pytest
22
+
23
+ ```
24
+ uv run pytest
25
+ ```
26
+
27
+ If any tests fail, fix them before continuing. Do not proceed until all tests pass.
28
+
29
+ ### 3. Bump the version
30
+
31
+ Read the current version from `pyproject.toml`. Then ask the user:
32
+
33
+ > The current version is `X.Y.Z`. How would you like to bump it?
34
+ > - **patch** → `X.Y.Z+1`
35
+ > - **minor** → `X.Y+1.0`
36
+ > - **major** → `X+1.0.0`
37
+ > - **manual** → enter a custom version string
38
+
39
+ Apply the chosen version to the `version` field in `pyproject.toml`.
40
+
41
+ ### 4. Confirm commit and push
42
+
43
+ Ask the user:
44
+
45
+ > Ready to commit and push `vX.Y.Z` to `origin/main`?
46
+ > - **Yes** — stage `pyproject.toml`, commit with message `chore: bump version to X.Y.Z`, then push to `origin main`
47
+ > - **No** — stop here; leave the version bump in place for the user to handle manually
48
+
49
+ Only run `git add`, `git commit`, and `git push` if the user explicitly confirms.
50
+
51
+ ### 5. Build
52
+
53
+ ```
54
+ uv build
55
+ ```
56
+
57
+ If the build fails, report the error and stop. Do not proceed to publish.
58
+
59
+ ### 6. Confirm publish
60
+
61
+ Ask the user:
62
+
63
+ > Build succeeded. Publish `vX.Y.Z` to PyPI with `uv publish`?
64
+ > - **Yes** — run `uv publish`
65
+ > - **No** — stop here; the built artifacts are in `dist/`
66
+
67
+ Only run `uv publish` if the user explicitly confirms.
@@ -0,0 +1,30 @@
1
+ name: Python Tests
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ pull_request:
7
+ branches: [ main ]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ # Python 3.7 requires a completely different setup because it is very EOL.
15
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - name: Install UV
19
+ uses: astral-sh/setup-uv@v7
20
+ with:
21
+ python-version: ${{ matrix.python-version }}
22
+ - name: Install project
23
+ run: |
24
+ uv sync --locked --dev
25
+ - name: Run unit tests
26
+ run: |
27
+ uv run pytest
28
+ - name: Mypy Type Check
29
+ run: |
30
+ uv run mypy --check-untyped-defs ./src/
@@ -0,0 +1,10 @@
1
+ {
2
+ "python-envs.defaultEnvManager": "ms-python.python:system",
3
+ "python-envs.pythonProjects": [],
4
+ "chat.tools.terminal.autoApprove": {
5
+ "/^uv run mypy src/ tests/$/": {
6
+ "approve": true,
7
+ "matchCommandLine": true
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,84 @@
1
+ Metadata-Version: 2.5
2
+ Name: stinger-python-utils
3
+ Version: 0.2.0
4
+ Summary: Common utilities for Stinger Python services.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Requires-Python: >=3.7
8
+ Requires-Dist: pydantic>=2.5.3
9
+ Requires-Dist: pyqttier>=0.2.0
10
+ Description-Content-Type: text/markdown
11
+
12
+ # stinger-python-utils
13
+
14
+ Shared utilities for Stinger Python services, providing convenient message creation for MQTT communication.
15
+
16
+
17
+
18
+ ## Installation
19
+
20
+ ```bash
21
+ uv add stinger-python-utils
22
+ ```
23
+
24
+ ## MessageCreator
25
+
26
+ `MessageCreator` is a utility class for creating MQTT messages with standardized properties and payloads.
27
+
28
+ ### Basic Usage
29
+
30
+ ```python
31
+ from pydantic import BaseModel
32
+ from stinger_python_utils.message_creator import MessageCreator
33
+
34
+ class MyPayload(BaseModel):
35
+ name: str
36
+ value: int
37
+
38
+ payload = MyPayload(name="test", value=42)
39
+ message = MessageCreator.signal_message("my/topic", payload)
40
+ ```
41
+
42
+ ### Methods
43
+
44
+ | Method | Purpose | Return Code |
45
+ |--------|---------|-------------|
46
+ | `signal_message(topic, payload)` | Send a signal with one-time delivery | QoS 1, no retain |
47
+ | `status_message(topic, payload, expiry_seconds)` | Send status that expires | QoS 1, retained, with expiry |
48
+ | `error_response_message(topic, return_code, correlation_id, debug_info)` | Error response to a request | QoS 1, user properties: `ReturnCode` |
49
+ | `response_message(topic, payload, return_code, correlation_id)` | Successful response to a request | QoS 1, user properties: `ReturnCode` |
50
+ | `property_state_message(topic, payload, state_version)` | Publish property state | QoS 1, retained, JSON content type |
51
+ | `property_update_request_message(topic, payload, version, response_topic, correlation_id)` | Request property update | QoS 1, user property: `PropertyVersion` |
52
+ | `property_response_message(topic, payload, version, return_code, correlation_id, debug_info)` | Respond to property update | QoS 1, user properties: `ReturnCode`, `PropertyVersion` |
53
+ | `request_message(topic, payload, response_topic, correlation_id)` | Send a request (auto-generates UUID if no correlation_id) | QoS 1, auto correlation ID |
54
+
55
+ ### Example: Request/Response Pattern
56
+
57
+ ```python
58
+ from pydantic import BaseModel
59
+ from stinger_python_utils.message_creator import MessageCreator
60
+
61
+ class Request(BaseModel):
62
+ action: str
63
+
64
+ request = Request(action="start")
65
+ msg = MessageCreator.request_message(
66
+ "devices/cmd",
67
+ request,
68
+ response_topic="devices/response"
69
+ )
70
+ # Returns a Message with auto-generated correlation ID
71
+ ```
72
+
73
+ ### Example: Error Response
74
+
75
+ ```python
76
+ msg = MessageCreator.error_response_message(
77
+ "devices/response",
78
+ return_code=500,
79
+ correlation_id="req-123",
80
+ debug_info="Device not found"
81
+ )
82
+ # User properties include: ReturnCode=500, DebugInfo=Device not found
83
+ ```
84
+
@@ -0,0 +1,73 @@
1
+ # stinger-python-utils
2
+
3
+ Shared utilities for Stinger Python services, providing convenient message creation for MQTT communication.
4
+
5
+
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ uv add stinger-python-utils
11
+ ```
12
+
13
+ ## MessageCreator
14
+
15
+ `MessageCreator` is a utility class for creating MQTT messages with standardized properties and payloads.
16
+
17
+ ### Basic Usage
18
+
19
+ ```python
20
+ from pydantic import BaseModel
21
+ from stinger_python_utils.message_creator import MessageCreator
22
+
23
+ class MyPayload(BaseModel):
24
+ name: str
25
+ value: int
26
+
27
+ payload = MyPayload(name="test", value=42)
28
+ message = MessageCreator.signal_message("my/topic", payload)
29
+ ```
30
+
31
+ ### Methods
32
+
33
+ | Method | Purpose | Return Code |
34
+ |--------|---------|-------------|
35
+ | `signal_message(topic, payload)` | Send a signal with one-time delivery | QoS 1, no retain |
36
+ | `status_message(topic, payload, expiry_seconds)` | Send status that expires | QoS 1, retained, with expiry |
37
+ | `error_response_message(topic, return_code, correlation_id, debug_info)` | Error response to a request | QoS 1, user properties: `ReturnCode` |
38
+ | `response_message(topic, payload, return_code, correlation_id)` | Successful response to a request | QoS 1, user properties: `ReturnCode` |
39
+ | `property_state_message(topic, payload, state_version)` | Publish property state | QoS 1, retained, JSON content type |
40
+ | `property_update_request_message(topic, payload, version, response_topic, correlation_id)` | Request property update | QoS 1, user property: `PropertyVersion` |
41
+ | `property_response_message(topic, payload, version, return_code, correlation_id, debug_info)` | Respond to property update | QoS 1, user properties: `ReturnCode`, `PropertyVersion` |
42
+ | `request_message(topic, payload, response_topic, correlation_id)` | Send a request (auto-generates UUID if no correlation_id) | QoS 1, auto correlation ID |
43
+
44
+ ### Example: Request/Response Pattern
45
+
46
+ ```python
47
+ from pydantic import BaseModel
48
+ from stinger_python_utils.message_creator import MessageCreator
49
+
50
+ class Request(BaseModel):
51
+ action: str
52
+
53
+ request = Request(action="start")
54
+ msg = MessageCreator.request_message(
55
+ "devices/cmd",
56
+ request,
57
+ response_topic="devices/response"
58
+ )
59
+ # Returns a Message with auto-generated correlation ID
60
+ ```
61
+
62
+ ### Example: Error Response
63
+
64
+ ```python
65
+ msg = MessageCreator.error_response_message(
66
+ "devices/response",
67
+ return_code=500,
68
+ correlation_id="req-123",
69
+ debug_info="Device not found"
70
+ )
71
+ # User properties include: ReturnCode=500, DebugInfo=Device not found
72
+ ```
73
+
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "stinger-python-utils"
7
- version = "0.1.9"
7
+ version = "0.2.0"
8
8
  description = "Common utilities for Stinger Python services."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -15,16 +15,6 @@ dependencies = [
15
15
  "pyqttier>=0.2.0",
16
16
  ]
17
17
 
18
- [project.optional-dependencies]
19
- mcp = [
20
- "mcp>=1.27.0; python_version >= '3.10'",
21
- "stevedore>=5.7; python_version >= '3.10'",
22
- "uvicorn>=0.44.0; python_version >= '3.10'",
23
- ]
24
-
25
- [project.scripts]
26
- stinger-mcp-server = "stinger_python_utils.mcp.__main__:main"
27
-
28
18
  [tool.uv]
29
19
  managed = true
30
20
 
@@ -32,5 +22,5 @@ managed = true
32
22
  dev = [
33
23
  "black>=23.3.0",
34
24
  "mypy>=1.4.1",
35
- "pytest>=7.0.0",
25
+ "pytest>=7.0.0; python_version >= '3.10'",
36
26
  ]
@@ -16,27 +16,73 @@ class MessageCreator:
16
16
 
17
17
  @classmethod
18
18
  def signal_message(cls, topic: str, payload: BaseModel) -> Message:
19
+ return cls.binary_signal_message(
20
+ topic,
21
+ payload.model_dump_json(by_alias=True).encode("utf-8"),
22
+ "application/json",
23
+ )
24
+
25
+ @classmethod
26
+ def binary_signal_message(cls, topic: str, payload: bytes, content_type: str) -> Message:
19
27
  cls._validate_topic(topic)
20
28
  return Message(
21
29
  topic=topic,
22
- payload=payload.model_dump_json(by_alias=True).encode("utf-8"),
30
+ payload=payload,
23
31
  qos=1,
24
32
  retain=False,
25
- content_type="application/json",
33
+ content_type=content_type,
34
+ )
35
+
36
+ @classmethod
37
+ def command_message(cls, topic: str, payload: BaseModel, qos: int = 2) -> Message:
38
+ return cls.binary_command_message(
39
+ topic,
40
+ payload.model_dump_json(by_alias=True).encode("utf-8"),
41
+ "application/json",
42
+ qos=qos,
43
+ )
44
+
45
+ @classmethod
46
+ def binary_command_message(
47
+ cls, topic: str, payload: bytes, content_type: str, qos: int = 2
48
+ ) -> Message:
49
+ """
50
+ A command is a signal in reverse: the client publishes it to the server and never
51
+ learns whether it was acted on. There is therefore no response topic and no
52
+ correlation data on the message.
53
+ """
54
+ cls._validate_topic(topic)
55
+ return Message(
56
+ topic=topic,
57
+ payload=payload,
58
+ qos=qos,
59
+ retain=False,
60
+ content_type=content_type,
26
61
  )
27
62
 
28
63
  @classmethod
29
64
  def status_message(
30
- cls, topic, status_message: BaseModel, expiry_seconds: int
65
+ cls, topic: str, status_message: BaseModel, expiry_seconds: int
66
+ ) -> Message:
67
+ return cls.binary_status_message(
68
+ topic,
69
+ status_message.model_dump_json(by_alias=True).encode("utf-8"),
70
+ "application/json",
71
+ expiry_seconds=expiry_seconds,
72
+ )
73
+
74
+ @classmethod
75
+ def binary_status_message(
76
+ cls, topic: str, payload: bytes, content_type: str, expiry_seconds: int
31
77
  ) -> Message:
32
78
  cls._validate_topic(topic)
33
79
  return Message(
34
80
  topic=topic,
35
- payload=status_message.model_dump_json(by_alias=True).encode("utf-8"),
81
+ payload=payload,
36
82
  qos=1,
37
83
  retain=True,
38
84
  message_expiry_interval=expiry_seconds,
39
- content_type="application/json",
85
+ content_type=content_type,
40
86
  )
41
87
 
42
88
  @classmethod
@@ -46,9 +92,14 @@ class MessageCreator:
46
92
  return_code: Union[int, MethodReturnCode],
47
93
  correlation_id: Union[str, bytes, None] = None,
48
94
  debug_info: Optional[str] = None,
95
+ content_type: str = "application/json",
49
96
  ) -> Message:
50
97
  """
51
98
  This could be used for a response to a request, but where there was an error fulfilling the request.
99
+
100
+ An error carries no data -- the return code and any debug info travel in MQTT user
101
+ properties -- so the body is an empty JSON object for a JSON response and empty
102
+ bytes for any other content type, since a protobuf consumer would reject "{}".
52
103
  """
53
104
  cls._validate_topic(topic)
54
105
  rc = (
@@ -58,7 +109,7 @@ class MessageCreator:
58
109
  )
59
110
  msg_obj = Message(
60
111
  topic=topic,
61
- payload=b"{}",
112
+ payload=b"{}" if content_type == "application/json" else b"",
62
113
  qos=1,
63
114
  retain=False,
64
115
  correlation_data=(
@@ -67,7 +118,7 @@ class MessageCreator:
67
118
  else correlation_id
68
119
  ),
69
120
  user_properties={"ReturnCode": str(rc)},
70
- content_type="application/json",
121
+ content_type=content_type,
71
122
  )
72
123
  if (
73
124
  debug_info is not None and msg_obj.user_properties is not None
@@ -86,19 +137,32 @@ class MessageCreator:
86
137
  """
87
138
  This could be used for a successful response to a request.
88
139
  """
89
- cls._validate_topic(response_topic, "response_topic")
90
140
  if isinstance(response_obj, BaseModel):
91
141
  payload = response_obj.model_dump_json(by_alias=True).encode("utf-8")
92
142
  elif isinstance(response_obj, str):
93
143
  payload = response_obj.encode("utf-8")
94
144
  else:
95
145
  payload = response_obj
146
+ return cls.binary_response_message(
147
+ response_topic, payload, "application/json", return_code, correlation_id
148
+ )
149
+
150
+ @classmethod
151
+ def binary_response_message(
152
+ cls,
153
+ response_topic: str,
154
+ payload: bytes,
155
+ content_type: str,
156
+ return_code: Union[int, MethodReturnCode],
157
+ correlation_id: Union[str, bytes, None] = None,
158
+ ) -> Message:
159
+ cls._validate_topic(response_topic, "response_topic")
96
160
  rc = (
97
161
  return_code.value
98
162
  if isinstance(return_code, MethodReturnCode)
99
163
  else return_code
100
164
  )
101
- msg_obj = Message(
165
+ return Message(
102
166
  topic=response_topic,
103
167
  payload=payload,
104
168
  qos=1,
@@ -109,9 +173,8 @@ class MessageCreator:
109
173
  else correlation_id
110
174
  ),
111
175
  user_properties={"ReturnCode": str(rc)},
112
- content_type="application/json",
176
+ content_type=content_type,
113
177
  )
114
- return msg_obj
115
178
 
116
179
  @classmethod
117
180
  def property_state_message(
@@ -120,13 +183,24 @@ class MessageCreator:
120
183
  """
121
184
  Creates a retained message representing the state/value of a property.
122
185
  """
186
+ return cls.binary_property_state_message(
187
+ topic,
188
+ state_obj.model_dump_json(by_alias=True).encode("utf-8"),
189
+ "application/json",
190
+ state_version,
191
+ )
192
+
193
+ @classmethod
194
+ def binary_property_state_message(
195
+ cls, topic: str, payload: bytes, content_type: str, state_version: Optional[int] = None
196
+ ) -> Message:
123
197
  cls._validate_topic(topic)
124
198
  msg_obj = Message(
125
199
  topic=topic,
126
- payload=state_obj.model_dump_json(by_alias=True).encode("utf-8"),
200
+ payload=payload,
127
201
  qos=1,
128
202
  retain=True,
129
- content_type="application/json",
203
+ content_type=content_type,
130
204
  )
131
205
  if state_version is not None:
132
206
  msg_obj.user_properties = {"PropertyVersion": str(state_version)}
@@ -144,14 +218,33 @@ class MessageCreator:
144
218
  """
145
219
  Creates a message representing a request to update a property.
146
220
  """
221
+ return cls.binary_property_update_request_message(
222
+ topic,
223
+ property_obj.model_dump_json(by_alias=True).encode("utf-8"),
224
+ "application/json",
225
+ version,
226
+ response_topic,
227
+ correlation_id,
228
+ )
229
+
230
+ @classmethod
231
+ def binary_property_update_request_message(
232
+ cls,
233
+ topic: str,
234
+ payload: bytes,
235
+ content_type: str,
236
+ version: str,
237
+ response_topic: str,
238
+ correlation_id: Union[str, bytes, None] = None,
239
+ ) -> Message:
147
240
  cls._validate_topic(topic)
148
241
  cls._validate_topic(response_topic, "response_topic")
149
- msg_obj = Message(
242
+ return Message(
150
243
  topic=topic,
151
- payload=property_obj.model_dump_json(by_alias=True).encode("utf-8"),
244
+ payload=payload,
152
245
  qos=1,
153
246
  retain=False,
154
- content_type="application/json",
247
+ content_type=content_type,
155
248
  response_topic=response_topic,
156
249
  correlation_data=(
157
250
  correlation_id.encode("utf-8")
@@ -160,7 +253,6 @@ class MessageCreator:
160
253
  ),
161
254
  user_properties={"PropertyVersion": str(version)},
162
255
  )
163
- return msg_obj
164
256
 
165
257
  @classmethod
166
258
  def property_response_message(
@@ -175,6 +267,27 @@ class MessageCreator:
175
267
  """
176
268
  Creates a message representing a response to a property update request.
177
269
  """
270
+ return cls.binary_property_response_message(
271
+ response_topic,
272
+ property_obj.model_dump_json(by_alias=True).encode("utf-8"),
273
+ "application/json",
274
+ version,
275
+ return_code,
276
+ correlation_id,
277
+ debug_info,
278
+ )
279
+
280
+ @classmethod
281
+ def binary_property_response_message(
282
+ cls,
283
+ response_topic: str,
284
+ payload: bytes,
285
+ content_type: str,
286
+ version: str,
287
+ return_code: Union[int, MethodReturnCode],
288
+ correlation_id: Union[str, bytes, None] = None,
289
+ debug_info: Optional[str] = None,
290
+ ) -> Message:
178
291
  cls._validate_topic(response_topic, "response_topic")
179
292
  rc = (
180
293
  return_code.value
@@ -183,10 +296,10 @@ class MessageCreator:
183
296
  )
184
297
  msg_obj = Message(
185
298
  topic=response_topic,
186
- payload=property_obj.model_dump_json(by_alias=True).encode("utf-8"),
299
+ payload=payload,
187
300
  qos=1,
188
301
  retain=False,
189
- content_type="application/json",
302
+ content_type=content_type,
190
303
  correlation_data=(
191
304
  correlation_id.encode("utf-8")
192
305
  if isinstance(correlation_id, str)
@@ -210,22 +323,38 @@ class MessageCreator:
210
323
  request_obj: BaseModel,
211
324
  response_topic: str,
212
325
  correlation_id: Union[str, bytes, None] = None,
326
+ ) -> Message:
327
+ return cls.binary_request_message(
328
+ topic,
329
+ request_obj.model_dump_json(by_alias=True).encode("utf-8"),
330
+ "application/json",
331
+ response_topic,
332
+ correlation_id,
333
+ )
334
+
335
+ @classmethod
336
+ def binary_request_message(
337
+ cls,
338
+ topic: str,
339
+ payload: bytes,
340
+ content_type: str,
341
+ response_topic: str,
342
+ correlation_id: Union[str, bytes, None] = None,
213
343
  ) -> Message:
214
344
  cls._validate_topic(topic)
215
345
  cls._validate_topic(response_topic, "response_topic")
216
346
  if correlation_id is None:
217
347
  correlation_id = str(uuid.uuid4())
218
- msg_obj = Message(
348
+ return Message(
219
349
  topic=topic,
220
- payload=request_obj.model_dump_json(by_alias=True).encode("utf-8"),
350
+ payload=payload,
221
351
  qos=1,
222
352
  retain=False,
223
353
  response_topic=response_topic,
224
- content_type="application/json",
354
+ content_type=content_type,
225
355
  correlation_data=(
226
356
  correlation_id.encode("utf-8")
227
357
  if isinstance(correlation_id, str)
228
358
  else correlation_id
229
359
  ),
230
360
  )
231
- return msg_obj