scitex 2.0.0__py2.py3-none-any.whl → 2.1.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (704) hide show
  1. scitex/__init__.py +53 -15
  2. scitex/__main__.py +72 -26
  3. scitex/__version__.py +1 -1
  4. scitex/_sh.py +145 -23
  5. scitex/ai/__init__.py +30 -16
  6. scitex/ai/_gen_ai/_Anthropic.py +5 -7
  7. scitex/ai/_gen_ai/_BaseGenAI.py +2 -2
  8. scitex/ai/_gen_ai/_DeepSeek.py +10 -2
  9. scitex/ai/_gen_ai/_Google.py +2 -2
  10. scitex/ai/_gen_ai/_Llama.py +2 -2
  11. scitex/ai/_gen_ai/_OpenAI.py +2 -2
  12. scitex/ai/_gen_ai/_PARAMS.py +51 -65
  13. scitex/ai/_gen_ai/_Perplexity.py +2 -2
  14. scitex/ai/_gen_ai/__init__.py +25 -14
  15. scitex/ai/_gen_ai/_format_output_func.py +4 -4
  16. scitex/ai/classification/{classifier_server.py → Classifier.py} +5 -5
  17. scitex/ai/classification/CrossValidationExperiment.py +374 -0
  18. scitex/ai/classification/__init__.py +43 -4
  19. scitex/ai/classification/reporters/_BaseClassificationReporter.py +281 -0
  20. scitex/ai/classification/reporters/_ClassificationReporter.py +773 -0
  21. scitex/ai/classification/reporters/_MultiClassificationReporter.py +406 -0
  22. scitex/ai/classification/reporters/_SingleClassificationReporter.py +1834 -0
  23. scitex/ai/classification/reporters/__init__.py +11 -0
  24. scitex/ai/classification/reporters/reporter_utils/_Plotter.py +1028 -0
  25. scitex/ai/classification/reporters/reporter_utils/__init__.py +80 -0
  26. scitex/ai/classification/reporters/reporter_utils/aggregation.py +457 -0
  27. scitex/ai/classification/reporters/reporter_utils/data_models.py +313 -0
  28. scitex/ai/classification/reporters/reporter_utils/reporting.py +1056 -0
  29. scitex/ai/classification/reporters/reporter_utils/storage.py +221 -0
  30. scitex/ai/classification/reporters/reporter_utils/validation.py +395 -0
  31. scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
  32. scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
  33. scitex/ai/classification/timeseries/_TimeSeriesMetadata.py +139 -0
  34. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
  35. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
  36. scitex/ai/classification/timeseries/_TimeSeriesStrategy.py +84 -0
  37. scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
  38. scitex/ai/classification/timeseries/__init__.py +39 -0
  39. scitex/ai/classification/timeseries/_normalize_timestamp.py +436 -0
  40. scitex/ai/clustering/_umap.py +2 -2
  41. scitex/ai/feature_extraction/vit.py +1 -0
  42. scitex/ai/feature_selection/__init__.py +30 -0
  43. scitex/ai/feature_selection/feature_selection.py +364 -0
  44. scitex/ai/loss/multi_task_loss.py +1 -1
  45. scitex/ai/metrics/__init__.py +51 -4
  46. scitex/ai/metrics/_calc_bacc.py +61 -0
  47. scitex/ai/metrics/_calc_bacc_from_conf_mat.py +38 -0
  48. scitex/ai/metrics/_calc_clf_report.py +78 -0
  49. scitex/ai/metrics/_calc_conf_mat.py +93 -0
  50. scitex/ai/metrics/_calc_feature_importance.py +183 -0
  51. scitex/ai/metrics/_calc_mcc.py +61 -0
  52. scitex/ai/metrics/_calc_pre_rec_auc.py +116 -0
  53. scitex/ai/metrics/_calc_roc_auc.py +110 -0
  54. scitex/ai/metrics/_calc_seizure_prediction_metrics.py +490 -0
  55. scitex/ai/metrics/{silhoute_score_block.py → _calc_silhouette_score.py} +15 -8
  56. scitex/ai/metrics/_normalize_labels.py +83 -0
  57. scitex/ai/plt/__init__.py +47 -8
  58. scitex/ai/plt/{_conf_mat.py → _plot_conf_mat.py} +158 -87
  59. scitex/ai/plt/_plot_feature_importance.py +323 -0
  60. scitex/ai/plt/_plot_learning_curve.py +345 -0
  61. scitex/ai/plt/_plot_optuna_study.py +225 -0
  62. scitex/ai/plt/_plot_pre_rec_curve.py +290 -0
  63. scitex/ai/plt/_plot_roc_curve.py +255 -0
  64. scitex/ai/training/{learning_curve_logger.py → _LearningCurveLogger.py} +197 -213
  65. scitex/ai/training/__init__.py +2 -2
  66. scitex/ai/utils/grid_search.py +3 -3
  67. scitex/benchmark/__init__.py +52 -0
  68. scitex/benchmark/benchmark.py +400 -0
  69. scitex/benchmark/monitor.py +370 -0
  70. scitex/benchmark/profiler.py +297 -0
  71. scitex/browser/__init__.py +48 -0
  72. scitex/browser/automation/CookieHandler.py +216 -0
  73. scitex/browser/automation/__init__.py +7 -0
  74. scitex/browser/collaboration/__init__.py +55 -0
  75. scitex/browser/collaboration/auth_helpers.py +94 -0
  76. scitex/browser/collaboration/collaborative_agent.py +136 -0
  77. scitex/browser/collaboration/credential_manager.py +188 -0
  78. scitex/browser/collaboration/interactive_panel.py +400 -0
  79. scitex/browser/collaboration/persistent_browser.py +170 -0
  80. scitex/browser/collaboration/shared_session.py +383 -0
  81. scitex/browser/collaboration/standard_interactions.py +246 -0
  82. scitex/browser/collaboration/visual_feedback.py +181 -0
  83. scitex/browser/core/BrowserMixin.py +326 -0
  84. scitex/browser/core/ChromeProfileManager.py +446 -0
  85. scitex/browser/core/__init__.py +9 -0
  86. scitex/browser/debugging/__init__.py +18 -0
  87. scitex/browser/debugging/_browser_logger.py +657 -0
  88. scitex/browser/debugging/_highlight_element.py +143 -0
  89. scitex/browser/debugging/_show_grid.py +154 -0
  90. scitex/browser/interaction/__init__.py +24 -0
  91. scitex/browser/interaction/click_center.py +149 -0
  92. scitex/browser/interaction/click_with_fallbacks.py +206 -0
  93. scitex/browser/interaction/close_popups.py +498 -0
  94. scitex/browser/interaction/fill_with_fallbacks.py +209 -0
  95. scitex/browser/pdf/__init__.py +14 -0
  96. scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +200 -0
  97. scitex/browser/pdf/detect_chrome_pdf_viewer.py +198 -0
  98. scitex/browser/remote/CaptchaHandler.py +434 -0
  99. scitex/browser/remote/ZenRowsAPIClient.py +347 -0
  100. scitex/browser/remote/ZenRowsBrowserManager.py +570 -0
  101. scitex/browser/remote/__init__.py +11 -0
  102. scitex/browser/stealth/HumanBehavior.py +344 -0
  103. scitex/browser/stealth/StealthManager.py +1008 -0
  104. scitex/browser/stealth/__init__.py +9 -0
  105. scitex/browser/template.py +122 -0
  106. scitex/capture/__init__.py +110 -0
  107. scitex/capture/__main__.py +25 -0
  108. scitex/capture/capture.py +848 -0
  109. scitex/capture/cli.py +233 -0
  110. scitex/capture/gif.py +344 -0
  111. scitex/capture/mcp_server.py +961 -0
  112. scitex/capture/session.py +70 -0
  113. scitex/capture/utils.py +705 -0
  114. scitex/cli/__init__.py +17 -0
  115. scitex/cli/cloud.py +447 -0
  116. scitex/cli/main.py +42 -0
  117. scitex/cli/scholar.py +280 -0
  118. scitex/context/_suppress_output.py +5 -3
  119. scitex/db/__init__.py +30 -3
  120. scitex/db/__main__.py +75 -0
  121. scitex/db/_check_health.py +381 -0
  122. scitex/db/_delete_duplicates.py +25 -386
  123. scitex/db/_inspect.py +335 -114
  124. scitex/db/_inspect_optimized.py +301 -0
  125. scitex/db/{_PostgreSQL.py → _postgresql/_PostgreSQL.py} +3 -3
  126. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BackupMixin.py +1 -1
  127. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BatchMixin.py +1 -1
  128. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_BlobMixin.py +1 -1
  129. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ConnectionMixin.py +1 -1
  130. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_MaintenanceMixin.py +1 -1
  131. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_QueryMixin.py +1 -1
  132. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_SchemaMixin.py +1 -1
  133. scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TransactionMixin.py +1 -1
  134. scitex/db/_postgresql/__init__.py +6 -0
  135. scitex/db/_sqlite3/_SQLite3.py +210 -0
  136. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin.py +581 -0
  137. scitex/db/_sqlite3/_SQLite3Mixins/_ArrayMixin_v01-need-_hash-col.py +517 -0
  138. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_BatchMixin.py +1 -1
  139. scitex/db/_sqlite3/_SQLite3Mixins/_BlobMixin.py +281 -0
  140. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin.py +548 -0
  141. scitex/db/_sqlite3/_SQLite3Mixins/_ColumnMixin_v01-indentation-issues.py +583 -0
  142. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ConnectionMixin.py +29 -13
  143. scitex/db/_sqlite3/_SQLite3Mixins/_GitMixin.py +583 -0
  144. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_ImportExportMixin.py +1 -1
  145. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_IndexMixin.py +1 -1
  146. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_MaintenanceMixin.py +2 -1
  147. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_QueryMixin.py +37 -10
  148. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_RowMixin.py +46 -6
  149. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TableMixin.py +56 -10
  150. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/_TransactionMixin.py +1 -1
  151. scitex/db/{_SQLite3Mixins → _sqlite3/_SQLite3Mixins}/__init__.py +14 -2
  152. scitex/db/_sqlite3/__init__.py +7 -0
  153. scitex/db/_sqlite3/_delete_duplicates.py +274 -0
  154. scitex/decorators/__init__.py +2 -0
  155. scitex/decorators/_cache_disk.py +13 -5
  156. scitex/decorators/_cache_disk_async.py +49 -0
  157. scitex/decorators/_deprecated.py +175 -10
  158. scitex/decorators/_timeout.py +1 -1
  159. scitex/dev/_analyze_code_flow.py +2 -2
  160. scitex/dict/_DotDict.py +73 -15
  161. scitex/dict/_DotDict_v01-not-handling-recursive-instantiations.py +442 -0
  162. scitex/dict/_DotDict_v02-not-serializing-Path-object.py +446 -0
  163. scitex/dict/__init__.py +2 -0
  164. scitex/dict/_flatten.py +27 -0
  165. scitex/dsp/_crop.py +2 -2
  166. scitex/dsp/_demo_sig.py +2 -2
  167. scitex/dsp/_detect_ripples.py +2 -2
  168. scitex/dsp/_hilbert.py +2 -2
  169. scitex/dsp/_listen.py +6 -6
  170. scitex/dsp/_modulation_index.py +2 -2
  171. scitex/dsp/_pac.py +1 -1
  172. scitex/dsp/_psd.py +2 -2
  173. scitex/dsp/_resample.py +2 -1
  174. scitex/dsp/_time.py +3 -2
  175. scitex/dsp/_wavelet.py +3 -2
  176. scitex/dsp/add_noise.py +2 -2
  177. scitex/dsp/example.py +1 -0
  178. scitex/dsp/filt.py +10 -9
  179. scitex/dsp/template.py +3 -2
  180. scitex/dsp/utils/_differential_bandpass_filters.py +1 -1
  181. scitex/dsp/utils/pac.py +2 -2
  182. scitex/dt/_normalize_timestamp.py +432 -0
  183. scitex/errors.py +572 -0
  184. scitex/gen/_DimHandler.py +2 -2
  185. scitex/gen/__init__.py +37 -7
  186. scitex/gen/_deprecated_close.py +80 -0
  187. scitex/gen/_deprecated_start.py +26 -0
  188. scitex/gen/_detect_environment.py +152 -0
  189. scitex/gen/_detect_notebook_path.py +169 -0
  190. scitex/gen/_embed.py +6 -2
  191. scitex/gen/_get_notebook_path.py +257 -0
  192. scitex/gen/_less.py +1 -1
  193. scitex/gen/_list_packages.py +2 -2
  194. scitex/gen/_norm.py +44 -9
  195. scitex/gen/_norm_cache.py +269 -0
  196. scitex/gen/_src.py +3 -5
  197. scitex/gen/_title_case.py +3 -3
  198. scitex/io/__init__.py +28 -6
  199. scitex/io/_glob.py +13 -7
  200. scitex/io/_load.py +108 -21
  201. scitex/io/_load_cache.py +303 -0
  202. scitex/io/_load_configs.py +40 -15
  203. scitex/io/{_H5Explorer.py → _load_modules/_H5Explorer.py} +80 -17
  204. scitex/io/_load_modules/_ZarrExplorer.py +114 -0
  205. scitex/io/_load_modules/_bibtex.py +207 -0
  206. scitex/io/_load_modules/_hdf5.py +53 -178
  207. scitex/io/_load_modules/_json.py +5 -3
  208. scitex/io/_load_modules/_pdf.py +871 -16
  209. scitex/io/_load_modules/_sqlite3.py +15 -0
  210. scitex/io/_load_modules/_txt.py +41 -12
  211. scitex/io/_load_modules/_yaml.py +4 -3
  212. scitex/io/_load_modules/_zarr.py +126 -0
  213. scitex/io/_save.py +429 -171
  214. scitex/io/_save_modules/__init__.py +6 -0
  215. scitex/io/_save_modules/_bibtex.py +194 -0
  216. scitex/io/_save_modules/_csv.py +8 -4
  217. scitex/io/_save_modules/_excel.py +174 -15
  218. scitex/io/_save_modules/_hdf5.py +251 -226
  219. scitex/io/_save_modules/_image.py +1 -3
  220. scitex/io/_save_modules/_json.py +49 -4
  221. scitex/io/_save_modules/_listed_dfs_as_csv.py +1 -3
  222. scitex/io/_save_modules/_listed_scalars_as_csv.py +1 -3
  223. scitex/io/_save_modules/_tex.py +277 -0
  224. scitex/io/_save_modules/_yaml.py +42 -3
  225. scitex/io/_save_modules/_zarr.py +160 -0
  226. scitex/io/utils/__init__.py +20 -0
  227. scitex/io/utils/h5_to_zarr.py +616 -0
  228. scitex/linalg/_geometric_median.py +6 -2
  229. scitex/{gen/_tee.py → logging/_Tee.py} +43 -84
  230. scitex/logging/__init__.py +122 -0
  231. scitex/logging/_config.py +158 -0
  232. scitex/logging/_context.py +103 -0
  233. scitex/logging/_formatters.py +128 -0
  234. scitex/logging/_handlers.py +64 -0
  235. scitex/logging/_levels.py +35 -0
  236. scitex/logging/_logger.py +163 -0
  237. scitex/logging/_print_capture.py +95 -0
  238. scitex/ml/__init__.py +69 -0
  239. scitex/{ai/genai/anthropic.py → ml/_gen_ai/_Anthropic.py} +13 -19
  240. scitex/{ai/genai/base_genai.py → ml/_gen_ai/_BaseGenAI.py} +5 -5
  241. scitex/{ai/genai/deepseek.py → ml/_gen_ai/_DeepSeek.py} +11 -16
  242. scitex/{ai/genai/google.py → ml/_gen_ai/_Google.py} +7 -15
  243. scitex/{ai/genai/groq.py → ml/_gen_ai/_Groq.py} +1 -8
  244. scitex/{ai/genai/llama.py → ml/_gen_ai/_Llama.py} +3 -16
  245. scitex/{ai/genai/openai.py → ml/_gen_ai/_OpenAI.py} +3 -3
  246. scitex/{ai/genai/params.py → ml/_gen_ai/_PARAMS.py} +51 -65
  247. scitex/{ai/genai/perplexity.py → ml/_gen_ai/_Perplexity.py} +3 -14
  248. scitex/ml/_gen_ai/__init__.py +43 -0
  249. scitex/{ai/genai/calc_cost.py → ml/_gen_ai/_calc_cost.py} +1 -1
  250. scitex/{ai/genai/format_output_func.py → ml/_gen_ai/_format_output_func.py} +4 -4
  251. scitex/{ai/genai/genai_factory.py → ml/_gen_ai/_genai_factory.py} +8 -8
  252. scitex/ml/activation/__init__.py +8 -0
  253. scitex/ml/activation/_define.py +11 -0
  254. scitex/{ai/classifier_server.py → ml/classification/Classifier.py} +5 -5
  255. scitex/ml/classification/CrossValidationExperiment.py +374 -0
  256. scitex/ml/classification/__init__.py +46 -0
  257. scitex/ml/classification/reporters/_BaseClassificationReporter.py +281 -0
  258. scitex/ml/classification/reporters/_ClassificationReporter.py +773 -0
  259. scitex/ml/classification/reporters/_MultiClassificationReporter.py +406 -0
  260. scitex/ml/classification/reporters/_SingleClassificationReporter.py +1834 -0
  261. scitex/ml/classification/reporters/__init__.py +11 -0
  262. scitex/ml/classification/reporters/reporter_utils/_Plotter.py +1028 -0
  263. scitex/ml/classification/reporters/reporter_utils/__init__.py +80 -0
  264. scitex/ml/classification/reporters/reporter_utils/aggregation.py +457 -0
  265. scitex/ml/classification/reporters/reporter_utils/data_models.py +313 -0
  266. scitex/ml/classification/reporters/reporter_utils/reporting.py +1056 -0
  267. scitex/ml/classification/reporters/reporter_utils/storage.py +221 -0
  268. scitex/ml/classification/reporters/reporter_utils/validation.py +395 -0
  269. scitex/ml/classification/timeseries/_TimeSeriesBlockingSplit.py +568 -0
  270. scitex/ml/classification/timeseries/_TimeSeriesCalendarSplit.py +688 -0
  271. scitex/ml/classification/timeseries/_TimeSeriesMetadata.py +139 -0
  272. scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +1716 -0
  273. scitex/ml/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +1685 -0
  274. scitex/ml/classification/timeseries/_TimeSeriesStrategy.py +84 -0
  275. scitex/ml/classification/timeseries/_TimeSeriesStratifiedSplit.py +610 -0
  276. scitex/ml/classification/timeseries/__init__.py +39 -0
  277. scitex/ml/classification/timeseries/_normalize_timestamp.py +436 -0
  278. scitex/ml/clustering/__init__.py +11 -0
  279. scitex/ml/clustering/_pca.py +115 -0
  280. scitex/ml/clustering/_umap.py +376 -0
  281. scitex/ml/feature_extraction/__init__.py +56 -0
  282. scitex/ml/feature_extraction/vit.py +149 -0
  283. scitex/ml/feature_selection/__init__.py +30 -0
  284. scitex/ml/feature_selection/feature_selection.py +364 -0
  285. scitex/ml/loss/_L1L2Losses.py +34 -0
  286. scitex/ml/loss/__init__.py +12 -0
  287. scitex/ml/loss/multi_task_loss.py +47 -0
  288. scitex/ml/metrics/__init__.py +56 -0
  289. scitex/ml/metrics/_calc_bacc.py +61 -0
  290. scitex/ml/metrics/_calc_bacc_from_conf_mat.py +38 -0
  291. scitex/ml/metrics/_calc_clf_report.py +78 -0
  292. scitex/ml/metrics/_calc_conf_mat.py +93 -0
  293. scitex/ml/metrics/_calc_feature_importance.py +183 -0
  294. scitex/ml/metrics/_calc_mcc.py +61 -0
  295. scitex/ml/metrics/_calc_pre_rec_auc.py +116 -0
  296. scitex/ml/metrics/_calc_roc_auc.py +110 -0
  297. scitex/ml/metrics/_calc_seizure_prediction_metrics.py +490 -0
  298. scitex/ml/metrics/_calc_silhouette_score.py +503 -0
  299. scitex/ml/metrics/_normalize_labels.py +83 -0
  300. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/__init__.py +0 -0
  301. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/__init__.py +3 -0
  302. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger.py +207 -0
  303. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger2020.py +238 -0
  304. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/ranger913A.py +215 -0
  305. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/ranger/rangerqh.py +184 -0
  306. scitex/ml/optim/Ranger_Deep_Learning_Optimizer/setup.py +24 -0
  307. scitex/ml/optim/__init__.py +13 -0
  308. scitex/ml/optim/_get_set.py +31 -0
  309. scitex/ml/optim/_optimizers.py +71 -0
  310. scitex/ml/plt/__init__.py +60 -0
  311. scitex/ml/plt/_plot_conf_mat.py +663 -0
  312. scitex/ml/plt/_plot_feature_importance.py +323 -0
  313. scitex/ml/plt/_plot_learning_curve.py +345 -0
  314. scitex/ml/plt/_plot_optuna_study.py +225 -0
  315. scitex/ml/plt/_plot_pre_rec_curve.py +290 -0
  316. scitex/ml/plt/_plot_roc_curve.py +255 -0
  317. scitex/ml/sk/__init__.py +11 -0
  318. scitex/ml/sk/_clf.py +58 -0
  319. scitex/ml/sk/_to_sktime.py +100 -0
  320. scitex/ml/sklearn/__init__.py +26 -0
  321. scitex/ml/sklearn/clf.py +58 -0
  322. scitex/ml/sklearn/to_sktime.py +100 -0
  323. scitex/{ai/training/early_stopping.py → ml/training/_EarlyStopping.py} +1 -2
  324. scitex/{ai → ml/training}/_LearningCurveLogger.py +198 -242
  325. scitex/ml/training/__init__.py +7 -0
  326. scitex/ml/utils/__init__.py +22 -0
  327. scitex/ml/utils/_check_params.py +50 -0
  328. scitex/ml/utils/_default_dataset.py +46 -0
  329. scitex/ml/utils/_format_samples_for_sktime.py +26 -0
  330. scitex/ml/utils/_label_encoder.py +134 -0
  331. scitex/ml/utils/_merge_labels.py +22 -0
  332. scitex/ml/utils/_sliding_window_data_augmentation.py +11 -0
  333. scitex/ml/utils/_under_sample.py +51 -0
  334. scitex/ml/utils/_verify_n_gpus.py +16 -0
  335. scitex/ml/utils/grid_search.py +148 -0
  336. scitex/nn/_BNet.py +15 -9
  337. scitex/nn/_Filters.py +2 -2
  338. scitex/nn/_ModulationIndex.py +2 -2
  339. scitex/nn/_PAC.py +1 -1
  340. scitex/nn/_Spectrogram.py +12 -3
  341. scitex/nn/__init__.py +9 -10
  342. scitex/path/__init__.py +18 -0
  343. scitex/path/_clean.py +4 -0
  344. scitex/path/_find.py +9 -4
  345. scitex/path/_symlink.py +348 -0
  346. scitex/path/_version.py +4 -3
  347. scitex/pd/__init__.py +2 -0
  348. scitex/pd/_get_unique.py +99 -0
  349. scitex/plt/__init__.py +114 -5
  350. scitex/plt/_subplots/_AxesWrapper.py +1 -3
  351. scitex/plt/_subplots/_AxisWrapper.py +7 -3
  352. scitex/plt/_subplots/_AxisWrapperMixins/_AdjustmentMixin.py +47 -13
  353. scitex/plt/_subplots/_AxisWrapperMixins/_MatplotlibPlotMixin.py +160 -2
  354. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin.py +26 -4
  355. scitex/plt/_subplots/_AxisWrapperMixins/_UnitAwareMixin.py +322 -0
  356. scitex/plt/_subplots/_AxisWrapperMixins/__init__.py +1 -0
  357. scitex/plt/_subplots/_FigWrapper.py +62 -6
  358. scitex/plt/_subplots/_export_as_csv.py +43 -27
  359. scitex/plt/_subplots/_export_as_csv_formatters/__init__.py +5 -4
  360. scitex/plt/_subplots/_export_as_csv_formatters/_format_annotate.py +81 -0
  361. scitex/plt/_subplots/_export_as_csv_formatters/_format_bar.py +1 -3
  362. scitex/plt/_subplots/_export_as_csv_formatters/_format_barh.py +20 -5
  363. scitex/plt/_subplots/_export_as_csv_formatters/_format_boxplot.py +1 -3
  364. scitex/plt/_subplots/_export_as_csv_formatters/_format_contour.py +1 -3
  365. scitex/plt/_subplots/_export_as_csv_formatters/_format_errorbar.py +35 -18
  366. scitex/plt/_subplots/_export_as_csv_formatters/_format_eventplot.py +1 -3
  367. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill.py +1 -3
  368. scitex/plt/_subplots/_export_as_csv_formatters/_format_fill_between.py +1 -3
  369. scitex/plt/_subplots/_export_as_csv_formatters/_format_hist.py +1 -3
  370. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow.py +1 -3
  371. scitex/plt/_subplots/_export_as_csv_formatters/_format_imshow2d.py +1 -3
  372. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot.py +15 -3
  373. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_box.py +1 -3
  374. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_conf_mat.py +1 -3
  375. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_ecdf.py +1 -3
  376. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_fillv.py +1 -3
  377. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_heatmap.py +1 -3
  378. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_image.py +1 -3
  379. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_joyplot.py +1 -3
  380. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_kde.py +1 -3
  381. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_line.py +1 -3
  382. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_ci.py +1 -3
  383. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_mean_std.py +1 -3
  384. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_median_iqr.py +1 -3
  385. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_raster.py +1 -3
  386. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_rectangle.py +1 -3
  387. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter.py +35 -0
  388. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_scatter_hist.py +1 -3
  389. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_shaded_line.py +1 -3
  390. scitex/plt/_subplots/_export_as_csv_formatters/_format_plot_violin.py +1 -3
  391. scitex/plt/_subplots/_export_as_csv_formatters/_format_scatter.py +6 -4
  392. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_barplot.py +1 -3
  393. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_boxplot.py +1 -3
  394. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_heatmap.py +1 -3
  395. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_histplot.py +1 -3
  396. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_jointplot.py +1 -3
  397. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_kdeplot.py +1 -3
  398. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_lineplot.py +1 -3
  399. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_pairplot.py +1 -3
  400. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_scatterplot.py +1 -3
  401. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_stripplot.py +1 -3
  402. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_swarmplot.py +1 -3
  403. scitex/plt/_subplots/_export_as_csv_formatters/_format_sns_violinplot.py +1 -3
  404. scitex/plt/_subplots/_export_as_csv_formatters/_format_text.py +60 -0
  405. scitex/plt/_subplots/_export_as_csv_formatters/_format_violin.py +1 -3
  406. scitex/plt/_subplots/_export_as_csv_formatters/_format_violinplot.py +1 -3
  407. scitex/plt/_subplots/_export_as_csv_formatters/test_formatters.py +1 -3
  408. scitex/plt/_subplots/_export_as_csv_formatters.py +56 -59
  409. scitex/plt/ax/_style/_hide_spines.py +1 -3
  410. scitex/plt/ax/_style/_rotate_labels.py +180 -76
  411. scitex/plt/ax/_style/_rotate_labels_v01.py +248 -0
  412. scitex/plt/ax/_style/_set_meta.py +11 -4
  413. scitex/plt/ax/_style/_set_supxyt.py +3 -3
  414. scitex/plt/ax/_style/_set_xyt.py +3 -3
  415. scitex/plt/ax/_style/_share_axes.py +2 -2
  416. scitex/plt/color/__init__.py +4 -4
  417. scitex/plt/color/{_get_colors_from_cmap.py → _get_colors_from_conf_matap.py} +7 -7
  418. scitex/plt/utils/_configure_mpl.py +99 -86
  419. scitex/plt/utils/_histogram_utils.py +1 -3
  420. scitex/plt/utils/_is_valid_axis.py +1 -3
  421. scitex/plt/utils/_scitex_config.py +1 -0
  422. scitex/repro/__init__.py +75 -0
  423. scitex/{reproduce → repro}/_gen_ID.py +1 -1
  424. scitex/{reproduce → repro}/_gen_timestamp.py +1 -1
  425. scitex/repro_rng/_RandomStateManager.py +590 -0
  426. scitex/repro_rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
  427. scitex/repro_rng/__init__.py +39 -0
  428. scitex/reproduce/__init__.py +25 -13
  429. scitex/reproduce/_hash_array.py +22 -0
  430. scitex/resource/_get_processor_usages.py +4 -4
  431. scitex/resource/_get_specs.py +2 -2
  432. scitex/resource/_log_processor_usages.py +2 -2
  433. scitex/rng/_RandomStateManager.py +590 -0
  434. scitex/rng/_RandomStateManager_v01-no-verbose-options.py +414 -0
  435. scitex/rng/__init__.py +39 -0
  436. scitex/scholar/__init__.py +309 -19
  437. scitex/scholar/__main__.py +319 -0
  438. scitex/scholar/auth/ScholarAuthManager.py +308 -0
  439. scitex/scholar/auth/__init__.py +12 -0
  440. scitex/scholar/auth/core/AuthenticationGateway.py +473 -0
  441. scitex/scholar/auth/core/BrowserAuthenticator.py +386 -0
  442. scitex/scholar/auth/core/StrategyResolver.py +309 -0
  443. scitex/scholar/auth/core/__init__.py +16 -0
  444. scitex/scholar/auth/gateway/_OpenURLLinkFinder.py +120 -0
  445. scitex/scholar/auth/gateway/_OpenURLResolver.py +209 -0
  446. scitex/scholar/auth/gateway/__init__.py +38 -0
  447. scitex/scholar/auth/gateway/_resolve_functions.py +101 -0
  448. scitex/scholar/auth/providers/BaseAuthenticator.py +166 -0
  449. scitex/scholar/auth/providers/EZProxyAuthenticator.py +484 -0
  450. scitex/scholar/auth/providers/OpenAthensAuthenticator.py +619 -0
  451. scitex/scholar/auth/providers/ShibbolethAuthenticator.py +686 -0
  452. scitex/scholar/auth/providers/__init__.py +18 -0
  453. scitex/scholar/auth/session/AuthCacheManager.py +189 -0
  454. scitex/scholar/auth/session/SessionManager.py +159 -0
  455. scitex/scholar/auth/session/__init__.py +11 -0
  456. scitex/scholar/auth/sso/BaseSSOAutomator.py +373 -0
  457. scitex/scholar/auth/sso/OpenAthensSSOAutomator.py +378 -0
  458. scitex/scholar/auth/sso/SSOAutomator.py +180 -0
  459. scitex/scholar/auth/sso/UniversityOfMelbourneSSOAutomator.py +380 -0
  460. scitex/scholar/auth/sso/__init__.py +15 -0
  461. scitex/scholar/browser/ScholarBrowserManager.py +705 -0
  462. scitex/scholar/browser/__init__.py +38 -0
  463. scitex/scholar/browser/utils/__init__.py +13 -0
  464. scitex/scholar/browser/utils/click_and_wait.py +205 -0
  465. scitex/scholar/browser/utils/close_unwanted_pages.py +140 -0
  466. scitex/scholar/browser/utils/wait_redirects.py +732 -0
  467. scitex/scholar/config/PublisherRules.py +132 -0
  468. scitex/scholar/config/ScholarConfig.py +126 -0
  469. scitex/scholar/config/__init__.py +17 -0
  470. scitex/scholar/core/Paper.py +627 -0
  471. scitex/scholar/core/Papers.py +722 -0
  472. scitex/scholar/core/Scholar.py +1975 -0
  473. scitex/scholar/core/__init__.py +9 -0
  474. scitex/scholar/impact_factor/ImpactFactorEngine.py +204 -0
  475. scitex/scholar/impact_factor/__init__.py +20 -0
  476. scitex/scholar/impact_factor/estimation/ImpactFactorEstimationEngine.py +0 -0
  477. scitex/scholar/impact_factor/estimation/__init__.py +40 -0
  478. scitex/scholar/impact_factor/estimation/build_database.py +0 -0
  479. scitex/scholar/impact_factor/estimation/core/__init__.py +28 -0
  480. scitex/scholar/impact_factor/estimation/core/cache_manager.py +523 -0
  481. scitex/scholar/impact_factor/estimation/core/calculator.py +355 -0
  482. scitex/scholar/impact_factor/estimation/core/journal_matcher.py +428 -0
  483. scitex/scholar/integration/__init__.py +59 -0
  484. scitex/scholar/integration/base.py +502 -0
  485. scitex/scholar/integration/mendeley/__init__.py +22 -0
  486. scitex/scholar/integration/mendeley/exporter.py +166 -0
  487. scitex/scholar/integration/mendeley/importer.py +236 -0
  488. scitex/scholar/integration/mendeley/linker.py +79 -0
  489. scitex/scholar/integration/mendeley/mapper.py +212 -0
  490. scitex/scholar/integration/zotero/__init__.py +27 -0
  491. scitex/scholar/integration/zotero/__main__.py +264 -0
  492. scitex/scholar/integration/zotero/exporter.py +351 -0
  493. scitex/scholar/integration/zotero/importer.py +372 -0
  494. scitex/scholar/integration/zotero/linker.py +415 -0
  495. scitex/scholar/integration/zotero/mapper.py +286 -0
  496. scitex/scholar/metadata_engines/ScholarEngine.py +588 -0
  497. scitex/scholar/metadata_engines/__init__.py +21 -0
  498. scitex/scholar/metadata_engines/individual/ArXivEngine.py +397 -0
  499. scitex/scholar/metadata_engines/individual/CrossRefEngine.py +274 -0
  500. scitex/scholar/metadata_engines/individual/CrossRefLocalEngine.py +263 -0
  501. scitex/scholar/metadata_engines/individual/OpenAlexEngine.py +350 -0
  502. scitex/scholar/metadata_engines/individual/PubMedEngine.py +329 -0
  503. scitex/scholar/metadata_engines/individual/SemanticScholarEngine.py +438 -0
  504. scitex/scholar/metadata_engines/individual/URLDOIEngine.py +410 -0
  505. scitex/scholar/metadata_engines/individual/_BaseDOIEngine.py +487 -0
  506. scitex/scholar/metadata_engines/individual/__init__.py +7 -0
  507. scitex/scholar/metadata_engines/utils/_PubMedConverter.py +469 -0
  508. scitex/scholar/metadata_engines/utils/_URLDOIExtractor.py +283 -0
  509. scitex/scholar/metadata_engines/utils/__init__.py +30 -0
  510. scitex/scholar/metadata_engines/utils/_metadata2bibtex.py +103 -0
  511. scitex/scholar/metadata_engines/utils/_standardize_metadata.py +376 -0
  512. scitex/scholar/pdf_download/ScholarPDFDownloader.py +579 -0
  513. scitex/scholar/pdf_download/__init__.py +5 -0
  514. scitex/scholar/pdf_download/strategies/__init__.py +38 -0
  515. scitex/scholar/pdf_download/strategies/chrome_pdf_viewer.py +376 -0
  516. scitex/scholar/pdf_download/strategies/direct_download.py +131 -0
  517. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +167 -0
  518. scitex/scholar/pdf_download/strategies/manual_download_utils.py +996 -0
  519. scitex/scholar/pdf_download/strategies/response_body.py +207 -0
  520. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +364 -0
  521. scitex/scholar/pipelines/ScholarPipelineParallel.py +478 -0
  522. scitex/scholar/pipelines/ScholarPipelineSingle.py +767 -0
  523. scitex/scholar/pipelines/__init__.py +49 -0
  524. scitex/scholar/storage/BibTeXHandler.py +1018 -0
  525. scitex/scholar/storage/PaperIO.py +468 -0
  526. scitex/scholar/storage/ScholarLibrary.py +182 -0
  527. scitex/scholar/storage/_DeduplicationManager.py +548 -0
  528. scitex/scholar/storage/_LibraryCacheManager.py +724 -0
  529. scitex/scholar/storage/_LibraryManager.py +1835 -0
  530. scitex/scholar/storage/__init__.py +28 -0
  531. scitex/scholar/url_finder/ScholarURLFinder.py +379 -0
  532. scitex/scholar/url_finder/__init__.py +7 -0
  533. scitex/scholar/url_finder/strategies/__init__.py +33 -0
  534. scitex/scholar/url_finder/strategies/find_pdf_urls_by_direct_links.py +261 -0
  535. scitex/scholar/url_finder/strategies/find_pdf_urls_by_dropdown.py +67 -0
  536. scitex/scholar/url_finder/strategies/find_pdf_urls_by_href.py +204 -0
  537. scitex/scholar/url_finder/strategies/find_pdf_urls_by_navigation.py +256 -0
  538. scitex/scholar/url_finder/strategies/find_pdf_urls_by_publisher_patterns.py +165 -0
  539. scitex/scholar/url_finder/strategies/find_pdf_urls_by_zotero_translators.py +163 -0
  540. scitex/scholar/url_finder/strategies/find_supplementary_urls_by_href.py +70 -0
  541. scitex/scholar/utils/__init__.py +22 -0
  542. scitex/scholar/utils/bibtex/__init__.py +9 -0
  543. scitex/scholar/utils/bibtex/_parse_bibtex.py +71 -0
  544. scitex/scholar/utils/cleanup/__init__.py +8 -0
  545. scitex/scholar/utils/cleanup/_cleanup_scholar_processes.py +96 -0
  546. scitex/scholar/utils/cleanup/cleanup_old_extractions.py +117 -0
  547. scitex/scholar/utils/text/_TextNormalizer.py +407 -0
  548. scitex/scholar/utils/text/__init__.py +9 -0
  549. scitex/scholar/zotero/__init__.py +38 -0
  550. scitex/session/__init__.py +51 -0
  551. scitex/session/_lifecycle.py +736 -0
  552. scitex/session/_manager.py +102 -0
  553. scitex/session/template.py +122 -0
  554. scitex/stats/__init__.py +30 -26
  555. scitex/stats/correct/__init__.py +21 -0
  556. scitex/stats/correct/_correct_bonferroni.py +551 -0
  557. scitex/stats/correct/_correct_fdr.py +634 -0
  558. scitex/stats/correct/_correct_holm.py +548 -0
  559. scitex/stats/correct/_correct_sidak.py +499 -0
  560. scitex/stats/descriptive/__init__.py +85 -0
  561. scitex/stats/descriptive/_circular.py +540 -0
  562. scitex/stats/descriptive/_describe.py +219 -0
  563. scitex/stats/descriptive/_nan.py +518 -0
  564. scitex/stats/descriptive/_real.py +189 -0
  565. scitex/stats/effect_sizes/__init__.py +41 -0
  566. scitex/stats/effect_sizes/_cliffs_delta.py +325 -0
  567. scitex/stats/effect_sizes/_cohens_d.py +342 -0
  568. scitex/stats/effect_sizes/_epsilon_squared.py +315 -0
  569. scitex/stats/effect_sizes/_eta_squared.py +302 -0
  570. scitex/stats/effect_sizes/_prob_superiority.py +296 -0
  571. scitex/stats/posthoc/__init__.py +19 -0
  572. scitex/stats/posthoc/_dunnett.py +463 -0
  573. scitex/stats/posthoc/_games_howell.py +383 -0
  574. scitex/stats/posthoc/_tukey_hsd.py +367 -0
  575. scitex/stats/power/__init__.py +19 -0
  576. scitex/stats/power/_power.py +433 -0
  577. scitex/stats/template.py +119 -0
  578. scitex/stats/utils/__init__.py +62 -0
  579. scitex/stats/utils/_effect_size.py +985 -0
  580. scitex/stats/utils/_formatters.py +270 -0
  581. scitex/stats/utils/_normalizers.py +927 -0
  582. scitex/stats/utils/_power.py +433 -0
  583. scitex/stats_v01/_EffectSizeCalculator.py +488 -0
  584. scitex/stats_v01/_StatisticalValidator.py +411 -0
  585. scitex/stats_v01/__init__.py +60 -0
  586. scitex/stats_v01/_additional_tests.py +415 -0
  587. scitex/{stats → stats_v01}/_p2stars.py +19 -5
  588. scitex/stats_v01/_two_sample_tests.py +141 -0
  589. scitex/stats_v01/desc/__init__.py +83 -0
  590. scitex/stats_v01/desc/_circular.py +540 -0
  591. scitex/stats_v01/desc/_describe.py +219 -0
  592. scitex/stats_v01/desc/_nan.py +518 -0
  593. scitex/{stats/desc/_nan.py → stats_v01/desc/_nan_v01-20250920_145731.py} +23 -12
  594. scitex/stats_v01/desc/_real.py +189 -0
  595. scitex/stats_v01/tests/__corr_test_optimized.py +221 -0
  596. scitex/stats_v01/tests/_corr_test_optimized.py +179 -0
  597. scitex/str/__init__.py +1 -3
  598. scitex/str/_clean_path.py +6 -2
  599. scitex/str/_latex_fallback.py +267 -160
  600. scitex/str/_parse.py +44 -36
  601. scitex/str/_printc.py +1 -3
  602. scitex/template/__init__.py +87 -0
  603. scitex/template/_create_project.py +267 -0
  604. scitex/template/create_pip_project.py +80 -0
  605. scitex/template/create_research.py +80 -0
  606. scitex/template/create_singularity.py +80 -0
  607. scitex/units.py +291 -0
  608. scitex/utils/_compress_hdf5.py +14 -3
  609. scitex/utils/_email.py +21 -2
  610. scitex/utils/_grid.py +6 -4
  611. scitex/utils/_notify.py +13 -10
  612. scitex/utils/_verify_scitex_format.py +589 -0
  613. scitex/utils/_verify_scitex_format_v01.py +370 -0
  614. scitex/utils/template.py +122 -0
  615. scitex/web/_search_pubmed.py +62 -16
  616. scitex-2.1.0.dist-info/LICENSE +21 -0
  617. scitex-2.1.0.dist-info/METADATA +677 -0
  618. scitex-2.1.0.dist-info/RECORD +919 -0
  619. {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/WHEEL +1 -1
  620. scitex-2.1.0.dist-info/entry_points.txt +3 -0
  621. scitex/ai/__Classifiers.py +0 -101
  622. scitex/ai/classification/classification_reporter.py +0 -1137
  623. scitex/ai/classification/classifiers.py +0 -101
  624. scitex/ai/classification_reporter.py +0 -1161
  625. scitex/ai/genai/__init__.py +0 -277
  626. scitex/ai/genai/anthropic_provider.py +0 -320
  627. scitex/ai/genai/anthropic_refactored.py +0 -109
  628. scitex/ai/genai/auth_manager.py +0 -200
  629. scitex/ai/genai/base_provider.py +0 -291
  630. scitex/ai/genai/chat_history.py +0 -307
  631. scitex/ai/genai/cost_tracker.py +0 -276
  632. scitex/ai/genai/deepseek_provider.py +0 -251
  633. scitex/ai/genai/google_provider.py +0 -228
  634. scitex/ai/genai/groq_provider.py +0 -248
  635. scitex/ai/genai/image_processor.py +0 -250
  636. scitex/ai/genai/llama_provider.py +0 -214
  637. scitex/ai/genai/mock_provider.py +0 -127
  638. scitex/ai/genai/model_registry.py +0 -304
  639. scitex/ai/genai/openai_provider.py +0 -293
  640. scitex/ai/genai/perplexity_provider.py +0 -205
  641. scitex/ai/genai/provider_base.py +0 -302
  642. scitex/ai/genai/provider_factory.py +0 -370
  643. scitex/ai/genai/response_handler.py +0 -235
  644. scitex/ai/layer/_Pass.py +0 -21
  645. scitex/ai/layer/__init__.py +0 -10
  646. scitex/ai/layer/_switch.py +0 -8
  647. scitex/ai/metrics/_bACC.py +0 -51
  648. scitex/ai/plt/_learning_curve.py +0 -194
  649. scitex/ai/plt/_optuna_study.py +0 -111
  650. scitex/ai/plt/aucs/__init__.py +0 -2
  651. scitex/ai/plt/aucs/example.py +0 -60
  652. scitex/ai/plt/aucs/pre_rec_auc.py +0 -223
  653. scitex/ai/plt/aucs/roc_auc.py +0 -246
  654. scitex/ai/sampling/undersample.py +0 -29
  655. scitex/db/_SQLite3.py +0 -2136
  656. scitex/db/_SQLite3Mixins/_BlobMixin.py +0 -229
  657. scitex/gen/_close.py +0 -222
  658. scitex/gen/_start.py +0 -451
  659. scitex/general/__init__.py +0 -5
  660. scitex/io/_load_modules/_db.py +0 -24
  661. scitex/life/__init__.py +0 -10
  662. scitex/life/_monitor_rain.py +0 -49
  663. scitex/reproduce/_fix_seeds.py +0 -45
  664. scitex/res/__init__.py +0 -5
  665. scitex/scholar/_local_search.py +0 -454
  666. scitex/scholar/_paper.py +0 -244
  667. scitex/scholar/_pdf_downloader.py +0 -325
  668. scitex/scholar/_search.py +0 -393
  669. scitex/scholar/_vector_search.py +0 -370
  670. scitex/scholar/_web_sources.py +0 -457
  671. scitex/stats/desc/__init__.py +0 -40
  672. scitex-2.0.0.dist-info/METADATA +0 -307
  673. scitex-2.0.0.dist-info/RECORD +0 -572
  674. scitex-2.0.0.dist-info/licenses/LICENSE +0 -7
  675. /scitex/ai/{act → activation}/__init__.py +0 -0
  676. /scitex/ai/{act → activation}/_define.py +0 -0
  677. /scitex/ai/{early_stopping.py → training/_EarlyStopping.py} +0 -0
  678. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_ImportExportMixin.py +0 -0
  679. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_IndexMixin.py +0 -0
  680. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_RowMixin.py +0 -0
  681. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/_TableMixin.py +0 -0
  682. /scitex/db/{_PostgreSQLMixins → _postgresql/_PostgreSQLMixins}/__init__.py +0 -0
  683. /scitex/{stats → stats_v01}/_calc_partial_corr.py +0 -0
  684. /scitex/{stats → stats_v01}/_corr_test_multi.py +0 -0
  685. /scitex/{stats → stats_v01}/_corr_test_wrapper.py +0 -0
  686. /scitex/{stats → stats_v01}/_describe_wrapper.py +0 -0
  687. /scitex/{stats → stats_v01}/_multiple_corrections.py +0 -0
  688. /scitex/{stats → stats_v01}/_nan_stats.py +0 -0
  689. /scitex/{stats → stats_v01}/_p2stars_wrapper.py +0 -0
  690. /scitex/{stats → stats_v01}/_statistical_tests.py +0 -0
  691. /scitex/{stats/desc/_describe.py → stats_v01/desc/_describe_v01-20250920_145731.py} +0 -0
  692. /scitex/{stats/desc/_real.py → stats_v01/desc/_real_v01-20250920_145731.py} +0 -0
  693. /scitex/{stats → stats_v01}/multiple/__init__.py +0 -0
  694. /scitex/{stats → stats_v01}/multiple/_bonferroni_correction.py +0 -0
  695. /scitex/{stats → stats_v01}/multiple/_fdr_correction.py +0 -0
  696. /scitex/{stats → stats_v01}/multiple/_multicompair.py +0 -0
  697. /scitex/{stats → stats_v01}/tests/__corr_test.py +0 -0
  698. /scitex/{stats → stats_v01}/tests/__corr_test_multi.py +0 -0
  699. /scitex/{stats → stats_v01}/tests/__corr_test_single.py +0 -0
  700. /scitex/{stats → stats_v01}/tests/__init__.py +0 -0
  701. /scitex/{stats → stats_v01}/tests/_brunner_munzel_test.py +0 -0
  702. /scitex/{stats → stats_v01}/tests/_nocorrelation_test.py +0 -0
  703. /scitex/{stats → stats_v01}/tests/_smirnov_grubbs.py +0 -0
  704. {scitex-2.0.0.dist-info → scitex-2.1.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,961 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # Timestamp: "2025-10-17 03:24:58 (ywatanabe)"
4
+ # File: /home/ywatanabe/proj/scitex-code/src/scitex/capture/mcp_server.py
5
+ # ----------------------------------------
6
+ from __future__ import annotations
7
+ import os
8
+ __FILE__ = (
9
+ "./src/scitex/capture/mcp_server.py"
10
+ )
11
+ __DIR__ = os.path.dirname(__FILE__)
12
+ # ----------------------------------------
13
+
14
+ """
15
+ MCP Server for SciTeX Capture - Screen Capture for Python
16
+ Provides screenshot capture capabilities via Model Context Protocol.
17
+ """
18
+
19
+ import asyncio
20
+ import base64
21
+ from datetime import datetime
22
+ from pathlib import Path
23
+
24
+ import mcp.types as types
25
+ from mcp.server import NotificationOptions, Server
26
+ from mcp.server.models import InitializationOptions
27
+ from mcp.server.stdio import stdio_server
28
+
29
+ from scitex import capture
30
+ import shutil
31
+
32
+
33
+ # Directory configuration
34
+ import os
35
+
36
+ # Use SCITEX_DIR environment variable if set, otherwise default to ~/.scitex
37
+ SCITEX_BASE_DIR = Path(os.getenv("SCITEX_DIR", Path.home() / ".scitex"))
38
+ SCITEX_CAPTURE_DIR = SCITEX_BASE_DIR / "capture"
39
+ LEGACY_CAPTURE_DIR = Path.home() / ".cache" / "cammy"
40
+
41
+
42
+ def get_capture_dir() -> Path:
43
+ """
44
+ Get the screenshot capture directory.
45
+ Uses $SCITEX_DIR/capture if SCITEX_DIR is set, otherwise ~/.scitex/capture.
46
+ Migrates from legacy location (~/.cache/cammy) if needed.
47
+
48
+ Returns:
49
+ Path to $SCITEX_DIR/capture or ~/.scitex/capture (migrating from ~/.cache/cammy if needed)
50
+ """
51
+ new_dir = SCITEX_CAPTURE_DIR
52
+ old_dir = LEGACY_CAPTURE_DIR
53
+
54
+ # Create new directory if it doesn't exist
55
+ new_dir.mkdir(parents=True, exist_ok=True)
56
+
57
+ # Migrate from old location if exists and new is empty
58
+ if old_dir.exists():
59
+ new_screenshots = list(new_dir.glob("*.jpg"))
60
+ if not new_screenshots or len(new_screenshots) == 0:
61
+ # Move files from old to new location
62
+ try:
63
+ for img in old_dir.glob("*.jpg"):
64
+ shutil.move(str(img), str(new_dir / img.name))
65
+ print(f"Migrated screenshots from {old_dir} to {new_dir}")
66
+ except Exception as e:
67
+ print(f"Warning: Could not migrate some files: {e}")
68
+
69
+ return new_dir
70
+
71
+
72
+ class CaptureServer:
73
+ def __init__(self):
74
+ self.server = Server("scitex-capture-server")
75
+ self.monitoring_active = False
76
+ self.monitoring_worker = None
77
+ self.setup_handlers()
78
+
79
+ def setup_handlers(self):
80
+ @self.server.list_tools()
81
+ async def handle_list_tools():
82
+ return [
83
+ types.Tool(
84
+ name="capture_screenshot",
85
+ description="Capture screenshot - monitor, window, browser, or everything including Windows screens from WSL",
86
+ inputSchema={
87
+ "type": "object",
88
+ "properties": {
89
+ "message": {
90
+ "type": "string",
91
+ "description": "Optional message to include in filename",
92
+ },
93
+ "monitor_id": {
94
+ "type": "integer",
95
+ "description": "Monitor number (0-based, default: 0 for primary monitor)",
96
+ "default": 0,
97
+ },
98
+ "all": {
99
+ "type": "boolean",
100
+ "description": "Capture all monitors (shorthand)",
101
+ "default": False,
102
+ },
103
+ "app": {
104
+ "type": "string",
105
+ "description": "App name to capture (e.g., 'chrome', 'code')",
106
+ },
107
+ "url": {
108
+ "type": "string",
109
+ "description": "URL to capture (e.g., '127.0.0.1:8000' or 'http://localhost:3000')",
110
+ },
111
+ "quality": {
112
+ "type": "integer",
113
+ "description": "JPEG quality (1-100, default: 85)",
114
+ "minimum": 1,
115
+ "maximum": 100,
116
+ "default": 85,
117
+ },
118
+ "return_base64": {
119
+ "type": "boolean",
120
+ "description": "Return screenshot as base64 string",
121
+ "default": False,
122
+ },
123
+ },
124
+ },
125
+ ),
126
+ types.Tool(
127
+ name="start_monitoring",
128
+ description="Start continuous screenshot monitoring at regular intervals",
129
+ inputSchema={
130
+ "type": "object",
131
+ "properties": {
132
+ "interval": {
133
+ "type": "number",
134
+ "description": "Seconds between captures (default: 1.0)",
135
+ "minimum": 0.1,
136
+ "default": 1.0,
137
+ },
138
+ "monitor_id": {
139
+ "type": "integer",
140
+ "description": "Monitor number (0-based, default: 0 for primary monitor)",
141
+ "default": 0,
142
+ },
143
+ "capture_all": {
144
+ "type": "boolean",
145
+ "description": "Capture all monitors combined into single image (overrides monitor_id)",
146
+ "default": False,
147
+ },
148
+ "output_dir": {
149
+ "type": "string",
150
+ "description": "Directory for screenshots (default: ~/.scitex/capture)",
151
+ },
152
+ "quality": {
153
+ "type": "integer",
154
+ "description": "JPEG quality (1-100, default: 60)",
155
+ "minimum": 1,
156
+ "maximum": 100,
157
+ "default": 60,
158
+ },
159
+ "verbose": {
160
+ "type": "boolean",
161
+ "description": "Show capture messages",
162
+ "default": True,
163
+ },
164
+ },
165
+ },
166
+ ),
167
+ types.Tool(
168
+ name="stop_monitoring",
169
+ description="Stop continuous screenshot monitoring",
170
+ inputSchema={"type": "object", "properties": {}},
171
+ ),
172
+ types.Tool(
173
+ name="get_monitoring_status",
174
+ description="Get current monitoring status and statistics",
175
+ inputSchema={"type": "object", "properties": {}},
176
+ ),
177
+ types.Tool(
178
+ name="analyze_screenshot",
179
+ description="Analyze a screenshot for error indicators (stdout/stderr categorization)",
180
+ inputSchema={
181
+ "type": "object",
182
+ "properties": {
183
+ "path": {
184
+ "type": "string",
185
+ "description": "Path to screenshot to analyze",
186
+ }
187
+ },
188
+ "required": ["path"],
189
+ },
190
+ ),
191
+ types.Tool(
192
+ name="list_recent_screenshots",
193
+ description="List recent screenshots from cache",
194
+ inputSchema={
195
+ "type": "object",
196
+ "properties": {
197
+ "limit": {
198
+ "type": "integer",
199
+ "description": "Maximum number of screenshots to list",
200
+ "default": 10,
201
+ "minimum": 1,
202
+ "maximum": 100,
203
+ },
204
+ "category": {
205
+ "type": "string",
206
+ "description": "Filter by category (stdout/stderr)",
207
+ "enum": ["stdout", "stderr", "all"],
208
+ "default": "all",
209
+ },
210
+ },
211
+ },
212
+ ),
213
+ types.Tool(
214
+ name="clear_cache",
215
+ description="Clear screenshot cache or manage cache size",
216
+ inputSchema={
217
+ "type": "object",
218
+ "properties": {
219
+ "max_size_gb": {
220
+ "type": "number",
221
+ "description": "Keep cache under this size in GB (removes oldest files)",
222
+ "minimum": 0.001,
223
+ "default": 1.0,
224
+ },
225
+ "clear_all": {
226
+ "type": "boolean",
227
+ "description": "Remove all cached screenshots",
228
+ "default": False,
229
+ },
230
+ },
231
+ },
232
+ ),
233
+ types.Tool(
234
+ name="create_gif",
235
+ description="Create an animated GIF from screenshots to summarize sessions or workflows",
236
+ inputSchema={
237
+ "type": "object",
238
+ "properties": {
239
+ "session_id": {
240
+ "type": "string",
241
+ "description": "Session ID to create GIF from (e.g., '20250823_104523'). Use 'latest' for most recent session.",
242
+ },
243
+ "image_paths": {
244
+ "type": "array",
245
+ "items": {"type": "string"},
246
+ "description": "List of image file paths to create GIF from (alternative to session_id)",
247
+ },
248
+ "pattern": {
249
+ "type": "string",
250
+ "description": "Glob pattern for images to include (alternative to session_id/image_paths)",
251
+ },
252
+ "output_path": {
253
+ "type": "string",
254
+ "description": "Output GIF file path (auto-generated if not specified)",
255
+ },
256
+ "duration": {
257
+ "type": "number",
258
+ "description": "Duration per frame in seconds (default: 0.5)",
259
+ "minimum": 0.1,
260
+ "maximum": 5.0,
261
+ "default": 0.5,
262
+ },
263
+ "optimize": {
264
+ "type": "boolean",
265
+ "description": "Optimize GIF for smaller file size (default: true)",
266
+ "default": True,
267
+ },
268
+ "max_frames": {
269
+ "type": "integer",
270
+ "description": "Maximum number of frames to include (default: no limit)",
271
+ "minimum": 1,
272
+ "maximum": 100,
273
+ },
274
+ },
275
+ },
276
+ ),
277
+ types.Tool(
278
+ name="list_sessions",
279
+ description="List available monitoring sessions that can be converted to GIFs",
280
+ inputSchema={
281
+ "type": "object",
282
+ "properties": {
283
+ "limit": {
284
+ "type": "integer",
285
+ "description": "Maximum number of sessions to list (default: 10)",
286
+ "minimum": 1,
287
+ "maximum": 50,
288
+ "default": 10,
289
+ }
290
+ },
291
+ },
292
+ ),
293
+ types.Tool(
294
+ name="get_info",
295
+ description="Enumerate all monitors, virtual desktops, and visible windows",
296
+ inputSchema={
297
+ "type": "object",
298
+ "properties": {},
299
+ },
300
+ ),
301
+ types.Tool(
302
+ name="list_windows",
303
+ description="List all visible windows with their handles and process names",
304
+ inputSchema={
305
+ "type": "object",
306
+ "properties": {},
307
+ },
308
+ ),
309
+ types.Tool(
310
+ name="capture_window",
311
+ description="Capture a specific window by its handle",
312
+ inputSchema={
313
+ "type": "object",
314
+ "properties": {
315
+ "window_handle": {
316
+ "type": "integer",
317
+ "description": "Window handle from list_windows",
318
+ },
319
+ "output_path": {
320
+ "type": "string",
321
+ "description": "Optional output path for screenshot",
322
+ },
323
+ "quality": {
324
+ "type": "integer",
325
+ "description": "JPEG quality (1-100, default: 85)",
326
+ "minimum": 1,
327
+ "maximum": 100,
328
+ "default": 85,
329
+ },
330
+ },
331
+ "required": ["window_handle"],
332
+ },
333
+ ),
334
+ ]
335
+
336
+ @self.server.call_tool()
337
+ async def handle_call_tool(name: str, arguments: dict):
338
+ if name == "capture_screenshot":
339
+ return await self.capture_screenshot(**arguments)
340
+ elif name == "start_monitoring":
341
+ return await self.start_monitoring(**arguments)
342
+ elif name == "stop_monitoring":
343
+ return await self.stop_monitoring()
344
+ elif name == "get_monitoring_status":
345
+ return await self.get_monitoring_status()
346
+ elif name == "analyze_screenshot":
347
+ return await self.analyze_screenshot(**arguments)
348
+ elif name == "list_recent_screenshots":
349
+ return await self.list_recent_screenshots(**arguments)
350
+ elif name == "clear_cache":
351
+ return await self.clear_cache(**arguments)
352
+ elif name == "create_gif":
353
+ return await self.create_gif(**arguments)
354
+ elif name == "list_sessions":
355
+ return await self.list_sessions(**arguments)
356
+ elif name == "get_info":
357
+ return await self.get_info_tool()
358
+ elif name == "list_windows":
359
+ return await self.list_windows_tool()
360
+ elif name == "capture_window":
361
+ return await self.capture_window_tool(**arguments)
362
+ else:
363
+ raise ValueError(f"Unknown tool: {name}")
364
+
365
+ # Provide screenshots as resources
366
+ @self.server.list_resources()
367
+ async def handle_list_resources():
368
+ cache_dir = get_capture_dir()
369
+ if not cache_dir.exists():
370
+ return []
371
+
372
+ resources = []
373
+ # Get last 20 screenshots, sorted by modification time
374
+ screenshots = sorted(
375
+ cache_dir.glob("*.jpg"),
376
+ key=lambda p: p.stat().st_mtime,
377
+ reverse=True,
378
+ )[:20]
379
+
380
+ for img_file in screenshots:
381
+ # Parse category from filename
382
+ category = (
383
+ "stdout"
384
+ if "-stdout.jpg" in img_file.name
385
+ else (
386
+ "stderr"
387
+ if "-stderr.jpg" in img_file.name
388
+ else "unknown"
389
+ )
390
+ )
391
+
392
+ mtime = datetime.fromtimestamp(img_file.stat().st_mtime)
393
+ resources.append(
394
+ types.Resource(
395
+ uri=f"screenshot://{img_file.name}",
396
+ name=img_file.name,
397
+ description=f"{category} screenshot from {mtime.strftime('%Y-%m-%d %H:%M:%S')}",
398
+ mimeType="image/jpeg",
399
+ )
400
+ )
401
+ return resources
402
+
403
+ @self.server.read_resource()
404
+ async def handle_read_resource(uri: str):
405
+ if uri.startswith("screenshot://"):
406
+ filename = uri.replace("screenshot://", "")
407
+ filepath = get_capture_dir() / filename
408
+
409
+ if filepath.exists():
410
+ with open(filepath, "rb") as f:
411
+ content = base64.b64encode(f.read()).decode()
412
+
413
+ return types.ResourceContent(
414
+ uri=uri, mimeType="image/jpeg", content=content
415
+ )
416
+ else:
417
+ raise ValueError(f"Screenshot not found: {filename}")
418
+
419
+ async def capture_screenshot(
420
+ self,
421
+ message=None,
422
+ monitor_id=0,
423
+ all=False,
424
+ app=None,
425
+ url=None,
426
+ quality=85,
427
+ return_base64=False,
428
+ ):
429
+ """Capture a screenshot - monitor, window, browser, or everything."""
430
+ try:
431
+ # Run in thread pool since capture is sync
432
+ loop = asyncio.get_event_loop()
433
+
434
+ # Use capture.snap which now handles all, app, url parameters
435
+ def do_capture():
436
+ return capture.snap(
437
+ message=message,
438
+ quality=quality,
439
+ monitor_id=monitor_id,
440
+ all=all,
441
+ app=app,
442
+ url=url,
443
+ verbose=True,
444
+ )
445
+
446
+ path = await loop.run_in_executor(None, do_capture)
447
+
448
+ if not path:
449
+ return {
450
+ "success": False,
451
+ "error": "Failed to capture screenshot",
452
+ }
453
+
454
+ # Determine category from filename
455
+ category = "stderr" if "-stderr.jpg" in path else "stdout"
456
+
457
+ result = {
458
+ "success": True,
459
+ "path": path,
460
+ "category": category,
461
+ "message": f"Screenshot saved to {path}",
462
+ "timestamp": datetime.now().isoformat(),
463
+ }
464
+
465
+ if return_base64 and path:
466
+ with open(path, "rb") as f:
467
+ result["base64"] = base64.b64encode(f.read()).decode()
468
+
469
+ return result
470
+
471
+ except Exception as e:
472
+ return {"success": False, "error": str(e)}
473
+
474
+ async def start_monitoring(
475
+ self,
476
+ interval=1.0,
477
+ monitor_id=0,
478
+ capture_all=False,
479
+ output_dir=None,
480
+ quality=60,
481
+ verbose=True,
482
+ ):
483
+ """Start continuous monitoring."""
484
+ if self.monitoring_active:
485
+ return {"success": False, "message": "Monitoring already active"}
486
+
487
+ try:
488
+ loop = asyncio.get_event_loop()
489
+
490
+ # Use a lambda to pass the monitor parameters correctly
491
+ def start_with_monitor():
492
+ return capture.start_monitor(
493
+ output_dir=output_dir or "~/.scitex/capture/",
494
+ interval=interval,
495
+ jpeg=True,
496
+ quality=quality,
497
+ on_capture=None,
498
+ on_error=None,
499
+ verbose=verbose,
500
+ monitor_id=monitor_id,
501
+ capture_all=capture_all,
502
+ )
503
+
504
+ self.monitoring_worker = await loop.run_in_executor(
505
+ None, start_with_monitor
506
+ )
507
+
508
+ self.monitoring_active = True
509
+
510
+ return {
511
+ "success": True,
512
+ "message": f"Started monitoring with {interval}s interval on monitor {monitor_id}",
513
+ "output_dir": output_dir or "~/.scitex/capture/",
514
+ "interval": interval,
515
+ "monitor_id": monitor_id,
516
+ "capture_all": capture_all,
517
+ "quality": quality,
518
+ }
519
+ except Exception as e:
520
+ return {"success": False, "error": str(e)}
521
+
522
+ async def stop_monitoring(self):
523
+ """Stop continuous monitoring."""
524
+ if not self.monitoring_active:
525
+ return {"success": False, "message": "Monitoring not active"}
526
+
527
+ try:
528
+ loop = asyncio.get_event_loop()
529
+ await loop.run_in_executor(None, capture.stop)
530
+
531
+ # Get stats from worker
532
+ stats = {}
533
+ if self.monitoring_worker:
534
+ stats = {
535
+ "screenshots_taken": self.monitoring_worker.screenshot_count,
536
+ "session_id": self.monitoring_worker.session_id,
537
+ "output_dir": str(self.monitoring_worker.output_dir),
538
+ }
539
+
540
+ self.monitoring_active = False
541
+ self.monitoring_worker = None
542
+
543
+ return {"success": True, "message": "Monitoring stopped", **stats}
544
+ except Exception as e:
545
+ return {"success": False, "error": str(e)}
546
+
547
+ async def get_monitoring_status(self):
548
+ """Get monitoring status."""
549
+ status = {
550
+ "active": self.monitoring_active,
551
+ "cache_dir": str(get_capture_dir()),
552
+ }
553
+
554
+ if self.monitoring_active and self.monitoring_worker:
555
+ status.update(
556
+ {
557
+ "screenshots_taken": self.monitoring_worker.screenshot_count,
558
+ "session_id": self.monitoring_worker.session_id,
559
+ "interval": self.monitoring_worker.interval_sec,
560
+ "output_dir": str(self.monitoring_worker.output_dir),
561
+ "running": self.monitoring_worker.running,
562
+ }
563
+ )
564
+
565
+ # Get cache size
566
+ cache_dir = get_capture_dir()
567
+ if cache_dir.exists():
568
+ total_size = sum(f.stat().st_size for f in cache_dir.glob("*.jpg"))
569
+ status["cache_size_mb"] = round(total_size / (1024 * 1024), 2)
570
+ status["screenshot_count"] = len(list(cache_dir.glob("*.jpg")))
571
+
572
+ return status
573
+
574
+ async def analyze_screenshot(self, path: str):
575
+ """Analyze screenshot for errors/warnings."""
576
+ try:
577
+ # Use scitex.capture's internal detection
578
+ from .utils import _detect_category
579
+
580
+ loop = asyncio.get_event_loop()
581
+ category = await loop.run_in_executor(None, _detect_category, path)
582
+
583
+ # Get file info
584
+ path_obj = Path(path)
585
+ if not path_obj.exists():
586
+ return {"success": False, "error": f"File not found: {path}"}
587
+
588
+ return {
589
+ "success": True,
590
+ "path": path,
591
+ "category": category,
592
+ "is_error": category == "stderr",
593
+ "size_kb": round(path_obj.stat().st_size / 1024, 2),
594
+ "modified": datetime.fromtimestamp(
595
+ path_obj.stat().st_mtime
596
+ ).isoformat(),
597
+ }
598
+ except Exception as e:
599
+ return {"success": False, "error": str(e)}
600
+
601
+ async def list_recent_screenshots(self, limit=10, category="all"):
602
+ """List recent screenshots from cache."""
603
+ try:
604
+ cache_dir = get_capture_dir()
605
+ if not cache_dir.exists():
606
+ return {
607
+ "success": True,
608
+ "screenshots": [],
609
+ "message": "Cache directory does not exist",
610
+ }
611
+
612
+ # Get all screenshots
613
+ screenshots = list(cache_dir.glob("*.jpg"))
614
+
615
+ # Filter by category if specified
616
+ if category == "stdout":
617
+ screenshots = [
618
+ s for s in screenshots if "-stdout.jpg" in s.name
619
+ ]
620
+ elif category == "stderr":
621
+ screenshots = [
622
+ s for s in screenshots if "-stderr.jpg" in s.name
623
+ ]
624
+
625
+ # Sort by modification time (newest first)
626
+ screenshots.sort(key=lambda p: p.stat().st_mtime, reverse=True)
627
+
628
+ # Limit results
629
+ screenshots = screenshots[:limit]
630
+
631
+ # Build result
632
+ result_list = []
633
+ for screenshot in screenshots:
634
+ cat = (
635
+ "stderr" if "-stderr.jpg" in screenshot.name else "stdout"
636
+ )
637
+ result_list.append(
638
+ {
639
+ "filename": screenshot.name,
640
+ "path": str(screenshot),
641
+ "category": cat,
642
+ "size_kb": round(screenshot.stat().st_size / 1024, 2),
643
+ "modified": datetime.fromtimestamp(
644
+ screenshot.stat().st_mtime
645
+ ).isoformat(),
646
+ }
647
+ )
648
+
649
+ return {
650
+ "success": True,
651
+ "screenshots": result_list,
652
+ "count": len(result_list),
653
+ "total_in_cache": len(list(cache_dir.glob("*.jpg"))),
654
+ }
655
+ except Exception as e:
656
+ return {"success": False, "error": str(e)}
657
+
658
+ async def clear_cache(self, max_size_gb=1.0, clear_all=False):
659
+ """Clear or manage cache size."""
660
+ try:
661
+ cache_dir = get_capture_dir()
662
+ if not cache_dir.exists():
663
+ return {
664
+ "success": True,
665
+ "message": "Cache directory does not exist",
666
+ }
667
+
668
+ if clear_all:
669
+ # Remove all screenshots
670
+ removed = 0
671
+ for screenshot in cache_dir.glob("*.jpg"):
672
+ try:
673
+ screenshot.unlink()
674
+ removed += 1
675
+ except:
676
+ pass
677
+
678
+ return {
679
+ "success": True,
680
+ "message": f"Removed {removed} screenshots",
681
+ "removed_count": removed,
682
+ }
683
+ else:
684
+ # Use scitex.capture's cache management
685
+ from .utils import _manage_cache_size
686
+
687
+ loop = asyncio.get_event_loop()
688
+ await loop.run_in_executor(
689
+ None, _manage_cache_size, cache_dir, max_size_gb
690
+ )
691
+
692
+ # Get new cache size
693
+ total_size = sum(
694
+ f.stat().st_size for f in cache_dir.glob("*.jpg")
695
+ )
696
+
697
+ return {
698
+ "success": True,
699
+ "message": f"Cache managed to stay under {max_size_gb}GB",
700
+ "cache_size_mb": round(total_size / (1024 * 1024), 2),
701
+ "max_size_gb": max_size_gb,
702
+ }
703
+ except Exception as e:
704
+ return {"success": False, "error": str(e)}
705
+
706
+ async def create_gif(
707
+ self,
708
+ session_id=None,
709
+ image_paths=None,
710
+ pattern=None,
711
+ output_path=None,
712
+ duration=0.5,
713
+ optimize=True,
714
+ max_frames=None,
715
+ ):
716
+ """Create GIF from screenshots."""
717
+ try:
718
+ from .gif import GifCreator
719
+
720
+ creator = GifCreator()
721
+ loop = asyncio.get_event_loop()
722
+
723
+ # Determine which creation method to use
724
+ if session_id:
725
+ if session_id == "latest":
726
+ # Use most recent session
727
+ result_path = await loop.run_in_executor(
728
+ None,
729
+ creator.create_gif_from_recent_session,
730
+ "~/.scitex/capture",
731
+ duration,
732
+ optimize,
733
+ max_frames,
734
+ )
735
+ else:
736
+ # Use specific session
737
+ result_path = await loop.run_in_executor(
738
+ None,
739
+ creator.create_gif_from_session,
740
+ session_id,
741
+ output_path,
742
+ "~/.scitex/capture",
743
+ duration,
744
+ optimize,
745
+ max_frames,
746
+ )
747
+ elif image_paths:
748
+ # Use specific image paths
749
+ if not output_path:
750
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
751
+ output_path = f"~/.scitex/capture/custom_gif_{timestamp}.gif"
752
+
753
+ result_path = await loop.run_in_executor(
754
+ None,
755
+ creator.create_gif_from_files,
756
+ image_paths,
757
+ output_path,
758
+ duration,
759
+ optimize,
760
+ )
761
+ elif pattern:
762
+ # Use glob pattern
763
+ result_path = await loop.run_in_executor(
764
+ None,
765
+ creator.create_gif_from_pattern,
766
+ pattern,
767
+ output_path,
768
+ duration,
769
+ optimize,
770
+ max_frames,
771
+ )
772
+ else:
773
+ return {
774
+ "success": False,
775
+ "error": "Must specify either session_id, image_paths, or pattern",
776
+ }
777
+
778
+ if result_path:
779
+ # Get file info
780
+ path_obj = Path(result_path)
781
+ file_size = path_obj.stat().st_size / 1024 # KB
782
+
783
+ return {
784
+ "success": True,
785
+ "path": result_path,
786
+ "size_kb": round(file_size, 2),
787
+ "message": f"GIF created successfully: {result_path}",
788
+ "duration_per_frame": duration,
789
+ "optimized": optimize,
790
+ }
791
+ else:
792
+ return {
793
+ "success": False,
794
+ "error": "Failed to create GIF - no suitable images found",
795
+ }
796
+
797
+ except ImportError:
798
+ return {
799
+ "success": False,
800
+ "error": "PIL (Pillow) is required for GIF creation. Install with: pip install Pillow",
801
+ }
802
+ except Exception as e:
803
+ return {"success": False, "error": str(e)}
804
+
805
+ async def list_sessions(self, limit=10):
806
+ """List available monitoring sessions."""
807
+ try:
808
+ from .gif import GifCreator
809
+
810
+ creator = GifCreator()
811
+ loop = asyncio.get_event_loop()
812
+
813
+ sessions = await loop.run_in_executor(
814
+ None, creator.get_recent_sessions, "~/.scitex/capture"
815
+ )
816
+
817
+ # Limit results
818
+ sessions = sessions[:limit]
819
+
820
+ # Get details for each session
821
+ session_details = []
822
+ cache_dir = get_capture_dir()
823
+
824
+ for session_id in sessions:
825
+ # Count screenshots in session
826
+ jpg_files = list(cache_dir.glob(f"{session_id}_*.jpg"))
827
+ png_files = list(cache_dir.glob(f"{session_id}_*.png"))
828
+
829
+ if not jpg_files and not png_files:
830
+ continue
831
+
832
+ files = jpg_files + png_files
833
+ files.sort()
834
+
835
+ if files:
836
+ # Get session info
837
+ first_file = files[0]
838
+ last_file = files[-1]
839
+ total_size = sum(f.stat().st_size for f in files)
840
+
841
+ session_details.append(
842
+ {
843
+ "session_id": session_id,
844
+ "screenshot_count": len(files),
845
+ "first_screenshot": first_file.name,
846
+ "last_screenshot": last_file.name,
847
+ "total_size_kb": round(total_size / 1024, 2),
848
+ "start_time": datetime.fromtimestamp(
849
+ first_file.stat().st_mtime
850
+ ).isoformat(),
851
+ "end_time": datetime.fromtimestamp(
852
+ last_file.stat().st_mtime
853
+ ).isoformat(),
854
+ }
855
+ )
856
+
857
+ return {
858
+ "success": True,
859
+ "sessions": session_details,
860
+ "count": len(session_details),
861
+ "message": f"Found {len(session_details)} monitoring sessions",
862
+ }
863
+
864
+ except Exception as e:
865
+ return {"success": False, "error": str(e)}
866
+
867
+ async def get_info_tool(self):
868
+ """Enumerate all monitors and virtual desktops."""
869
+ try:
870
+ loop = asyncio.get_event_loop()
871
+ info = await loop.run_in_executor(None, capture.get_info)
872
+
873
+ return {
874
+ "success": True,
875
+ "monitors": info.get("Monitors", {}),
876
+ "virtual_desktops": info.get("VirtualDesktops", {}),
877
+ "windows": info.get("Windows", {}),
878
+ "timestamp": info.get("Timestamp", ""),
879
+ }
880
+ except Exception as e:
881
+ return {"success": False, "error": str(e)}
882
+
883
+ async def list_windows_tool(self):
884
+ """List all visible windows."""
885
+ try:
886
+ loop = asyncio.get_event_loop()
887
+ info = await loop.run_in_executor(None, capture.get_info)
888
+
889
+ windows = info.get("Windows", {})
890
+ window_list = windows.get("Details", [])
891
+
892
+ # Format for easy use
893
+ formatted_windows = []
894
+ for win in window_list:
895
+ formatted_windows.append(
896
+ {
897
+ "handle": win.get("Handle"),
898
+ "title": win.get("Title"),
899
+ "process_name": win.get("ProcessName"),
900
+ "process_id": win.get("ProcessId"),
901
+ }
902
+ )
903
+
904
+ return {
905
+ "success": True,
906
+ "windows": formatted_windows,
907
+ "count": len(formatted_windows),
908
+ "visible_count": windows.get("VisibleCount", 0),
909
+ "message": f"Found {len(formatted_windows)} windows on current virtual desktop",
910
+ }
911
+ except Exception as e:
912
+ return {"success": False, "error": str(e)}
913
+
914
+ async def capture_window_tool(
915
+ self, window_handle: int, output_path: str = None, quality: int = 85
916
+ ):
917
+ """Capture a specific window by handle."""
918
+ try:
919
+ loop = asyncio.get_event_loop()
920
+ path = await loop.run_in_executor(
921
+ None, capture.capture_window, window_handle, output_path
922
+ )
923
+
924
+ if path:
925
+ return {
926
+ "success": True,
927
+ "path": path,
928
+ "window_handle": window_handle,
929
+ "message": f"Window captured to {path}",
930
+ }
931
+ else:
932
+ return {
933
+ "success": False,
934
+ "error": f"Failed to capture window {window_handle}",
935
+ }
936
+ except Exception as e:
937
+ return {"success": False, "error": str(e)}
938
+
939
+
940
+ async def main():
941
+ """Main entry point for the MCP server."""
942
+ server = CaptureServer()
943
+ async with stdio_server() as (read_stream, write_stream):
944
+ await server.server.run(
945
+ read_stream,
946
+ write_stream,
947
+ InitializationOptions(
948
+ server_name="scitex-capture",
949
+ server_version="0.2.1",
950
+ capabilities=server.server.get_capabilities(
951
+ notification_options=NotificationOptions(),
952
+ experimental_capabilities={},
953
+ ),
954
+ ),
955
+ )
956
+
957
+
958
+ if __name__ == "__main__":
959
+ asyncio.run(main())
960
+
961
+ # EOF