ratio1 3.4.36__tar.gz → 3.4.37__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 (212) hide show
  1. {ratio1-3.4.36 → ratio1-3.4.37}/PKG-INFO +1 -1
  2. {ratio1-3.4.36 → ratio1-3.4.37}/pyproject.toml +1 -1
  3. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/_ver.py +1 -1
  4. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/generic_session.py +264 -57
  5. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/bc/evm.py +17 -1
  6. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/cli/cli_commands.py +16 -6
  7. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/cli/nodes.py +140 -4
  8. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/cli/oracles.py +35 -14
  9. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/apps.py +1 -0
  10. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/plugins/deeploy_const.py +14 -0
  11. ratio1-3.4.37/tutorials/ex22_deeploy_custom_code.py +100 -0
  12. ratio1-3.4.37/xperimental/scripts/monitoring/collect_messages.py +57 -0
  13. ratio1-3.4.37/xperimental/scripts/monitoring/get_hb_distribution.py +153 -0
  14. ratio1-3.4.37/xperimental/scripts/monitoring/multithreaded_network_sniffer.py +171 -0
  15. {ratio1-3.4.36 → ratio1-3.4.37}/.devcontainer/Dockerfile +0 -0
  16. {ratio1-3.4.36 → ratio1-3.4.37}/.devcontainer/devcontainer.json +0 -0
  17. {ratio1-3.4.36 → ratio1-3.4.37}/.gitattributes +0 -0
  18. {ratio1-3.4.36 → ratio1-3.4.37}/.github/workflows/python-publish.yml +0 -0
  19. {ratio1-3.4.36 → ratio1-3.4.37}/.gitignore +0 -0
  20. {ratio1-3.4.36 → ratio1-3.4.37}/.vscode/launch.json +0 -0
  21. {ratio1-3.4.36 → ratio1-3.4.37}/LICENSE +0 -0
  22. {ratio1-3.4.36 → ratio1-3.4.37}/README.md +0 -0
  23. {ratio1-3.4.36 → ratio1-3.4.37}/TODOs.md +0 -0
  24. {ratio1-3.4.36 → ratio1-3.4.37}/__init__.py +0 -0
  25. {ratio1-3.4.36 → ratio1-3.4.37}/r1ctl.MD +0 -0
  26. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/__init__.py +0 -0
  27. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/__init__.py +0 -0
  28. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/distributed_custom_code_presets.py +0 -0
  29. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/instance.py +0 -0
  30. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/payload/__init__.py +0 -0
  31. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/payload/payload.py +0 -0
  32. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/pipeline.py +0 -0
  33. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/plugin_template.py +0 -0
  34. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/responses.py +0 -0
  35. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/transaction.py +0 -0
  36. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base/webapp_pipeline.py +0 -0
  37. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/base_decentra_object.py +0 -0
  38. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/bc/__init__.py +0 -0
  39. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/bc/base.py +0 -0
  40. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/bc/chain.py +0 -0
  41. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/bc/ec.py +0 -0
  42. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/certs/51.15.142.167.crt +0 -0
  43. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/certs/__init__.py +0 -0
  44. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/certs/a0d9818f.ala.eu-central-1.emqxsl.com.crt +0 -0
  45. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/certs/r9092118.ala.eu-central-1.emqxsl.com.crt +0 -0
  46. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/certs/s624dbd4.ala.us-east-1.emqxsl.com.crt +0 -0
  47. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/cli/README.md +0 -0
  48. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/cli/cli.py +0 -0
  49. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/cli/package_update.py +0 -0
  50. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/code_cheker/__init__.py +0 -0
  51. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/code_cheker/base.py +0 -0
  52. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/code_cheker/checker.py +0 -0
  53. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/comm/__init__.py +0 -0
  54. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/comm/amqp_wrapper.py +0 -0
  55. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/comm/base_comm_wrapper.py +0 -0
  56. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/comm/mqtt_wrapper.py +0 -0
  57. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/README.md +0 -0
  58. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/__init__.py +0 -0
  59. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/base.py +0 -0
  60. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/comms.py +0 -0
  61. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/environment.py +0 -0
  62. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/evm_net.py +0 -0
  63. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/formatter.py +0 -0
  64. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/heartbeat.py +0 -0
  65. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/misc.py +0 -0
  66. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/const/payload.py +0 -0
  67. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/__init__.py +0 -0
  68. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/instance/__init__.py +0 -0
  69. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/instance/chain_dist_custom_job_01_plugin.py +0 -0
  70. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/instance/custom_webapi_01_plugin.py +0 -0
  71. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/instance/generic_http_01_plugin.py +0 -0
  72. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/instance/net_mon_01_plugin.py +0 -0
  73. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/instance/telegram_basic_bot_01_plugin.py +0 -0
  74. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/instance/telegram_conversational_bot_01_plugin.py +0 -0
  75. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/instance/view_scene_01_plugin.py +0 -0
  76. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/default/session/mqtt_session.py +0 -0
  77. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/io_formatter/__init__.py +0 -0
  78. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/io_formatter/base/__init__.py +0 -0
  79. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/io_formatter/base/base_formatter.py +0 -0
  80. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/io_formatter/default/__init__.py +0 -0
  81. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/io_formatter/default/a_dummy.py +0 -0
  82. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/io_formatter/default/aixp1.py +0 -0
  83. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/io_formatter/default/default.py +0 -0
  84. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/io_formatter/io_formatter_manager.py +0 -0
  85. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/__init__.py +0 -0
  86. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/ifps_keygen +0 -0
  87. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/ipfs_setup/ipfs.service +0 -0
  88. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/ipfs_setup/launch_service.sh +0 -0
  89. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/ipfs_setup/restart.sh +0 -0
  90. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/ipfs_setup/run.sh +0 -0
  91. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/ipfs_setup/setup.sh +0 -0
  92. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/ipfs_setup/show.sh +0 -0
  93. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/ipfs_setup/write_key.sh +0 -0
  94. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/ipfs/r1fs.py +0 -0
  95. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/__init__.py +0 -0
  96. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/base_logger.py +0 -0
  97. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/__init__.py +0 -0
  98. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/class_instance_mixin.py +0 -0
  99. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/computer_vision_mixin.py +0 -0
  100. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/datetime_mixin.py +0 -0
  101. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/download_mixin.py +0 -0
  102. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/general_serialization_mixin.py +0 -0
  103. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/json_serialization_mixin.py +0 -0
  104. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/machine_mixin.py +0 -0
  105. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/pickle_serialization_mixin.py +0 -0
  106. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/process_mixin.py +0 -0
  107. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/resource_size_mixin.py +0 -0
  108. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/timers_mixin.py +0 -0
  109. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/upload_mixin.py +0 -0
  110. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/logger_mixins/utils_mixin.py +0 -0
  111. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/small_logger.py +0 -0
  112. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/tzlocal/__init__.py +0 -0
  113. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/tzlocal/unix.py +0 -0
  114. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/tzlocal/utils.py +0 -0
  115. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/tzlocal/win32.py +0 -0
  116. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/logging/tzlocal/windows_tz.py +0 -0
  117. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/plugins_manager_mixin.py +0 -0
  118. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/utils/__init__.py +0 -0
  119. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/utils/comm_utils.py +0 -0
  120. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/utils/config.py +0 -0
  121. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/utils/dotenv.py +0 -0
  122. {ratio1-3.4.36 → ratio1-3.4.37}/ratio1/utils/oracle_sync/oracle_tester.py +0 -0
  123. {ratio1-3.4.36 → ratio1-3.4.37}/requirements.txt +0 -0
  124. {ratio1-3.4.36 → ratio1-3.4.37}/template.env +0 -0
  125. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/.example_env +0 -0
  126. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/8. custom_code_fastapi_assets/index.html +0 -0
  127. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/9. code_sandbox_from_scratch_assets/index.html +0 -0
  128. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/_example_pk_sdk.pem +0 -0
  129. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/eth2025/deploy_home_safety.py +0 -0
  130. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/eth2025/deploy_maintenance_monitoring.py +0 -0
  131. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex01_part1_connect.py +0 -0
  132. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex01_part2_filter.py +0 -0
  133. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex01_part3_adv_filter.py +0 -0
  134. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex02_part1_deploy_webapi.py +0 -0
  135. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex02_part2_deploy_plugin.py +0 -0
  136. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex02_part3_deploy_r1fs_test.py +0 -0
  137. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex03_custom_code_on_one_remote__example_1.py +0 -0
  138. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex04_custom_code_on_one_remote__example_2.py +0 -0
  139. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex05_custom_code_on_one_remote__example_3.py +0 -0
  140. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex06_custom_code_on_multiple_remotes__example_1.py +0 -0
  141. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex07_custom_code_on_multiple_remotes__example_2.py +0 -0
  142. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex08_custom_webapi.py +0 -0
  143. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex09_code_sandbox_from_scratch.py +0 -0
  144. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex10_telegram_echo_bot.py +0 -0
  145. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex11_telegram_blackjack_bot.py +0 -0
  146. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex12_telegram_smart_bot.py +0 -0
  147. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex13_launch_repo_based_webapp.py +0 -0
  148. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex14_wallets.py +0 -0
  149. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex15_telegram_rogue_style_game.py +0 -0
  150. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex16_launch_repo_based_http_server.py +0 -0
  151. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex17_launch_repo_based_balanced_webapp.py +0 -0
  152. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex18_deploy_container_webapi.py +0 -0
  153. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex19_deeploy_container_example.py +0 -0
  154. /ratio1-3.4.36/tutorials/ex20_deeploy_telegram_bot_example.py → /ratio1-3.4.37/tutorials/ex20_deeploy_telegram_bot.py +0 -0
  155. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/ex21_telegram_community_bot.py +0 -0
  156. {ratio1-3.4.36 → ratio1-3.4.37}/tutorials/video_presentation/1. hello_world.ipynb +0 -0
  157. {ratio1-3.4.36 → ratio1-3.4.37}/winrun.bat +0 -0
  158. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/README.md +0 -0
  159. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/_show_config_check.py +0 -0
  160. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/cstore_check.py +0 -0
  161. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/dauth_check.py +0 -0
  162. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/keys_check.py +0 -0
  163. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/node_info_check.py +0 -0
  164. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/ora_check.py +0 -0
  165. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/ora_intervals.py +0 -0
  166. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/ora_sign_check.py +0 -0
  167. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/owner_check.py +0 -0
  168. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/payload_check.py +0 -0
  169. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/session_check.py +0 -0
  170. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/w3_auth_check.py +0 -0
  171. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/w3_nodes_licenses.py +0 -0
  172. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/w3_oracles_check.py +0 -0
  173. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/whitelist_check.py +0 -0
  174. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/_checks/workload_check.py +0 -0
  175. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/dates/check_date.py +0 -0
  176. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/debug/close_pipeline.py +0 -0
  177. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/decentralized/chain_dist_example.py +0 -0
  178. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/decentralized/chain_dist_example_initiator.py +0 -0
  179. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/decentralized/chain_dist_example_worker.py +0 -0
  180. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/deeploy/checker.py +0 -0
  181. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/deeploy/checker2.py +0 -0
  182. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/deeploy/create_request.py +0 -0
  183. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/enc_dec/enc_dec_test.py +0 -0
  184. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/eth/balance.py +0 -0
  185. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/eth/eth_sign.py +0 -0
  186. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/eth/info.md +0 -0
  187. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/eth/send.py +0 -0
  188. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/eth/send_many.py +0 -0
  189. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/eth/sign.py +0 -0
  190. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/log.py +0 -0
  191. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/logger/run.py +0 -0
  192. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/lp/lp_checker.py +0 -0
  193. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/multi-enc-dec/multi_test1.py +0 -0
  194. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/multi-enc-dec/multi_test2.py +0 -0
  195. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/pipelines/start.py +0 -0
  196. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/pipelines/stop.py +0 -0
  197. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/sign/simple_payload_checker.py +0 -0
  198. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/sign/test_sign1.py +0 -0
  199. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/sign/test_sign2.py +0 -0
  200. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/test.py +0 -0
  201. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/tokens/get_check.py +0 -0
  202. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/utils/get_documentation.py +0 -0
  203. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/various/attach_example.py +0 -0
  204. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/various/ex1.py +0 -0
  205. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/various/hello.py +0 -0
  206. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/various/remote_exec.py +0 -0
  207. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/various/save_images.py +0 -0
  208. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/wl/wl_add.py +0 -0
  209. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/xarchive/_tutorials/3. simple_real_time_custom_code.py +0 -0
  210. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/xarchive/_tutorials/4. real_time_custom_code_2.py +0 -0
  211. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/xarchive/_tutorials/8. chatbot.py +0 -0
  212. {ratio1-3.4.36 → ratio1-3.4.37}/xperimental/xarchive/test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ratio1
