slim-bindings 0.3.6__cp313-cp313-win_amd64.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.
@@ -0,0 +1,930 @@
1
+ Metadata-Version: 2.4
2
+ Name: slim-bindings
3
+ Version: 0.3.6
4
+ Classifier: Development Status :: 3 - Alpha
5
+ Classifier: Intended Audience :: Developers
6
+ Classifier: Topic :: Software Development :: Libraries
7
+ Classifier: Programming Language :: Python :: 3.9
8
+ Classifier: Programming Language :: Python :: 3.10
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Summary: SLIM Rust bindings for Python
13
+ License: Apache-2.0
14
+ Requires-Python: >=3.9, <4.0
15
+ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
16
+ Project-URL: Repository, https://github.com/agntcy/slim
17
+ Project-URL: Issues, https://github.com/agntcy/slim/issues
18
+ Project-URL: Changelog, https://github.com/agntcy/slim/blob/main/data-plane/python-bindings/CHANGELOG.md
19
+
20
+ # SLIM Python Bindings
21
+
22
+ Bindings to call the SLIM APIs from a python program.
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ pip install slim-bindings
28
+ ```
29
+
30
+ ## Include as dependency
31
+
32
+ ### With pyproject.toml
33
+
34
+ ```toml
35
+ [project]
36
+ name = "slim-example"
37
+ version = "0.1.0"
38
+ description = "Python program using SLIM"
39
+ requires-python = ">=3.9"
40
+ dependencies = [
41
+ "slim-bindings>=0.1.0"
42
+ ]
43
+ ```
44
+
45
+ ### With poetry project
46
+
47
+ ```toml
48
+ [tool.poetry]
49
+ name = "slim-example"
50
+ version = "0.1.0"
51
+ description = "Python program using SLIM"
52
+
53
+ [tool.poetry.dependencies]
54
+ python = ">=3.9,<3.14"
55
+ slim-bindings = ">=0.1.0"
56
+ ```
57
+
58
+ ## Example programs
59
+
60
+ ### Server
61
+
62
+ ```python
63
+ # Copyright AGNTCY Contributors (https://github.com/agntcy)
64
+ # SPDX-License-Identifier: Apache-2.0
65
+
66
+ import argparse
67
+ import asyncio
68
+ from signal import SIGINT
69
+
70
+ import slim_bindings
71
+
72
+
73
+ async def run_server(address: str, enable_opentelemetry: bool):
74
+ # init tracing
75
+ slim_bindings.init_tracing(
76
+ {
77
+ "log_level": "debug"
78
+ "opentelemetry": {
79
+ "enabled": enable_opentelemetry
80
+ }
81
+ }
82
+ )
83
+
84
+ global slim
85
+ # create new slim object
86
+ slim = await slim_bindings.Slim.new("cisco", "default", "slim")
87
+
88
+ # Run as server
89
+ await slim.run_server({"endpoint": address, "tls": {"insecure": True}})
90
+
91
+
92
+ async def main():
93
+ parser = argparse.ArgumentParser(
94
+ description="Command line client for slim server."
95
+ )
96
+ parser.add_argument(
97
+ "-s", "--slim", type=str, help="SLIM address.", default="127.0.0.1:12345"
98
+ )
99
+ parser.add_argument(
100
+ "--enable-opentelemetry",
101
+ "-t",
102
+ action="store_true",
103
+ default=False,
104
+ help="Enable OpenTelemetry tracing.",
105
+ )
106
+
107
+ args = parser.parse_args()
108
+
109
+ # Create an asyncio event to keep the loop running until interrupted
110
+ stop_event = asyncio.Event()
111
+
112
+ # Define a shutdown handler to set the event when interrupted
113
+ def shutdown():
114
+ print("\nShutting down...")
115
+ stop_event.set()
116
+
117
+ # Register the shutdown handler for SIGINT
118
+ loop = asyncio.get_running_loop()
119
+ loop.add_signal_handler(SIGINT, shutdown)
120
+
121
+ # Run the client task
122
+ client_task = asyncio.create_task(
123
+ run_server(args.slim, args.enable_opentelemetry)
124
+ )
125
+
126
+ # Wait until the stop event is set
127
+ await stop_event.wait()
128
+
129
+ # Cancel the client task
130
+ client_task.cancel()
131
+ try:
132
+ await client_task
133
+ except asyncio.CancelledError:
134
+ pass
135
+
136
+
137
+ if __name__ == "__main__":
138
+ try:
139
+ asyncio.run(main())
140
+ except KeyboardInterrupt:
141
+ print("Program terminated by user.")
142
+ ```
143
+
144
+ ### PubSub Client
145
+
146
+ ```python
147
+ # Copyright AGNTCY Contributors (https://github.com/agntcy)
148
+ # SPDX-License-Identifier: Apache-2.0
149
+
150
+ import argparse
151
+ import asyncio
152
+ import datetime
153
+
154
+ import slim_bindings
155
+
156
+ class color:
157
+ PURPLE = "\033[95m"
158
+ CYAN = "\033[96m"
159
+ DARKCYAN = "\033[36m"
160
+ BLUE = "\033[94m"
161
+ GREEN = "\033[92m"
162
+ YELLOW = "\033[93m"
163
+ RED = "\033[91m"
164
+ BOLD = "\033[1m"
165
+ UNDERLINE = "\033[4m"
166
+ END = "\033[0m"
167
+
168
+
169
+ def format_message(message1, message2):
170
+ return f"{color.BOLD}{color.CYAN}{message1.capitalize() :<45}{color.END}{message2}"
171
+
172
+
173
+ def split_id(id):
174
+ # Split the IDs into their respective components
175
+ try:
176
+ local_organization, local_namespace, local_agent = id.split("/")
177
+ except ValueError as e:
178
+ print("Error: IDs must be in the format organization/namespace/agent.")
179
+ raise e
180
+
181
+ return local_organization, local_namespace, local_agent
182
+
183
+
184
+ async def run_client(local_id, remote_id, address, enable_opentelemetry: bool):
185
+ # init tracing
186
+ slim_bindings.init_tracing(
187
+ {
188
+ "log_level": "info"
189
+ "opentelemetry": {
190
+ "enabled": enable_opentelemetry
191
+ }
192
+ }
193
+ )
194
+
195
+ # Split the local IDs into their respective components
196
+ local_organization, local_namespace, local_agent = split_id(local_id)
197
+
198
+ # Split the remote IDs into their respective components
199
+ remote_organization, remote_namespace, broadcast_topic = split_id(remote_id)
200
+
201
+ name = f"{local_agent}"
202
+
203
+ print(f"Creating participant {name}...")
204
+
205
+ participant = await slim_bindings.Slim.new(
206
+ local_organization, local_namespace, local_agent
207
+ )
208
+
209
+ # Connect to slim server
210
+ _ = await participant.connect(
211
+ {"endpoint": address, "tls": {"insecure": True}}
212
+ )
213
+
214
+ # set route for the chat, so that messages can be sent to the other participants
215
+ await participant.set_route(remote_organization, remote_namespace, broadcast_topic)
216
+
217
+ # Subscribe to the producer topic
218
+ await participant.subscribe(remote_organization, remote_namespace, broadcast_topic)
219
+
220
+ print(f"{name} -> Creating new pubsub sessions...")
221
+ # create pubsubb session. A pubsub session is a just a bidirectional
222
+ # streaming session, where participants are both sender and receivers
223
+ session_info = await participant.create_session(
224
+ slim_bindings.PySessionConfiguration.Streaming(
225
+ slim_bindings.PySessionDirection.BIDIRECTIONAL,
226
+ topic=slim_bindings.PyAgentType(
227
+ remote_organization, remote_namespace, broadcast_topic
228
+ ),
229
+ max_retries=5,
230
+ timeout=datetime.timedelta(seconds=5),
231
+ )
232
+ )
233
+
234
+ # define the background task
235
+ async def background_task():
236
+ msg = f"Hello from {local_agent}"
237
+
238
+ async with participant:
239
+ while True:
240
+ try:
241
+ # receive message from session
242
+ recv_session, msg_rcv = await participant.receive(
243
+ session=session_info.id
244
+ )
245
+
246
+ # Check if the message is calling this specific participant
247
+ # if not, ignore it
248
+ if local_agent in msg_rcv.decode():
249
+ # print the message
250
+ print(f"{name} -> Received message for me: {msg_rcv.decode()}")
251
+
252
+ # send the message to the next participant
253
+ await participant.publish(
254
+ recv_session,
255
+ msg.encode(),
256
+ remote_organization,
257
+ remote_namespace,
258
+ broadcast_topic,
259
+ )
260
+
261
+ print(f"{name} -> Sending message to all participants: {msg}")
262
+ else:
263
+ print(
264
+ f"{name} -> Receiving message: {msg_rcv.decode()} - not for me."
265
+ )
266
+ except asyncio.CancelledError:
267
+ break
268
+ except Exception as e:
269
+ print(f"{name} -> Error receiving message: {e}")
270
+ break
271
+
272
+ receive_task = asyncio.create_task(background_task())
273
+
274
+ async def background_task_keyboard():
275
+ while True:
276
+ user_input = await asyncio.to_thread(input, "message> ")
277
+ if user_input == "exit":
278
+ break
279
+
280
+ # Send the message to the all participants
281
+ await participant.publish(
282
+ session_info,
283
+ f"{user_input}".encode(),
284
+ remote_organization,
285
+ remote_namespace,
286
+ broadcast_topic,
287
+ )
288
+
289
+ receive_task.cancel()
290
+
291
+ send_task = asyncio.create_task(background_task_keyboard())
292
+
293
+ # Wait for both tasks to finish
294
+ await asyncio.gather(receive_task, send_task)
295
+
296
+
297
+ async def main():
298
+ parser = argparse.ArgumentParser(
299
+ description="Command line client for message passing."
300
+ )
301
+ parser.add_argument(
302
+ "-l",
303
+ "--local",
304
+ type=str,
305
+ help="Local ID in the format organization/namespace/agent.",
306
+ )
307
+ parser.add_argument(
308
+ "-r",
309
+ "--remote",
310
+ type=str,
311
+ help="Remote ID in the format organization/namespace/agent.",
312
+ )
313
+ parser.add_argument(
314
+ "-s",
315
+ "--slim",
316
+ type=str,
317
+ help="Slim address.",
318
+ default="http://127.0.0.1:46357",
319
+ )
320
+ parser.add_argument(
321
+ "-t",
322
+ "--enable-opentelemetry",
323
+ action="store_true",
324
+ default=False,
325
+ help="Enable OpenTelemetry tracing.",
326
+ )
327
+
328
+ args = parser.parse_args()
329
+
330
+ # Run the client with the specified local ID, remote ID, and optional message
331
+ await run_client(
332
+ args.local,
333
+ args.remote,
334
+ args.slim,
335
+ args.enable_opentelemetry,
336
+ )
337
+
338
+
339
+ if __name__ == "__main__":
340
+ try:
341
+ asyncio.run(main())
342
+ except KeyboardInterrupt:
343
+ print("Program terminated by user.")
344
+ ```
345
+
346
+ ### Streaming producer and consumer
347
+
348
+ ```python
349
+ # Copyright AGNTCY Contributors (https://github.com/agntcy)
350
+ # SPDX-License-Identifier: Apache-2.0
351
+
352
+ import argparse
353
+ import asyncio
354
+ import datetime
355
+ import os
356
+
357
+ import slim_bindings
358
+
359
+ class color:
360
+ PURPLE = "\033[95m"
361
+ CYAN = "\033[96m"
362
+ DARKCYAN = "\033[36m"
363
+ BLUE = "\033[94m"
364
+ GREEN = "\033[92m"
365
+ YELLOW = "\033[93m"
366
+ RED = "\033[91m"
367
+ BOLD = "\033[1m"
368
+ UNDERLINE = "\033[4m"
369
+ END = "\033[0m"
370
+
371
+
372
+ def format_message(message1, message2):
373
+ return f"{color.BOLD}{color.CYAN}{message1.capitalize() :<45}{color.END}{message2}"
374
+
375
+
376
+ def split_id(id):
377
+ # Split the IDs into their respective components
378
+ try:
379
+ local_organization, local_namespace, local_agent = id.split("/")
380
+ except ValueError as e:
381
+ print("Error: IDs must be in the format organization/namespace/agent.")
382
+ raise e
383
+
384
+ return local_organization, local_namespace, local_agent
385
+
386
+
387
+ async def run_client(
388
+ local_id, remote_id, address, producer, enable_opentelemetry: bool
389
+ ):
390
+ # init tracing
391
+ slim_bindings.init_tracing(
392
+ {
393
+ "log_level": "info"
394
+ "opentelemetry": {
395
+ "enabled": enable_opentelemetry
396
+ }
397
+ }
398
+ )
399
+
400
+ # Split the IDs into their respective components
401
+ local_organization, local_namespace, local_agent = split_id(local_id)
402
+
403
+ # create new slim object
404
+ slim = await slim_bindings.Slim.new(
405
+ local_organization, local_namespace, local_agent
406
+ )
407
+
408
+ # Connect to the service and subscribe for the local name
409
+ print(format_message("connecting to:", address))
410
+ _ = await slim.connect({"endpoint": address, "tls": {"insecure": True}})
411
+
412
+ # Split the IDs into their respective components
413
+ remote_organization, remote_namespace, broadcast_topic = split_id(remote_id)
414
+
415
+ # Get the local agent instance from env
416
+ instance = os.getenv("SLIM_INSTANCE_ID", local_agent)
417
+
418
+ async with slim:
419
+ if producer:
420
+ # Create a route to the remote ID
421
+ await slim.set_route(
422
+ remote_organization, remote_namespace, broadcast_topic
423
+ )
424
+
425
+ # create streaming session with default config
426
+ session_info = await slim.create_session(
427
+ slim_bindings.PySessionConfiguration.Streaming(
428
+ slim_bindings.PySessionDirection.SENDER,
429
+ topic=None,
430
+ max_retries=5,
431
+ timeout=datetime.timedelta(seconds=5),
432
+ )
433
+ )
434
+
435
+ # initialize count
436
+ count = 0
437
+
438
+ while True:
439
+ try:
440
+ # Send a message
441
+ print(
442
+ format_message(
443
+ f"{instance} streaming message to {remote_organization}/{remote_namespace}/{broadcast_topic}: ",
444
+ f"{count}",
445
+ )
446
+ )
447
+
448
+ # Send the message and wait for a reply
449
+ await slim.publish(
450
+ session_info,
451
+ f"{count}".encode(),
452
+ remote_organization,
453
+ remote_namespace,
454
+ broadcast_topic,
455
+ )
456
+
457
+ count += 1
458
+ except Exception as e:
459
+ print("received error: ", e)
460
+ finally:
461
+ await asyncio.sleep(1)
462
+ else:
463
+ # subscribe to streaming session
464
+ await slim.subscribe(
465
+ remote_organization, remote_namespace, broadcast_topic
466
+ )
467
+
468
+ # Wait for messages and not reply
469
+ while True:
470
+ session_info, _ = await slim.receive()
471
+ print(
472
+ format_message(
473
+ f"{instance.capitalize()} received a new session:",
474
+ f"{session_info.id}",
475
+ )
476
+ )
477
+
478
+ async def background_task(session_info):
479
+ while True:
480
+ # Receive the message from the session
481
+ session, msg = await slim.receive(session=session_info)
482
+ print(
483
+ format_message(
484
+ f"{local_agent.capitalize()} received from {remote_organization}/{remote_namespace}/{broadcast_topic}: ",
485
+ f"{msg.decode()}",
486
+ )
487
+ )
488
+
489
+ asyncio.create_task(background_task(session_info.id))
490
+
491
+
492
+ async def main():
493
+ parser = argparse.ArgumentParser(
494
+ description="Command line client for message passing."
495
+ )
496
+ parser.add_argument(
497
+ "-l",
498
+ "--local",
499
+ type=str,
500
+ help="Local ID in the format organization/namespace/agent.",
501
+ )
502
+ parser.add_argument(
503
+ "-r",
504
+ "--remote",
505
+ type=str,
506
+ help="Remote ID in the format organization/namespace/agent.",
507
+ )
508
+ parser.add_argument(
509
+ "-s",
510
+ "--slim",
511
+ type=str,
512
+ help="SLIM address.",
513
+ default="http://127.0.0.1:46357",
514
+ )
515
+ parser.add_argument(
516
+ "-t",
517
+ "--enable-opentelemetry",
518
+ action="store_true",
519
+ default=False,
520
+ help="Enable OpenTelemetry tracing.",
521
+ )
522
+ parser.add_argument(
523
+ "-p",
524
+ "--producer",
525
+ action="store_true",
526
+ default=False,
527
+ help="Enable producer mode.",
528
+ )
529
+
530
+ args = parser.parse_args()
531
+
532
+ # Run the client with the specified local ID, remote ID, and optional message
533
+ await run_client(
534
+ args.local,
535
+ args.remote,
536
+ args.slim,
537
+ args.producer,
538
+ args.enable_opentelemetry,
539
+ )
540
+
541
+
542
+ if __name__ == "__main__":
543
+ try:
544
+ asyncio.run(main())
545
+ except KeyboardInterrupt:
546
+ print("Program terminated by user.")
547
+
548
+ ```
549
+
550
+ ### Fire And Forget sender and receiver
551
+
552
+ ```python
553
+ # Copyright AGNTCY Contributors (https://github.com/agntcy)
554
+ # SPDX-License-Identifier: Apache-2.0
555
+
556
+ import argparse
557
+ import asyncio
558
+ import os
559
+
560
+ import slim_bindings
561
+
562
+
563
+ class color:
564
+ PURPLE = "\033[95m"
565
+ CYAN = "\033[96m"
566
+ DARKCYAN = "\033[36m"
567
+ BLUE = "\033[94m"
568
+ GREEN = "\033[92m"
569
+ YELLOW = "\033[93m"
570
+ RED = "\033[91m"
571
+ BOLD = "\033[1m"
572
+ UNDERLINE = "\033[4m"
573
+ END = "\033[0m"
574
+
575
+
576
+ def format_message(message1, message2):
577
+ return f"{color.BOLD}{color.CYAN}{message1.capitalize() :<45}{color.END}{message2}"
578
+
579
+
580
+ def split_id(id):
581
+ # Split the IDs into their respective components
582
+ try:
583
+ local_organization, local_namespace, local_agent = id.split("/")
584
+ except ValueError as e:
585
+ print("Error: IDs must be in the format organization/namespace/agent.")
586
+ raise e
587
+
588
+ return local_organization, local_namespace, local_agent
589
+
590
+
591
+ async def run_client(
592
+ local_id: str,
593
+ remote_id: str,
594
+ message: str,
595
+ address: str,
596
+ iterations: int,
597
+ enable_opentelemetry: bool,
598
+ ):
599
+ # init tracing
600
+ slim_bindings.init_tracing(
601
+ {
602
+ "log_level": "info"
603
+ "opentelemetry": {
604
+ "enabled": enable_opentelemetry
605
+ }
606
+ }
607
+ )
608
+
609
+ local_organization, local_namespace, local_agent = split_id(local_id)
610
+
611
+ # create new slim object
612
+ slim = await slim_bindings.Slim.new(
613
+ local_organization, local_namespace, local_agent
614
+ )
615
+
616
+ # Connect to remote slim server
617
+ print(format_message(f"connecting to: {address}"))
618
+ _ = await slim.connect({"endpoint": address, "tls": {"insecure": True}})
619
+
620
+ # Get the local agent instance from env
621
+ instance = os.getenv("SLIM_INSTANCE_ID", local_agent)
622
+
623
+ async with slim:
624
+ if message:
625
+ # Split the IDs into their respective components
626
+ remote_organization, remote_namespace, remote_agent = split_id(remote_id)
627
+
628
+ # Create a route to the remote ID
629
+ await slim.set_route(remote_organization, remote_namespace, remote_agent)
630
+
631
+ # create a session
632
+ session = await slim.create_session(
633
+ slim_bindings.PySessionConfiguration.FireAndForget()
634
+ )
635
+
636
+ for i in range(0, iterations):
637
+ try:
638
+ # Send the message
639
+ await slim.publish(
640
+ session,
641
+ message.encode(),
642
+ remote_organization,
643
+ remote_namespace,
644
+ remote_agent,
645
+ )
646
+ print(format_message(f"{instance} sent:", message))
647
+
648
+ # Wait for a reply
649
+ session_info, msg = await slim.receive(session=session.id)
650
+ print(
651
+ format_message(
652
+ f"{instance.capitalize()} received (from session {session_info.id}):",
653
+ f"{msg.decode()}",
654
+ )
655
+ )
656
+ except Exception as e:
657
+ print("received error: ", e)
658
+
659
+ await asyncio.sleep(1)
660
+ else:
661
+ # Wait for a message and reply in a loop
662
+ while True:
663
+ session_info, _ = await slim.receive()
664
+ print(
665
+ format_message(
666
+ f"{instance.capitalize()} received a new session:",
667
+ f"{session_info.id}",
668
+ )
669
+ )
670
+
671
+ async def background_task(session_id):
672
+ while True:
673
+ # Receive the message from the session
674
+ session, msg = await slim.receive(session=session_id)
675
+ print(
676
+ format_message(
677
+ f"{instance.capitalize()} received (from session {session_id}):",
678
+ f"{msg.decode()}",
679
+ )
680
+ )
681
+
682
+ ret = f"{msg.decode()} from {instance}"
683
+
684
+ await slim.publish_to(session, ret.encode())
685
+ print(format_message(f"{instance.capitalize()} replies:", ret))
686
+
687
+ asyncio.create_task(background_task(session_info.id))
688
+
689
+
690
+ async def main():
691
+ parser = argparse.ArgumentParser(
692
+ description="Command line client for message passing."
693
+ )
694
+ parser.add_argument(
695
+ "-l",
696
+ "--local",
697
+ type=str,
698
+ help="Local ID in the format organization/namespace/agent.",
699
+ )
700
+ parser.add_argument(
701
+ "-r",
702
+ "--remote",
703
+ type=str,
704
+ help="Remote ID in the format organization/namespace/agent.",
705
+ )
706
+ parser.add_argument("-m", "--message", type=str, help="Message to send.")
707
+ parser.add_argument(
708
+ "-s",
709
+ "--slim",
710
+ type=str,
711
+ help="SLIM address.",
712
+ default="http://127.0.0.1:46357",
713
+ )
714
+ parser.add_argument(
715
+ "-i",
716
+ "--iterations",
717
+ type=int,
718
+ help="Number of messages to send, one per second.",
719
+ )
720
+ parser.add_argument(
721
+ "-t",
722
+ "--enable-opentelemetry",
723
+ action="store_true",
724
+ default=False,
725
+ help="Enable OpenTelemetry tracing.",
726
+ )
727
+
728
+ args = parser.parse_args()
729
+
730
+ # Run the client with the specified local ID, remote ID, and optional message
731
+ await run_client(
732
+ args.local,
733
+ args.remote,
734
+ args.message,
735
+ args.slim,
736
+ args.iterations,
737
+ args.enable_opentelemetry,
738
+ )
739
+
740
+
741
+ if __name__ == "__main__":
742
+ try:
743
+ asyncio.run(main())
744
+ except KeyboardInterrupt:
745
+ print("Program terminated by user.")
746
+ ```
747
+
748
+ ### Request/Response Requester and Responder
749
+
750
+ ```python
751
+ # Copyright AGNTCY Contributors (https://github.com/agntcy)
752
+ # SPDX-License-Identifier: Apache-2.0
753
+
754
+ import argparse
755
+ import asyncio
756
+ import os
757
+
758
+ import slim_bindings
759
+
760
+
761
+ class color:
762
+ PURPLE = "\033[95m"
763
+ CYAN = "\033[96m"
764
+ DARKCYAN = "\033[36m"
765
+ BLUE = "\033[94m"
766
+ GREEN = "\033[92m"
767
+ YELLOW = "\033[93m"
768
+ RED = "\033[91m"
769
+ BOLD = "\033[1m"
770
+ UNDERLINE = "\033[4m"
771
+ END = "\033[0m"
772
+
773
+
774
+ def format_message(message1, message2):
775
+ return f"{color.BOLD}{color.CYAN}{message1.capitalize() :<45}{color.END}{message2}"
776
+
777
+
778
+ def split_id(id):
779
+ # Split the IDs into their respective components
780
+ try:
781
+ local_organization, local_namespace, local_agent = id.split("/")
782
+ except ValueError as e:
783
+ print("Error: IDs must be in the format organization/namespace/agent.")
784
+ raise e
785
+
786
+ return local_organization, local_namespace, local_agent
787
+
788
+
789
+ async def run_client(local_id, remote_id, message, address, enable_opentelemetry: bool):
790
+ # init tracing
791
+ slim_bindings.init_tracing(
792
+ {
793
+ "log_level": "info"
794
+ "opentelemetry": {
795
+ "enabled": enable_opentelemetry
796
+ }
797
+ }
798
+ )
799
+
800
+ # Split the IDs into their respective components
801
+ local_organization, local_namespace, local_agent = split_id(local_id)
802
+
803
+ # create new slim object
804
+ slim = await slim_bindings.Slim.new(
805
+ local_organization, local_namespace, local_agent
806
+ )
807
+
808
+ # Connect to the service and subscribe for the local name
809
+ print(format_message("connecting to:", address))
810
+ _ = await slim.connect({"endpoint": address, "tls": {"insecure": True}})
811
+
812
+ # Get the local agent instance from env
813
+ instance = os.getenv("SLIM_INSTANCE_ID", local_agent)
814
+
815
+ async with slim:
816
+ if message:
817
+ # Split the IDs into their respective components
818
+ remote_organization, remote_namespace, remote_agent = split_id(remote_id)
819
+
820
+ # Create a route to the remote ID
821
+ await slim.set_route(remote_organization, remote_namespace, remote_agent)
822
+
823
+ # create a session
824
+ session = await slim.create_session(
825
+ slim_bindings.PySessionConfiguration.RequestResponse()
826
+ )
827
+
828
+ try:
829
+ # Send the message and wait for a reply
830
+ session_info, reply = await slim.request_reply(
831
+ session,
832
+ message.encode(),
833
+ remote_organization,
834
+ remote_namespace,
835
+ remote_agent,
836
+ )
837
+ print(format_message(f"{instance} sent:", message))
838
+
839
+ # Print reply and exit
840
+ print(
841
+ format_message(
842
+ f"{instance} received (from session {session_info.id}):",
843
+ f"{reply.decode()}",
844
+ )
845
+ )
846
+
847
+ except Exception as e:
848
+ print("received error: ", e)
849
+ else:
850
+ # Wait for a message and reply in a loop
851
+ while True:
852
+ session_info, _ = await slim.receive()
853
+ print(
854
+ format_message(
855
+ f"{instance.capitalize()} received a new session:",
856
+ f"{session_info.id}",
857
+ )
858
+ )
859
+
860
+ async def background_task(session_id):
861
+ while True:
862
+ # Receive the message from the session
863
+ session, msg = await slim.receive(session=session_id)
864
+ print(
865
+ format_message(
866
+ f"{instance.capitalize()} received (from session {session_id}):",
867
+ f"{msg.decode()}",
868
+ )
869
+ )
870
+
871
+ ret = f"{msg.decode()} from {instance}"
872
+
873
+ await slim.publish_to(session, ret.encode())
874
+ print(format_message(f"{instance.capitalize()} replies:", ret))
875
+
876
+ asyncio.create_task(background_task(session_info.id))
877
+
878
+
879
+ async def main():
880
+ parser = argparse.ArgumentParser(
881
+ description="Command line client for message passing."
882
+ )
883
+ parser.add_argument(
884
+ "-l",
885
+ "--local",
886
+ type=str,
887
+ help="Local ID in the format organization/namespace/agent.",
888
+ )
889
+ parser.add_argument(
890
+ "-r",
891
+ "--remote",
892
+ type=str,
893
+ help="Remote ID in the format organization/namespace/agent.",
894
+ )
895
+ parser.add_argument("-m", "--message", type=str, help="Message to send.")
896
+ parser.add_argument(
897
+ "-s",
898
+ "--slim",
899
+ type=str,
900
+ help="SLIM address.",
901
+ default="http://127.0.0.1:46357",
902
+ )
903
+
904
+ parser.add_argument(
905
+ "-t",
906
+ "--enable-opentelemetry",
907
+ action="store_true",
908
+ default=False,
909
+ help="Enable OpenTelemetry tracing.",
910
+ )
911
+
912
+ args = parser.parse_args()
913
+
914
+ # Run the client with the specified local ID, remote ID, and optional message
915
+ await run_client(
916
+ args.local,
917
+ args.remote,
918
+ args.message,
919
+ args.slim,
920
+ args.enable_opentelemetry,
921
+ )
922
+
923
+
924
+ if __name__ == "__main__":
925
+ try:
926
+ asyncio.run(main())
927
+ except KeyboardInterrupt:
928
+ print("Program terminated by user.")
929
+ ```
930
+