3
- Version: 3.4.36
3
+ Version: 3.4.37
4
4
  Summary: `ratio1` or Ration1 SDK is the Python SDK required for client app development for the Ratio1 ecosystem
5
5
  Project-URL: Homepage, https://github.com/Ratio1/ratio1_sdk
6
6
  Project-URL: Bug Tracker, https://github.com/Ratio1/ratio1_sdk/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ratio1"
7
- version = "3.4.36"
7
+ version = "3.4.37"
8
8
  authors = [
9
9
  { name="Andrei Ionut Damian", email="andrei.damian@ratio1.ai" },
10
10
  { name="Cristan Bleotiu", email="cristian.bleotiu@ratio1.ai" },
@@ -1,4 +1,4 @@
1
- __VER__ = "3.4.36"
1
+ __VER__ = "3.4.37"
2
2
 
3
3
  if __name__ == "__main__":
4
4
  with open("pyproject.toml", "rt") as fd:
@@ -27,7 +27,6 @@ from ..const import (
27
27
  BLOCKCHAIN_CONFIG, SESSION_CT, NET_CONFIG
28
28
  )
29
29
  from ..const import comms as comm_ct
30
- from ..const.evm_net import EVM_NET_DATA, EvmNetData
31
30
  from ..const import DEEPLOY_CT
32
31
  from ..io_formatter import IOFormatterWrapper
33
32
  from ..logging import Logger
@@ -1548,7 +1547,7 @@ class GenericSession(BaseDecentrAIObject):
1548
1547
  If no oracles are found for the wallet on the current network
1549
1548
  """
1550
1549
  current_network = block_engine.current_evm_network
1551
- api_base_url = EVM_NET_DATA[current_network][EvmNetData.EE_DEEPLOY_API_URL_KEY]
1550
+ api_base_url = block_engine.get_deeploy_url()
1552
1551
 
1553
1552
  wallet_nodes = block_engine.web3_get_wallet_nodes(block_engine.eth_address)
1554
1553
  oracles_on_the_network = block_engine.web3_get_oracles(current_network)
@@ -1750,6 +1749,13 @@ class GenericSession(BaseDecentrAIObject):
1750
1749
  dct_aliases = {v: k for k, v in self.__dct_node_address_to_alias.items()}
1751
1750
  return dct_aliases
1752
1751
 
1752
+ def get_node_address(self, node):
1753
+ """
1754
+ A public wrapper for __get_node_address.
1755
+ """
1756
+ return self.__get_node_address(node)
1757
+
1758
+
1753
1759
  def __get_node_address(self, node):
1754
1760
  """
1755
1761
  Get the address of a node. If node is an address, return it. Else, return the address of the node.
@@ -2317,6 +2323,19 @@ class GenericSession(BaseDecentrAIObject):
2317
2323
 
2318
2324
  return active_supervisors
2319
2325
 
2326
+ def get_last_hb(self):
2327
+ """
2328
+ Get the last heartbeat of all nodes.
2329
+
2330
+ Returns
2331
+ -------
2332
+ dict
2333
+ A dictionary with the last heartbeat of all nodes.
2334
+ Where the key is the node address and the value is the last heartbeat.
2335
+ """
2336
+ return self._dct_online_nodes_last_heartbeat
2337
+
2338
+
2320
2339
  def attach_to_pipeline(self, *,
2321
2340
  node,
2322
2341
  name,
@@ -3307,21 +3326,6 @@ class GenericSession(BaseDecentrAIObject):
3307
3326
  if target_nodes_count == 0 and len(target_nodes) == 0:
3308
3327
  raise ValueError("You must specify at least one target node to deploy the container app.")
3309
3328
 
3310
- # Check if PK exists
3311
- if not os.path.isfile(signer_private_key_path):
3312
- raise ValueError("Private key path is not valid.")
3313
-
3314
- # Create a block engine instance with the private key
3315
- block_engine = DefaultBlockEngine(
3316
- log=logger,
3317
- name="deeploy_launch_container_app",
3318
- config={
3319
- BCct.K_PEM_FILE: signer_private_key_path,
3320
- BCct.K_PASSWORD: signer_private_key_password,
3321
- }
3322
- )
3323
-
3324
- current_network, api_base_url = self.__validate_deeploy_network_and_get_api_url(block_engine)
3325
3329
  #####################################################
3326
3330
 
3327
3331
  assert callable(message_handler), "The `message_handler` method parameter must be provided."
@@ -3354,43 +3358,126 @@ class GenericSession(BaseDecentrAIObject):
3354
3358
 
3355
3359
 
3356
3360
  #####################################################
3357
- request_data = {DEEPLOY_CT.DEEPLOY_KEYS.REQUEST: {
3358
- DEEPLOY_CT.DEEPLOY_KEYS.APP_ALIAS: name,
3359
- DEEPLOY_CT.DEEPLOY_KEYS.PLUGIN_SIGNATURE: signature,
3360
- DEEPLOY_CT.DEEPLOY_KEYS.TARGET_NODES: target_nodes,
3361
- DEEPLOY_CT.DEEPLOY_KEYS.TARGET_NODES_COUNT: target_nodes_count,
3362
- }}
3363
-
3364
- request_data[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS] = {
3365
- 'TELEGRAM_BOT_TOKEN': telegram_bot_token,
3366
- 'TELEGRAM_BOT_NAME': telegram_bot_name,
3367
- 'MESSAGE_HANDLER': func_base64_code,
3368
- 'MESSAGE_HANDLER_ARGS': func_args, # mandatory message and user
3369
- 'MESSAGE_HANDLER_NAME': func_name, # not mandatory
3370
- 'PROCESSING_HANDLER': proc_func_base64_code, # not mandatory
3371
- 'PROCESSING_HANDLER_ARGS': proc_func_args, # not mandatory
3361
+ app_params = {
3362
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS_TELEGRAM_BOT_TOKEN: telegram_bot_token,
3363
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS_TELEGRAM_BOT_NAME: telegram_bot_name,
3364
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS_MESSAGE_HANDLER: func_base64_code,
3365
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS_MESSAGE_HANDLER_ARGS: func_args, # mandatory message and user
3366
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS_MESSAGE_HANDLER_NAME: func_name, # not mandatory
3367
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS_PROCESSING_HANDLER: proc_func_base64_code, # not mandatory
3368
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS_PROCESSING_HANDLER_ARGS: proc_func_args, # not mandatory
3372
3369
  }
3373
3370
 
3371
+ api_base_url, request_body = self.__generate_deeploy_request(
3372
+ logger=logger,
3373
+ signer_private_key_path=signer_private_key_path,
3374
+ signer_private_key_password=signer_private_key_password,
3375
+ app_alias=name,
3376
+ signature=signature,
3377
+ target_nodes=target_nodes,
3378
+ target_nodes_count=target_nodes_count,
3379
+ app_params=app_params
3380
+ )
3381
+
3382
+
3374
3383
  try:
3375
- # Set the nonce for the request
3376
- nonce = f"0x{int(time.time() * 1000):x}"
3377
- request_data[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.NONCE] = nonce
3384
+ # Send request
3385
+ response = requests.post(f"{api_base_url}/{DEEPLOY_CT.DEEPLOY_REQUEST_PATHS.CREATE_PIPELINE}", json=request_body)
3386
+ return response.json()
3387
+ except Exception as e:
3388
+ logger.P(f"Error during deeploy_simple_telegram_bot: {e}", color='r', show=True)
3389
+ raise e
3378
3390
 
3379
- # Sign the payload using eth_sign_payload
3380
- signature = block_engine.eth_sign_payload(
3381
- payload=request_data[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST],
3382
- indent=1,
3383
- no_hash=True,
3384
- message_prefix="Please sign this message for Deeploy: "
3385
- )
3391
+ def deeploy_custom_code(self,
3392
+ signer_private_key_path: str,
3393
+ logger,
3394
+ custom_code=None,
3395
+ target_nodes=[],
3396
+ target_nodes_count=0,
3397
+ signer_private_key_password='',
3398
+ name="deeploy_custom_code",
3399
+ signature=PLUGIN_SIGNATURES.CUSTOM_EXEC_01,
3400
+ config={},
3401
+ ):
3402
+ """
3403
+ Deploy custom Python code on the Ratio1 Edge Protocol network using the Deeploy API.
3404
+ Parameters
3405
+ ----------
3406
+ signer_private_key_path : str
3407
+ Path to the PEM file containing the private key used for signing the deployment request
3408
+
3409
+ logger : Logger
3410
+ Logger instance for recording deployment activities and errors
3411
+
3412
+ custom_code : callable
3413
+ Python function to deploy. Must be a callable function that will be serialized and deployed to edge nodes.
3414
+ The function will be validated for syntax errors before deployment.
3415
+
3416
+ target_nodes : list, optional
3417
+ List of specific node addresses to deploy the custom code to.
3418
+ If empty, uses target_nodes_count instead. Defaults to []
3419
+
3420
+ target_nodes_count : int, optional
3421
+ Number of nodes to deploy to when target_nodes is not specified. Defaults to 0
3422
+
3423
+ signer_private_key_password : str, optional
3424
+ Password for the private key file if it's encrypted. Defaults to ''
3425
+
3426
+ name : str, optional
3427
+ Application alias/name for identification. Defaults to "deeploy_custom_code"
3428
+
3429
+ signature : str, optional
3430
+ The signature of the plugin that will be used. Defaults to PLUGIN_SIGNATURES.CUSTOM_EXEC_01
3431
+
3432
+ config : dict, optional
3433
+ Additional configuration parameters to pass to the deployed custom code.
3434
+ These parameters will be available to the custom code at runtime. Defaults to {}
3435
+
3436
+ Returns
3437
+ -------
3438
+ dict
3439
+ JSON response from the Deeploy API containing deployment status and details
3386
3440
 
3441
+ """
3442
+ if target_nodes_count == 0 and len(target_nodes) == 0:
3443
+ raise ValueError("You must specify at least one target node to deploy the container app.")
3444
+
3445
+ #####################################################
3446
+
3447
+ assert callable(custom_code), "The `custom_code` method parameter must be provided."
3448
+
3449
+ base_code_checker_inst = BaseCodeChecker()
3450
+
3451
+ plain_code = base_code_checker_inst.get_function_source_code(custom_code)
3452
+ custom_code_base64, error_messages = base_code_checker_inst.code_to_base64(plain_code, return_errors=True)
3453
+ if error_messages:
3454
+ raise ValueError(f"Custom code has errors: {error_messages}")
3455
+ #####################################################
3456
+ app_params = {
3457
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS_CODE: custom_code_base64,
3458
+ **config
3459
+ }
3460
+
3461
+ api_base_url, request_body = self.__generate_deeploy_request(
3462
+ logger=logger,
3463
+ signer_private_key_path=signer_private_key_path,
3464
+ signer_private_key_password=signer_private_key_password,
3465
+ app_alias=name,
3466
+ signature=signature,
3467
+ target_nodes=target_nodes,
3468
+ target_nodes_count=target_nodes_count,
3469
+ app_params=app_params
3470
+ )
3471
+
3472
+ try:
3387
3473
  # Send request
3388
- response = requests.post(f"{api_base_url}/{DEEPLOY_CT.DEEPLOY_REQUEST_PATHS.CREATE_PIPELINE}", json=request_data)
3474
+ response = requests.post(f"{api_base_url}/{DEEPLOY_CT.DEEPLOY_REQUEST_PATHS.CREATE_PIPELINE}", json=request_body)
3389
3475
  return response.json()
3390
3476
  except Exception as e:
3391
- logger.P(f"Error during deeploy_launch_container_app: {e}", color='r', show=True)
3477
+ logger.P(f"Error during deeploy_custom_code: {e}", color='r', show=True)
3392
3478
  raise e
3393
3479
 
3480
+
3394
3481
  def deeploy_close(self, logger,
3395
3482
  signer_private_key_path: str,
3396
3483
  app_id: str,
@@ -3471,37 +3558,157 @@ class GenericSession(BaseDecentrAIObject):
3471
3558
  --------
3472
3559
  deeploy_launch_container_app : Deploy a new containerized application
3473
3560
  """
3561
+
3562
+
3563
+ api_base_url, request_body = self.__generate_deeploy_request(
3564
+ logger=logger,
3565
+ signer_private_key_path=signer_private_key_path,
3566
+ signer_private_key_password=signer_private_key_password,
3567
+ request_params={
3568
+ DEEPLOY_CT.DEEPLOY_KEYS.APP_ID: app_id,
3569
+ DEEPLOY_CT.DEEPLOY_KEYS.TARGET_NODES: target_nodes,
3570
+ }
3571
+ )
3572
+
3573
+ endpoint = DEEPLOY_CT.DEEPLOY_REQUEST_PATHS.DELETE_PIPELINE
3574
+ response = requests.post(f"{api_base_url}/{endpoint}", json=request_body)
3575
+ return response.json()
3576
+
3577
+ def __generate_deeploy_request(self,
3578
+ logger,
3579
+ signer_private_key_path,
3580
+ signer_private_key_password,
3581
+ app_alias=None,
3582
+ signature=None,
3583
+ target_nodes=None,
3584
+ target_nodes_count=0,
3585
+ app_params={},
3586
+ request_params={}):
3587
+ """
3588
+ Generate a signed Deeploy API request for deploying applications to edge nodes.
3589
+
3590
+ This method creates a properly formatted and cryptographically signed request
3591
+ that can be sent to the Deeploy API for deploying various types of applications
3592
+ (containers, custom code, Telegram bots, etc.) to the Ratio1 Edge Protocol network.
3593
+
3594
+ Parameters
3595
+ ----------
3596
+ logger : Logger
3597
+ Logger instance for recording request generation activities and errors
3598
+
3599
+ signer_private_key_path : str
3600
+ Path to the PEM file containing the private key used for signing the request
3601
+
3602
+ signer_private_key_password : str
3603
+ Password for the private key file if it's encrypted
3604
+
3605
+ app_alias : str, optional
3606
+ Application alias/name for identification in the Deeploy system.
3607
+ Required for creation requests, not needed for deletion or query operations.
3608
+ Defaults to None
3609
+
3610
+ signature : str, optional
3611
+ The plugin signature that identifies the type of application to deploy
3612
+ (e.g., CONTAINER_APP_RUNNER, TELEGRAM_BASIC_BOT_01, CUSTOM_EXEC_01).
3613
+ Required for creation requests, not needed for deletion or query operations.
3614
+ Defaults to None
3615
+
3616
+ target_nodes : list, optional
3617
+ List of specific node addresses to deploy the application to.
3618
+ If empty, uses target_nodes_count instead.
3619
+ Required for creation requests, optional for other operations.
3620
+ Defaults to None
3621
+
3622
+ target_nodes_count : int, optional
3623
+ Number of nodes to deploy to when target_nodes is not specified.
3624
+ Used when you want Deeploy to automatically select nodes.
3625
+ Defaults to 0
3626
+
3627
+ app_params : dict, optional
3628
+ Application-specific parameters that will be passed to the deployed application.
3629
+ These parameters vary depending on the application type and signature.
3630
+ Defaults to {}
3631
+
3632
+ request_params : dict, optional
3633
+ Additional parameters to include in the request payload.
3634
+ These are typically metadata or configuration options for the deployment process.
3635
+ Defaults to {}
3636
+
3637
+ Returns
3638
+ -------
3639
+ tuple
3640
+ A tuple containing (api_base_url, request_body) where:
3641
+ - api_base_url (str): The base URL for the Deeploy API endpoint
3642
+ - request_body (dict): The complete signed request payload ready for HTTP transmission
3643
+
3644
+ Examples
3645
+ --------
3646
+ Generate a request for deploying a container application:
3647
+
3648
+ >>> api_url, request = session.__generate_deeploy_request(
3649
+ ... logger=my_logger,
3650
+ ... signer_private_key_path="/path/to/private_key.pem",
3651
+ ... signer_private_key_password="",
3652
+ ... app_alias="my_web_app",
3653
+ ... signature="CONTAINER_APP_RUNNER",
3654
+ ... target_nodes=["node1_address", "node2_address"],
3655
+ ... target_nodes_count=0,
3656
+ ... app_params={"image": "nginx:latest", "port": 80}
3657
+ ... )
3658
+
3659
+ Generate a request for deleting an application (no signature/app_alias needed):
3660
+
3661
+ >>> api_url, request = session.__generate_deeploy_request(
3662
+ ... logger=my_logger,
3663
+ ... signer_private_key_path="/path/to/private_key.pem",
3664
+ ... signer_private_key_password="",
3665
+ ... request_params={"app_id": "my_app_12345", "target_nodes": ["node1_address"]}
3666
+ ... )
3667
+ """
3668
+
3669
+ request_body = {DEEPLOY_CT.DEEPLOY_KEYS.REQUEST: {
3670
+ DEEPLOY_CT.DEEPLOY_KEYS.TARGET_NODES_COUNT: target_nodes_count,
3671
+ **request_params
3672
+ }}
3673
+
3674
+ # Add creation-specific fields only if provided
3675
+ if app_alias is not None:
3676
+ request_body[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.APP_ALIAS] = app_alias
3677
+ if signature is not None:
3678
+ request_body[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.PLUGIN_SIGNATURE] = signature
3679
+ if target_nodes is not None:
3680
+ request_body[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.TARGET_NODES] = target_nodes
3681
+ if app_params:
3682
+ request_body[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.APP_PARAMS] = app_params
3683
+
3684
+ # Check if PK exists
3685
+ if not os.path.isfile(signer_private_key_path):
3686
+ raise ValueError("Private key path is not valid.")
3687
+
3474
3688
  # Create a block engine instance with the private key
3475
3689
  block_engine = DefaultBlockEngine(
3476
3690
  log=logger,
3477
- name="deeploy_close",
3691
+ name="deeploy_request_block_engine",
3478
3692
  config={
3479
3693
  BCct.K_PEM_FILE: signer_private_key_path,
3480
3694
  BCct.K_PASSWORD: signer_private_key_password,
3481
3695
  }
3482
3696
  )
3483
3697
 
3484
- current_network, api_base_url = self.__validate_deeploy_network_and_get_api_url(block_engine)
3485
-
3486
- endpoint = DEEPLOY_CT.DEEPLOY_REQUEST_PATHS.DELETE_PIPELINE
3487
- request_data = {DEEPLOY_CT.DEEPLOY_KEYS.REQUEST: {}}
3488
- request_data[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.APP_ID] = app_id
3489
- request_data[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.TARGET_NODES] = target_nodes
3490
-
3698
+ # Set the nonce for the request
3491
3699
  nonce = f"0x{int(time.time() * 1000):x}"
3492
- request_data[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.NONCE] = nonce
3700
+ request_body[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST][DEEPLOY_CT.DEEPLOY_KEYS.NONCE] = nonce
3493
3701
 
3494
3702
  # Sign the payload using eth_sign_payload
3495
3703
  block_engine.eth_sign_payload(
3496
- payload=request_data[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST],
3704
+ payload=request_body[DEEPLOY_CT.DEEPLOY_KEYS.REQUEST],
3497
3705
  indent=1,
3498
3706
  no_hash=True,
3499
3707
  message_prefix="Please sign this message for Deeploy: "
3500
3708
  )
3501
3709
 
3502
- response = requests.post(f"{api_base_url}/{endpoint}", json=request_data)
3503
- return response.json()
3504
-
3710
+ _, api_base_url = self.__validate_deeploy_network_and_get_api_url(block_engine)
3711
+ return api_base_url, request_body
3505
3712
 
3506
3713
  def __is_assets_valid(self, assets, mandatory=True, raise_exception=True, default_field_values=None):
3507
3714
  if assets is None:
@@ -11,6 +11,7 @@ from eth_utils import keccak, to_checksum_address
11
11
  from eth_account.messages import encode_defunct
12
12
 
13
13
  from ..const.base import EE_VPN_IMPL_ENV_KEY, dAuth, BCctbase, ETHVarTypes, EVM_ABI_DATA
14
+ from ..const.evm_net import EVM_NET_DATA, EvmNetData
14
15
 
15
16
  EE_VPN_IMPL = str(os.environ.get(EE_VPN_IMPL_ENV_KEY, False)).lower() in [
16
17
  'true', '1', 'yes', 'y', 't', 'on'
@@ -1291,4 +1292,19 @@ class _EVMMixin:
1291
1292
  "r1Balance": float(r1_balance),
1292
1293
  }
1293
1294
 
1294
- return balances
1295
+ return balances
1296
+
1297
+
1298
+ def get_deeploy_url(self):
1299
+ """
1300
+ Returns the deeploy URL from the environment or the network data
1301
+
1302
+ Returns
1303
+ -------
1304
+ str
1305
+ the deeploy URL.
1306
+
1307
+ """
1308
+ current_network = self.current_evm_network
1309
+ deeploy_api_base_url = EVM_NET_DATA[current_network][EvmNetData.EE_DEEPLOY_API_URL_KEY]
1310
+ return deeploy_api_base_url
@@ -10,9 +10,9 @@ and it is used by cli.py
10
10
  """
11
11
 
12
12
  from ratio1.cli.nodes import (
13
- get_nodes, get_supervisors,
13
+ get_nodes, get_supervisors,
14
14
  restart_node, shutdown_node,
15
- get_apps
15
+ get_apps, inspect_node
16
16
  )
17
17
  from ratio1.cli.oracles import get_availability, oracle_rollout
18
18
  from ratio1.utils.config import (
@@ -122,14 +122,16 @@ CLI_COMMANDS = {
122
122
  "func": restart_node,
123
123
  "description": "Restart a node",
124
124
  "params": {
125
- "node": "The node to restart"
125
+ "node": "The node to restart",
126
+ "--ignore-peering": "Ignore peering when running the command (flag)",
126
127
  }
127
128
  },
128
129
  "shutdown": {
129
130
  "func": shutdown_node,
130
131
  "description": "Shutdown a node",
131
132
  "params": {
132
- "node": "The node to shutdown"
133
+ "node": "The node to shutdown",
134
+ "--ignore-peering": "Ignore peering when running the command (flag)",
133
135
  }
134
136
  },
135
137
  "update": {
@@ -141,6 +143,14 @@ CLI_COMMANDS = {
141
143
  },
142
144
  "oracle-rollout": {
143
145
  "func": oracle_rollout,
144
- "description": "Rollout update on all nodes in the network. The rollout order is seed nodes -> oracle nodes -> all other edge nodes. This command is needed when defining new environment variables in seed nodes, in order to make it available to all nodes in the network.",
145
- }
146
+ "description": "Rollout update on all nodes in the network. The rollout order is seed nodes -> oracle nodes -> all other edge nodes. This command is needed when defining new environment variables in seed nodes, in order to make it available to all nodes in the network."
147
+ },
148
+ "inspect": {
149
+ "func": inspect_node,
150
+ "description": "Inspect a node by address or alias.",
151
+ "params": {
152
+ "node": "The node address or alias to inspect",
153
+ "--wide": "Display all available information (flag)"
154
+ }
155
+ },
146
156
  }
@@ -211,8 +211,8 @@ def get_apps(args):
211
211
  def _send_command_to_node(args, command, ignore_not_found=False):
212
212
  node = args.node
213
213
  silent = not args.verbose
214
+ ignore_peering = args.ignore_peering
214
215
 
215
-
216
216
  t1 = time()
217
217
  df, _, _, _, _, sess = _get_netstats(
218
218
  silent=silent, online_only=True, return_session=True, all_info=True,
@@ -233,17 +233,21 @@ def _send_command_to_node(args, command, ignore_not_found=False):
233
233
  node_addr = df_found.Address.values[0]
234
234
  log_with_color(f"{df_found}")
235
235
  else:
236
- log_with_color("Node '{}' <{}> not found in network (toal {} nodes, {} peered).".format(
236
+ log_with_color("Node '{}' <{}> not found in network (total {} nodes, {} peered).".format(
237
237
  node, node_addr, df.shape[0], df.Peered.sum()), color='r'
238
238
  )
239
239
  node_addr = node
240
-
240
+
241
+ if not peered and not ignore_peering:
242
+ log_with_color(f"Node '{node}' <{node_addr}> not peered, exiting...", color='r')
243
+ return
244
+
241
245
  if not peered:
242
246
  if found:
243
247
  log_with_color(f"Node '{node}' <{node_addr}> is not peered.", color='r')
244
248
  else:
245
249
  log_with_color(f"Node '{node}' <{node_addr}> may not accept this command.", color='r')
246
-
250
+
247
251
  # TODO: currently this is based on node alias, but we should be based on node address
248
252
  # and maybe even node alias
249
253
  if (found and peered) or ignore_not_found:
@@ -271,6 +275,7 @@ def restart_node(args):
271
275
  Arguments passed to the function.
272
276
  """
273
277
  node = args.node
278
+
274
279
  log_with_color(f"Attempting to restart node <{node}>", color='b')
275
280
  _send_command_to_node(args, COMMANDS.RESTART, ignore_not_found=True)
276
281
  return
@@ -286,7 +291,138 @@ def shutdown_node(args):
286
291
  Arguments passed to the function.
287
292
  """
288
293
  node = args.node
294
+
289
295
  log_with_color(f"Attempting to shutdown node <{node}>", color='b')
290
296
  _send_command_to_node(args, COMMANDS.STOP, ignore_not_found=True)
291
297
  return
292
298
 
299
+
300
+ def inspect_node(args):
301
+ """
302
+ This function is used to inspect the node info.
303
+
304
+ Parameters
305
+ ----------
306
+ args : argparse.Namespace
307
+ Arguments passed to the function.
308
+ """
309
+ node = args.node
310
+ wide = args.wide
311
+ silent = not args.verbose
312
+
313
+ if not silent:
314
+ log_with_color(f"Inspecting node <{node}>", color='b')
315
+
316
+ from ratio1 import Session
317
+ session = Session(
318
+ silent=silent
319
+ )
320
+ try:
321
+ node_address = session.get_node_address(node)
322
+ last_hb = session.get_last_hb()
323
+ node_last_hb = last_hb.get(node_address)
324
+
325
+ if not node_last_hb:
326
+ # No last HB was found for specified Address, ETH Address or alias.
327
+ raise ValueError("Could not find a node by provided address/alias.")
328
+
329
+ # Display node stats from HB:
330
+ _display_node_stats_for_hb(node_last_hb, wide)
331
+
332
+ except Exception as e:
333
+ log_with_color(f"An error occurred while trying to get node info: {e}", color='r')
334
+ finally:
335
+ session.close()
336
+ return
337
+
338
+
339
+ def _display_node_stats_for_hb(node_hb, wide=False):
340
+ """
341
+ Display main system information from node heartbeat data.
342
+
343
+ Parameters
344
+ ----------
345
+ node_hb : dict
346
+ Node heartbeat data containing system information
347
+ wide: bool
348
+ If True, display all plugins.
349
+ """
350
+ from ratio1 import HEARTBEAT_DATA
351
+ from ratio1.utils.config import log_with_color
352
+ from ratio1.const.base import BCctbase
353
+ from ratio1 import PAYLOAD_DATA
354
+
355
+ # Extract basic node info
356
+ ee_id = node_hb.get(HEARTBEAT_DATA.EE_ID, 'Unknown')
357
+ node_addr = node_hb.get(HEARTBEAT_DATA.EE_ADDR, 'Unknown')
358
+ eth_addr = node_hb.get(BCctbase.ETH_SENDER, 'Unknown')
359
+ ee_version = node_hb.get(PAYLOAD_DATA.EE_VERSION, 'Unknown')
360
+ uptime = node_hb.get(HEARTBEAT_DATA.UPTIME, 0)
361
+
362
+ # Display node header
363
+ log_with_color(f"\n=== Node Information ===", color='b')
364
+ log_with_color(f"Node alias: {ee_id}", color='g')
365
+ log_with_color(f"Address: {node_addr}", color='g')
366
+ log_with_color(f"ETH Address: {eth_addr}", color='g')
367
+ log_with_color(f"Edge Node Version: {ee_version}", color='g')
368
+ log_with_color(f"Uptime: {uptime:.1f} seconds ({uptime/3600:.1f} hours)", color='g')
369
+
370
+ # CPU Information
371
+ cpu_info = node_hb.get(HEARTBEAT_DATA.CPU, 'Unknown')
372
+ cpu_cores = node_hb.get(HEARTBEAT_DATA.CPU_NR_CORES, 0)
373
+ cpu_used = node_hb.get(HEARTBEAT_DATA.CPU_USED, 0)
374
+
375
+ log_with_color(f"\n=== CPU Information ===", color='b')
376
+ log_with_color(f"Processor: {cpu_info}", color='y')
377
+ log_with_color(f"Cores: {cpu_cores}", color='y')
378
+ log_with_color(f"Usage: {cpu_used:.1f}%", color='y')
379
+
380
+ # Memory Information
381
+ total_memory = node_hb.get(HEARTBEAT_DATA.MACHINE_MEMORY, 0)
382
+ available_memory = node_hb.get(HEARTBEAT_DATA.AVAILABLE_MEMORY, 0)
383
+ process_memory = node_hb.get(HEARTBEAT_DATA.PROCESS_MEMORY, 0)
384
+ is_alert_ram = node_hb.get(HEARTBEAT_DATA.IS_ALERT_RAM, False)
385
+
386
+ used_memory = total_memory - available_memory if total_memory and available_memory else 0
387
+ memory_usage_pct = (used_memory / total_memory * 100) if total_memory > 0 else 0
388
+
389
+ log_with_color(f"\n=== Memory Information ===", color='b')
390
+ log_with_color(f"Total Memory: {total_memory:.2f} GB", color='y')
391
+ log_with_color(f"Available Memory: {available_memory:.2f} GB", color='y')
392
+ log_with_color(f"Used Memory: {used_memory:.2f} GB ({memory_usage_pct:.1f}%)", color='y')
393
+ log_with_color(f"Process Memory: {process_memory:.2f} GB", color='y')
394
+ if is_alert_ram:
395
+ log_with_color(f"RAM Alert: True", color='r')
396
+
397
+ # Disk Information
398
+ total_disk = node_hb.get(HEARTBEAT_DATA.TOTAL_DISK, 0)
399
+ available_disk = node_hb.get(HEARTBEAT_DATA.AVAILABLE_DISK, 0)
400
+
401
+ used_disk = total_disk - available_disk if total_disk and available_disk else 0
402
+ disk_usage_pct = (used_disk / total_disk * 100) if total_disk > 0 else 0
403
+
404
+ log_with_color(f"\n=== Disk Information ===", color='b')
405
+ log_with_color(f"Total Disk: {total_disk:.2f} GB", color='y')
406
+ log_with_color(f"Available Disk: {available_disk:.2f} GB", color='y')
407
+ log_with_color(f"Used Disk: {used_disk:.2f} GB ({disk_usage_pct:.1f}%)", color='y')
408
+
409
+ # Active Plugins
410
+ active_plugins = node_hb.get(HEARTBEAT_DATA.ACTIVE_PLUGINS, [])
411
+ log_with_color(f"\n=== Active Plugins ===", color='b')
412
+ log_with_color(f"Number of Active Plugins: {len(active_plugins)}", color='y')
413
+
414
+ if active_plugins:
415
+ plugins_to_display = 99999 if wide else 5
416
+ for i, plugin in enumerate(active_plugins[:plugins_to_display]): # Show first 5 plugins
417
+ stream_id = plugin.get(HEARTBEAT_DATA.ACTIVE_PLUGINS_INFO.STREAM_ID, 'Unknown')
418
+ signature = plugin.get(HEARTBEAT_DATA.ACTIVE_PLUGINS_INFO.SIGNATURE, 'Unknown')
419
+ instance_id = plugin.get(HEARTBEAT_DATA.ACTIVE_PLUGINS_INFO.INSTANCE_ID, 'Unknown')
420
+ total_payload = plugin.get(HEARTBEAT_DATA.ACTIVE_PLUGINS_INFO.TOTAL_PAYLOAD_COUNT, 0)
421
+ log_with_color(f" {i+1}. {stream_id} {signature} ({instance_id}) - {total_payload} payloads", color='w')
422
+
423
+ if len(active_plugins) > plugins_to_display:
424
+ log_with_color(f" ... and {len(active_plugins) - plugins_to_display} more plugins", color='w')
425
+
426
+ log_with_color(f"\n", color='w') # Add final newline for spacing
427
+
428
+ return