scitex 2.14.0__py3-none-any.whl → 2.15.2__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 (300) hide show
  1. scitex/__init__.py +71 -17
  2. scitex/_env_loader.py +156 -0
  3. scitex/_mcp_resources/__init__.py +37 -0
  4. scitex/_mcp_resources/_cheatsheet.py +135 -0
  5. scitex/_mcp_resources/_figrecipe.py +138 -0
  6. scitex/_mcp_resources/_formats.py +102 -0
  7. scitex/_mcp_resources/_modules.py +337 -0
  8. scitex/_mcp_resources/_session.py +149 -0
  9. scitex/_mcp_tools/__init__.py +4 -0
  10. scitex/_mcp_tools/audio.py +66 -0
  11. scitex/_mcp_tools/diagram.py +11 -95
  12. scitex/_mcp_tools/introspect.py +210 -0
  13. scitex/_mcp_tools/plt.py +260 -305
  14. scitex/_mcp_tools/scholar.py +74 -0
  15. scitex/_mcp_tools/social.py +244 -0
  16. scitex/_mcp_tools/template.py +24 -0
  17. scitex/_mcp_tools/writer.py +21 -204
  18. scitex/ai/_gen_ai/_PARAMS.py +10 -7
  19. scitex/ai/classification/reporters/_SingleClassificationReporter.py +45 -1603
  20. scitex/ai/classification/reporters/_mixins/__init__.py +36 -0
  21. scitex/ai/classification/reporters/_mixins/_constants.py +67 -0
  22. scitex/ai/classification/reporters/_mixins/_cv_summary.py +387 -0
  23. scitex/ai/classification/reporters/_mixins/_feature_importance.py +119 -0
  24. scitex/ai/classification/reporters/_mixins/_metrics.py +275 -0
  25. scitex/ai/classification/reporters/_mixins/_plotting.py +179 -0
  26. scitex/ai/classification/reporters/_mixins/_reports.py +153 -0
  27. scitex/ai/classification/reporters/_mixins/_storage.py +160 -0
  28. scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +30 -1550
  29. scitex/ai/classification/timeseries/_sliding_window_core.py +467 -0
  30. scitex/ai/classification/timeseries/_sliding_window_plotting.py +369 -0
  31. scitex/audio/README.md +40 -36
  32. scitex/audio/__init__.py +129 -61
  33. scitex/audio/_branding.py +185 -0
  34. scitex/audio/_mcp/__init__.py +32 -0
  35. scitex/audio/_mcp/handlers.py +59 -6
  36. scitex/audio/_mcp/speak_handlers.py +238 -0
  37. scitex/audio/_relay.py +225 -0
  38. scitex/audio/_tts.py +18 -10
  39. scitex/audio/engines/base.py +17 -10
  40. scitex/audio/engines/elevenlabs_engine.py +7 -2
  41. scitex/audio/mcp_server.py +228 -75
  42. scitex/canvas/README.md +1 -1
  43. scitex/canvas/editor/_dearpygui/__init__.py +25 -0
  44. scitex/canvas/editor/_dearpygui/_editor.py +147 -0
  45. scitex/canvas/editor/_dearpygui/_handlers.py +476 -0
  46. scitex/canvas/editor/_dearpygui/_panels/__init__.py +17 -0
  47. scitex/canvas/editor/_dearpygui/_panels/_control.py +119 -0
  48. scitex/canvas/editor/_dearpygui/_panels/_element_controls.py +190 -0
  49. scitex/canvas/editor/_dearpygui/_panels/_preview.py +43 -0
  50. scitex/canvas/editor/_dearpygui/_panels/_sections.py +390 -0
  51. scitex/canvas/editor/_dearpygui/_plotting.py +187 -0
  52. scitex/canvas/editor/_dearpygui/_rendering.py +504 -0
  53. scitex/canvas/editor/_dearpygui/_selection.py +295 -0
  54. scitex/canvas/editor/_dearpygui/_state.py +93 -0
  55. scitex/canvas/editor/_dearpygui/_utils.py +61 -0
  56. scitex/canvas/editor/flask_editor/_core/__init__.py +27 -0
  57. scitex/canvas/editor/flask_editor/_core/_bbox_extraction.py +200 -0
  58. scitex/canvas/editor/flask_editor/_core/_editor.py +173 -0
  59. scitex/canvas/editor/flask_editor/_core/_export_helpers.py +353 -0
  60. scitex/canvas/editor/flask_editor/_core/_routes_basic.py +190 -0
  61. scitex/canvas/editor/flask_editor/_core/_routes_export.py +332 -0
  62. scitex/canvas/editor/flask_editor/_core/_routes_panels.py +252 -0
  63. scitex/canvas/editor/flask_editor/_core/_routes_save.py +218 -0
  64. scitex/canvas/editor/flask_editor/_core.py +25 -1684
  65. scitex/canvas/editor/flask_editor/templates/__init__.py +32 -70
  66. scitex/cli/__init__.py +38 -43
  67. scitex/cli/audio.py +76 -27
  68. scitex/cli/capture.py +13 -20
  69. scitex/cli/introspect.py +481 -0
  70. scitex/cli/main.py +200 -109
  71. scitex/cli/mcp.py +60 -34
  72. scitex/cli/plt.py +357 -0
  73. scitex/cli/repro.py +15 -8
  74. scitex/cli/resource.py +15 -8
  75. scitex/cli/scholar/__init__.py +23 -8
  76. scitex/cli/scholar/_crossref_scitex.py +296 -0
  77. scitex/cli/scholar/_fetch.py +25 -3
  78. scitex/cli/social.py +314 -0
  79. scitex/cli/stats.py +15 -8
  80. scitex/cli/template.py +129 -12
  81. scitex/cli/tex.py +15 -8
  82. scitex/cli/writer.py +132 -8
  83. scitex/cloud/__init__.py +41 -2
  84. scitex/config/README.md +1 -1
  85. scitex/config/__init__.py +16 -2
  86. scitex/config/_env_registry.py +256 -0
  87. scitex/context/__init__.py +22 -0
  88. scitex/dev/__init__.py +20 -1
  89. scitex/diagram/__init__.py +42 -19
  90. scitex/diagram/mcp_server.py +13 -125
  91. scitex/gen/__init__.py +50 -14
  92. scitex/gen/_list_packages.py +4 -4
  93. scitex/introspect/__init__.py +82 -0
  94. scitex/introspect/_call_graph.py +303 -0
  95. scitex/introspect/_class_hierarchy.py +163 -0
  96. scitex/introspect/_core.py +41 -0
  97. scitex/introspect/_docstring.py +131 -0
  98. scitex/introspect/_examples.py +113 -0
  99. scitex/introspect/_imports.py +271 -0
  100. scitex/{gen/_inspect_module.py → introspect/_list_api.py} +43 -54
  101. scitex/introspect/_mcp/__init__.py +41 -0
  102. scitex/introspect/_mcp/handlers.py +233 -0
  103. scitex/introspect/_members.py +155 -0
  104. scitex/introspect/_resolve.py +89 -0
  105. scitex/introspect/_signature.py +131 -0
  106. scitex/introspect/_source.py +80 -0
  107. scitex/introspect/_type_hints.py +172 -0
  108. scitex/io/_save.py +1 -2
  109. scitex/io/bundle/README.md +1 -1
  110. scitex/logging/_formatters.py +19 -9
  111. scitex/mcp_server.py +98 -5
  112. scitex/os/__init__.py +4 -0
  113. scitex/{gen → os}/_check_host.py +4 -5
  114. scitex/plt/__init__.py +245 -550
  115. scitex/plt/_subplots/_AxisWrapperMixins/_SeabornMixin/_wrappers.py +5 -10
  116. scitex/plt/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  117. scitex/plt/gallery/README.md +1 -1
  118. scitex/plt/utils/_hitmap/__init__.py +82 -0
  119. scitex/plt/utils/_hitmap/_artist_extraction.py +343 -0
  120. scitex/plt/utils/_hitmap/_color_application.py +346 -0
  121. scitex/plt/utils/_hitmap/_color_conversion.py +121 -0
  122. scitex/plt/utils/_hitmap/_constants.py +40 -0
  123. scitex/plt/utils/_hitmap/_hitmap_core.py +334 -0
  124. scitex/plt/utils/_hitmap/_path_extraction.py +357 -0
  125. scitex/plt/utils/_hitmap/_query.py +113 -0
  126. scitex/plt/utils/_hitmap.py +46 -1616
  127. scitex/plt/utils/_metadata/__init__.py +80 -0
  128. scitex/plt/utils/_metadata/_artists/__init__.py +25 -0
  129. scitex/plt/utils/_metadata/_artists/_base.py +195 -0
  130. scitex/plt/utils/_metadata/_artists/_collections.py +356 -0
  131. scitex/plt/utils/_metadata/_artists/_extract.py +57 -0
  132. scitex/plt/utils/_metadata/_artists/_images.py +80 -0
  133. scitex/plt/utils/_metadata/_artists/_lines.py +261 -0
  134. scitex/plt/utils/_metadata/_artists/_patches.py +247 -0
  135. scitex/plt/utils/_metadata/_artists/_text.py +106 -0
  136. scitex/plt/utils/_metadata/_csv.py +416 -0
  137. scitex/plt/utils/_metadata/_detect.py +225 -0
  138. scitex/plt/utils/_metadata/_legend.py +127 -0
  139. scitex/plt/utils/_metadata/_rounding.py +117 -0
  140. scitex/plt/utils/_metadata/_verification.py +202 -0
  141. scitex/schema/README.md +1 -1
  142. scitex/scholar/__init__.py +8 -0
  143. scitex/scholar/_mcp/crossref_handlers.py +265 -0
  144. scitex/scholar/core/Scholar.py +63 -1700
  145. scitex/scholar/core/_mixins/__init__.py +36 -0
  146. scitex/scholar/core/_mixins/_enrichers.py +270 -0
  147. scitex/scholar/core/_mixins/_library_handlers.py +100 -0
  148. scitex/scholar/core/_mixins/_loaders.py +103 -0
  149. scitex/scholar/core/_mixins/_pdf_download.py +375 -0
  150. scitex/scholar/core/_mixins/_pipeline.py +312 -0
  151. scitex/scholar/core/_mixins/_project_handlers.py +125 -0
  152. scitex/scholar/core/_mixins/_savers.py +69 -0
  153. scitex/scholar/core/_mixins/_search.py +103 -0
  154. scitex/scholar/core/_mixins/_services.py +88 -0
  155. scitex/scholar/core/_mixins/_url_finding.py +105 -0
  156. scitex/scholar/crossref_scitex.py +367 -0
  157. scitex/scholar/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  158. scitex/scholar/examples/00_run_all.sh +120 -0
  159. scitex/scholar/jobs/_executors.py +27 -3
  160. scitex/scholar/pdf_download/ScholarPDFDownloader.py +38 -416
  161. scitex/scholar/pdf_download/_cli.py +154 -0
  162. scitex/scholar/pdf_download/strategies/__init__.py +11 -8
  163. scitex/scholar/pdf_download/strategies/manual_download_fallback.py +80 -3
  164. scitex/scholar/pipelines/ScholarPipelineBibTeX.py +73 -121
  165. scitex/scholar/pipelines/ScholarPipelineParallel.py +80 -138
  166. scitex/scholar/pipelines/ScholarPipelineSingle.py +43 -63
  167. scitex/scholar/pipelines/_single_steps.py +71 -36
  168. scitex/scholar/storage/_LibraryManager.py +97 -1695
  169. scitex/scholar/storage/_mixins/__init__.py +30 -0
  170. scitex/scholar/storage/_mixins/_bibtex_handlers.py +128 -0
  171. scitex/scholar/storage/_mixins/_library_operations.py +218 -0
  172. scitex/scholar/storage/_mixins/_metadata_conversion.py +226 -0
  173. scitex/scholar/storage/_mixins/_paper_saving.py +456 -0
  174. scitex/scholar/storage/_mixins/_resolution.py +376 -0
  175. scitex/scholar/storage/_mixins/_storage_helpers.py +121 -0
  176. scitex/scholar/storage/_mixins/_symlink_handlers.py +226 -0
  177. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +462 -0
  178. scitex/scholar/url_finder/.tmp/open_url/README.md +223 -0
  179. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +694 -0
  180. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +1160 -0
  181. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +344 -0
  182. scitex/scholar/url_finder/.tmp/open_url/__init__.py +24 -0
  183. scitex/security/README.md +3 -3
  184. scitex/session/README.md +1 -1
  185. scitex/session/__init__.py +26 -7
  186. scitex/session/_decorator.py +1 -1
  187. scitex/sh/README.md +1 -1
  188. scitex/sh/__init__.py +7 -4
  189. scitex/social/__init__.py +155 -0
  190. scitex/social/docs/EXTERNAL_PACKAGE_BRANDING.md +149 -0
  191. scitex/stats/_mcp/_handlers/__init__.py +31 -0
  192. scitex/stats/_mcp/_handlers/_corrections.py +113 -0
  193. scitex/stats/_mcp/_handlers/_descriptive.py +78 -0
  194. scitex/stats/_mcp/_handlers/_effect_size.py +106 -0
  195. scitex/stats/_mcp/_handlers/_format.py +94 -0
  196. scitex/stats/_mcp/_handlers/_normality.py +110 -0
  197. scitex/stats/_mcp/_handlers/_posthoc.py +224 -0
  198. scitex/stats/_mcp/_handlers/_power.py +247 -0
  199. scitex/stats/_mcp/_handlers/_recommend.py +102 -0
  200. scitex/stats/_mcp/_handlers/_run_test.py +279 -0
  201. scitex/stats/_mcp/_handlers/_stars.py +48 -0
  202. scitex/stats/_mcp/handlers.py +19 -1171
  203. scitex/stats/auto/_stat_style.py +175 -0
  204. scitex/stats/auto/_style_definitions.py +411 -0
  205. scitex/stats/auto/_styles.py +22 -620
  206. scitex/stats/descriptive/__init__.py +11 -8
  207. scitex/stats/descriptive/_ci.py +39 -0
  208. scitex/stats/power/_power.py +15 -4
  209. scitex/str/__init__.py +2 -1
  210. scitex/str/_title_case.py +63 -0
  211. scitex/template/README.md +1 -1
  212. scitex/template/__init__.py +25 -10
  213. scitex/template/_code_templates.py +147 -0
  214. scitex/template/_mcp/handlers.py +81 -0
  215. scitex/template/_mcp/tool_schemas.py +55 -0
  216. scitex/template/_templates/__init__.py +51 -0
  217. scitex/template/_templates/audio.py +233 -0
  218. scitex/template/_templates/canvas.py +312 -0
  219. scitex/template/_templates/capture.py +268 -0
  220. scitex/template/_templates/config.py +43 -0
  221. scitex/template/_templates/diagram.py +294 -0
  222. scitex/template/_templates/io.py +107 -0
  223. scitex/template/_templates/module.py +53 -0
  224. scitex/template/_templates/plt.py +202 -0
  225. scitex/template/_templates/scholar.py +267 -0
  226. scitex/template/_templates/session.py +130 -0
  227. scitex/template/_templates/session_minimal.py +43 -0
  228. scitex/template/_templates/session_plot.py +67 -0
  229. scitex/template/_templates/session_stats.py +77 -0
  230. scitex/template/_templates/stats.py +323 -0
  231. scitex/template/_templates/writer.py +296 -0
  232. scitex/template/clone_writer_directory.py +5 -5
  233. scitex/ui/_backends/_email.py +10 -2
  234. scitex/ui/_backends/_webhook.py +5 -1
  235. scitex/web/_search_pubmed.py +10 -6
  236. scitex/writer/README.md +1 -1
  237. scitex/writer/_mcp/handlers.py +11 -744
  238. scitex/writer/_mcp/tool_schemas.py +5 -335
  239. scitex-2.15.2.dist-info/METADATA +648 -0
  240. {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/RECORD +246 -150
  241. scitex/canvas/editor/flask_editor/templates/_scripts.py +0 -4933
  242. scitex/canvas/editor/flask_editor/templates/_styles.py +0 -1658
  243. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +0 -90
  244. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +0 -1571
  245. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +0 -6262
  246. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +0 -1274
  247. scitex/dev/plt/data/mpl/dir_ax.txt +0 -459
  248. scitex/diagram/_compile.py +0 -312
  249. scitex/diagram/_diagram.py +0 -355
  250. scitex/diagram/_mcp/__init__.py +0 -4
  251. scitex/diagram/_mcp/handlers.py +0 -400
  252. scitex/diagram/_mcp/tool_schemas.py +0 -157
  253. scitex/diagram/_presets.py +0 -173
  254. scitex/diagram/_schema.py +0 -182
  255. scitex/diagram/_split.py +0 -278
  256. scitex/gen/_ci.py +0 -12
  257. scitex/gen/_title_case.py +0 -89
  258. scitex/plt/_mcp/__init__.py +0 -4
  259. scitex/plt/_mcp/_handlers_annotation.py +0 -102
  260. scitex/plt/_mcp/_handlers_figure.py +0 -195
  261. scitex/plt/_mcp/_handlers_plot.py +0 -252
  262. scitex/plt/_mcp/_handlers_style.py +0 -219
  263. scitex/plt/_mcp/handlers.py +0 -74
  264. scitex/plt/_mcp/tool_schemas.py +0 -497
  265. scitex/plt/mcp_server.py +0 -231
  266. scitex/scholar/data/.gitkeep +0 -0
  267. scitex/scholar/data/README.md +0 -44
  268. scitex/scholar/data/bib_files/bibliography.bib +0 -1952
  269. scitex/scholar/data/bib_files/neurovista.bib +0 -277
  270. scitex/scholar/data/bib_files/neurovista_enriched.bib +0 -441
  271. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +0 -441
  272. scitex/scholar/data/bib_files/neurovista_processed.bib +0 -338
  273. scitex/scholar/data/bib_files/openaccess.bib +0 -89
  274. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +0 -2178
  275. scitex/scholar/data/bib_files/pac.bib +0 -698
  276. scitex/scholar/data/bib_files/pac_enriched.bib +0 -1061
  277. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  278. scitex/scholar/data/bib_files/pac_titles.txt +0 -75
  279. scitex/scholar/data/bib_files/paywalled.bib +0 -98
  280. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +0 -58
  281. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +0 -87
  282. scitex/scholar/data/bib_files/seizure_prediction.bib +0 -694
  283. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  284. scitex/scholar/data/bib_files/test_complete_enriched.bib +0 -437
  285. scitex/scholar/data/bib_files/test_final_enriched.bib +0 -437
  286. scitex/scholar/data/bib_files/test_seizure.bib +0 -46
  287. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  288. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  289. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  290. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  291. scitex/scholar/data/impact_factor.db +0 -0
  292. scitex/scholar/examples/SUGGESTIONS.md +0 -865
  293. scitex/scholar/examples/dev.py +0 -38
  294. scitex-2.14.0.dist-info/METADATA +0 -1238
  295. /scitex/{gen → context}/_detect_environment.py +0 -0
  296. /scitex/{gen → context}/_get_notebook_path.py +0 -0
  297. /scitex/{gen/_shell.py → sh/_shell_legacy.py} +0 -0
  298. {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/WHEEL +0 -0
  299. {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/entry_points.txt +0 -0
  300. {scitex-2.14.0.dist-info → scitex-2.15.2.dist-info}/licenses/LICENSE +0 -0
@@ -10,13 +10,13 @@ Enables remote agents to connect and play audio on local speakers.
10
10
 
11
11
  Usage:
12
12
  scitex audio serve # stdio (default)
13
- scitex audio serve -t http --port 8084 # HTTP transport
14
- scitex audio serve -t sse --port 8084 # SSE transport
13
+ scitex audio serve -t http --port 31293 # HTTP transport
14
+ scitex audio serve -t sse --port 31293 # SSE transport
15
15
 
16
16
  For remote audio:
17
- 1. Run locally: scitex audio serve -t http --port 8084
18
- 2. SSH tunnel: ssh -R 8084:localhost:8084 remote-host
19
- 3. Remote agent connects to http://localhost:8084
17
+ 1. Run locally: scitex audio serve -t http --port 31293
18
+ 2. SSH tunnel: ssh -R 31293:localhost:31293 remote-host
19
+ 3. Remote agent connects to http://localhost:31293
20
20
  """
21
21
 
22
22
  from __future__ import annotations
@@ -35,16 +35,19 @@ except ImportError:
35
35
  FASTMCP_AVAILABLE = False
36
36
  FastMCP = None # type: ignore
37
37
 
38
- __all__ = ["mcp", "run_server", "main", "FASTMCP_AVAILABLE"]
38
+ __all__ = ["mcp", "run_server", "run_relay_server", "main", "FASTMCP_AVAILABLE"]
39
+
40
+ # Import branding
41
+ from ._branding import (
42
+ get_mcp_instructions,
43
+ get_mcp_server_name,
44
+ )
39
45
 
40
46
  # Initialize MCP server
41
47
  if FASTMCP_AVAILABLE:
42
48
  mcp = FastMCP(
43
- name="scitex-audio",
44
- instructions=(
45
- "Text-to-Speech server with multiple backends (pyttsx3, gtts, elevenlabs). "
46
- "Use speak() to convert text to speech. Supports cross-process FIFO queuing."
47
- ),
49
+ name=get_mcp_server_name(),
50
+ instructions=get_mcp_instructions(),
48
51
  )
49
52
  else:
50
53
  mcp = None
@@ -61,6 +64,16 @@ def _get_audio_dir() -> Path:
61
64
 
62
65
 
63
66
  if FASTMCP_AVAILABLE:
67
+ import asyncio
68
+
69
+ def _run_async(coro):
70
+ """Run async handler synchronously for FastMCP tools."""
71
+ try:
72
+ loop = asyncio.get_event_loop()
73
+ except RuntimeError:
74
+ loop = asyncio.new_event_loop()
75
+ asyncio.set_event_loop(loop)
76
+ return loop.run_until_complete(coro)
64
77
 
65
78
  @mcp.tool()
66
79
  def speak(
@@ -74,72 +87,92 @@ if FASTMCP_AVAILABLE:
74
87
  fallback: bool = True,
75
88
  agent_id: Optional[str] = None,
76
89
  ) -> str:
77
- """Convert text to speech with fallback (pyttsx3 -> gtts -> elevenlabs).
90
+ """[audio] Convert text to speech with fallback (pyttsx3 -> gtts -> elevenlabs).
78
91
 
79
- Args:
80
- text: Text to convert to speech
81
- backend: TTS backend (auto-selects with fallback if not specified)
82
- voice: Voice/language (gtts: 'en','fr'; elevenlabs: 'rachel','adam')
83
- rate: Speech rate in words per minute (pyttsx3 only, default 150)
84
- speed: Speed multiplier for gtts (1.0=normal, 1.5=faster, 0.7=slower)
85
- play: Play audio after generation (default True)
86
- save: Save audio to file (default False)
87
- fallback: Try next backend on failure (default True)
88
- agent_id: Optional identifier for the agent making the request
92
+ NOTE: Plays on SERVER. For remote use, see speak_relay.
93
+ """
94
+ return audio_speak_local(
95
+ text=text, backend=backend, voice=voice, rate=rate,
96
+ speed=speed, play=play, save=save, fallback=fallback,
97
+ agent_id=agent_id,
98
+ )
89
99
 
90
- Returns
91
- -------
92
- JSON string with success status and details
100
+ @mcp.tool()
101
+ def audio_speak_local(
102
+ text: str,
103
+ backend: Optional[str] = None,
104
+ voice: Optional[str] = None,
105
+ rate: int = 150,
106
+ speed: float = 1.5,
107
+ play: bool = True,
108
+ save: bool = False,
109
+ fallback: bool = True,
110
+ agent_id: Optional[str] = None,
111
+ ) -> str:
112
+ """[audio] Convert text to speech on the LOCAL/SERVER machine.
93
113
 
94
- Examples
95
- --------
96
- speak("Hello world")
97
- speak("Bonjour", backend="gtts", voice="fr")
98
- speak("Fast speech", rate=200)
99
- """
100
- from . import speak as tts_speak
101
- from ._cross_process_lock import AudioPlaybackLock
114
+ Use when running Claude Code directly on your local machine.
115
+ Audio plays where MCP server runs.
102
116
 
103
- output_path = None
104
- if save:
105
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
106
- output_path = str(_get_audio_dir() / f"tts_{timestamp}.mp3")
117
+ Args:
118
+ text: Text to convert to speech
119
+ backend: TTS backend (pyttsx3, gtts, elevenlabs)
120
+ voice: Voice/language
121
+ rate: Speech rate (pyttsx3 only)
122
+ speed: Speed multiplier (gtts)
123
+ play: Play audio (default True)
124
+ save: Save to file (default False)
125
+ fallback: Try next backend on failure
126
+ agent_id: Agent identifier
127
+ """
128
+ from ._mcp.speak_handlers import speak_local_handler
107
129
 
108
- try:
109
- # Acquire cross-process lock to ensure FIFO across all instances
110
- lock = AudioPlaybackLock()
111
- lock.acquire(timeout=120.0)
112
- try:
113
- tts_speak(
114
- text=text,
115
- backend=backend,
116
- voice=voice,
117
- play=play,
118
- output_path=output_path,
119
- fallback=fallback,
120
- rate=rate,
121
- speed=speed,
122
- )
123
- finally:
124
- lock.release()
130
+ result = _run_async(speak_local_handler(
131
+ text=text, backend=backend, voice=voice, rate=rate,
132
+ speed=speed, play=play, save=save, fallback=fallback,
133
+ agent_id=agent_id,
134
+ ))
135
+ return json.dumps(result, indent=2)
125
136
 
126
- response = {
127
- "success": True,
128
- "text": text,
129
- "backend": backend,
130
- "voice": voice,
131
- "played": play,
132
- "agent_id": agent_id,
133
- "timestamp": datetime.now().isoformat(),
134
- }
137
+ @mcp.tool()
138
+ def audio_speak_relay(
139
+ text: str,
140
+ backend: Optional[str] = None,
141
+ voice: Optional[str] = None,
142
+ rate: int = 150,
143
+ speed: float = 1.5,
144
+ play: bool = True,
145
+ save: bool = False,
146
+ fallback: bool = True,
147
+ agent_id: Optional[str] = None,
148
+ ) -> str:
149
+ """[audio] Convert text to speech via RELAY server (remote playback).
135
150
 
136
- if output_path:
137
- response["saved_to"] = output_path
151
+ Use when running on remote server (e.g., NAS) and want audio
152
+ on your local machine. REQUIRES relay server running locally.
138
153
 
139
- return json.dumps(response, indent=2)
154
+ Args:
155
+ text: Text to convert to speech
156
+ backend: TTS backend (pyttsx3, gtts, elevenlabs)
157
+ voice: Voice/language
158
+ rate: Speech rate (pyttsx3 only)
159
+ speed: Speed multiplier (gtts)
160
+ play: Play audio (default True)
161
+ save: Save to file (default False)
162
+ fallback: Try next backend on failure
163
+ agent_id: Agent identifier
164
+
165
+ Returns:
166
+ Success with playback info, or error with setup instructions
167
+ """
168
+ from ._mcp.speak_handlers import speak_relay_handler
140
169
 
141
- except Exception as e:
142
- return json.dumps({"success": False, "error": str(e)}, indent=2)
170
+ result = _run_async(speak_relay_handler(
171
+ text=text, backend=backend, voice=voice, rate=rate,
172
+ speed=speed, play=play, save=save, fallback=fallback,
173
+ agent_id=agent_id,
174
+ ))
175
+ return json.dumps(result, indent=2)
143
176
 
144
177
  @mcp.tool()
145
178
  def list_backends() -> str:
@@ -244,21 +277,28 @@ if FASTMCP_AVAILABLE:
244
277
 
245
278
  def run_server(
246
279
  transport: str = "stdio",
247
- host: str = "0.0.0.0",
248
- port: int = 8084,
280
+ host: Optional[str] = None,
281
+ port: Optional[int] = None,
249
282
  ) -> None:
250
283
  """Run the MCP server.
251
284
 
252
285
  Args:
253
286
  transport: Transport protocol ("stdio", "sse", or "http")
254
- host: Host for HTTP/SSE transport
255
- port: Port for HTTP/SSE transport
287
+ host: Host for HTTP/SSE transport (default from branding)
288
+ port: Port for HTTP/SSE transport (default from branding)
256
289
  """
290
+ from ._branding import DEFAULT_HOST, DEFAULT_PORT
291
+
292
+ host = host or DEFAULT_HOST
293
+ port = port or DEFAULT_PORT
294
+
257
295
  if not FASTMCP_AVAILABLE:
258
296
  import sys
259
297
 
298
+ from ._branding import BRAND_NAME
299
+
260
300
  print("=" * 60)
261
- print("MCP Server 'scitex-audio' requires the 'fastmcp' package.")
301
+ print(f"MCP Server '{BRAND_NAME}' requires the 'fastmcp' package.")
262
302
  print()
263
303
  print("Install with:")
264
304
  print(" pip install fastmcp")
@@ -268,19 +308,132 @@ def run_server(
268
308
  print("=" * 60)
269
309
  sys.exit(1)
270
310
 
311
+ from ._branding import BRAND_NAME
312
+
271
313
  if transport == "stdio":
272
314
  mcp.run(transport="stdio")
273
315
  elif transport == "sse":
274
- print(f"Starting scitex-audio MCP server (SSE) on {host}:{port}")
316
+ print(f"Starting {BRAND_NAME} MCP server (SSE) on {host}:{port}")
275
317
  mcp.run(transport="sse", host=host, port=port)
276
318
  elif transport == "http":
277
- print(f"Starting scitex-audio MCP server (HTTP) on {host}:{port}")
319
+ print(f"Starting {BRAND_NAME} MCP server (HTTP) on {host}:{port}")
278
320
  print(f"Connect via: http://{host}:{port}/mcp")
279
321
  mcp.run(transport="streamable-http", host=host, port=port)
280
322
  else:
281
323
  raise ValueError(f"Unknown transport: {transport}")
282
324
 
283
325
 
326
+ def run_relay_server(host: Optional[str] = None, port: Optional[int] = None) -> None:
327
+ """Run HTTP relay server for remote audio playback.
328
+
329
+ This exposes simple REST endpoints that remote agents can connect to.
330
+ Unlike the MCP server, this uses standard HTTP POST/GET.
331
+
332
+ Endpoints:
333
+ POST /speak - Speak text
334
+ GET /health - Health check
335
+ GET /list_backends - List available backends
336
+ """
337
+ from ._branding import BRAND_NAME, DEFAULT_HOST, DEFAULT_PORT
338
+
339
+ host = host or DEFAULT_HOST
340
+ port = port or DEFAULT_PORT
341
+
342
+ try:
343
+ from http.server import BaseHTTPRequestHandler, HTTPServer
344
+ except ImportError as e:
345
+ raise RuntimeError(f"HTTP server not available: {e}") from e
346
+
347
+ class RelayHandler(BaseHTTPRequestHandler):
348
+ """HTTP handler for audio relay requests."""
349
+
350
+ def _send_json(self, data: dict, status: int = 200) -> None:
351
+ """Send JSON response."""
352
+ import json
353
+
354
+ body = json.dumps(data, indent=2).encode("utf-8")
355
+ self.send_response(status)
356
+ self.send_header("Content-Type", "application/json")
357
+ self.send_header("Content-Length", str(len(body)))
358
+ self.send_header("Access-Control-Allow-Origin", "*")
359
+ self.end_headers()
360
+ self.wfile.write(body)
361
+
362
+ def do_OPTIONS(self) -> None:
363
+ """Handle CORS preflight."""
364
+ self.send_response(200)
365
+ self.send_header("Access-Control-Allow-Origin", "*")
366
+ self.send_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
367
+ self.send_header("Access-Control-Allow-Headers", "Content-Type")
368
+ self.end_headers()
369
+
370
+ def do_GET(self) -> None:
371
+ """Handle GET requests."""
372
+ import json
373
+
374
+ if self.path == "/health":
375
+ self._send_json({"status": "healthy", "server": BRAND_NAME})
376
+ elif self.path == "/list_backends":
377
+ result = list_backends()
378
+ self._send_json(json.loads(result))
379
+ else:
380
+ self._send_json({"error": "Not found"}, 404)
381
+
382
+ def do_POST(self) -> None:
383
+ """Handle POST requests."""
384
+ import json
385
+
386
+ if self.path == "/speak":
387
+ content_length = int(self.headers.get("Content-Length", 0))
388
+ body = self.rfile.read(content_length)
389
+ try:
390
+ data = json.loads(body.decode("utf-8"))
391
+ # Import speak directly from audio module (not MCP tool)
392
+ from . import speak as tts_speak
393
+ from ._cross_process_lock import AudioPlaybackLock
394
+
395
+ # Acquire lock for FIFO
396
+ lock = AudioPlaybackLock()
397
+ lock.acquire(timeout=120.0)
398
+ try:
399
+ tts_speak(
400
+ text=data.get("text", ""),
401
+ backend=data.get("backend"),
402
+ voice=data.get("voice"),
403
+ rate=data.get("rate", 150),
404
+ speed=data.get("speed", 1.5),
405
+ play=data.get("play", True),
406
+ fallback=data.get("fallback", True),
407
+ mode="local",
408
+ )
409
+ finally:
410
+ lock.release()
411
+
412
+ self._send_json({
413
+ "success": True,
414
+ "text": data.get("text", ""),
415
+ "played": True,
416
+ "timestamp": datetime.now().isoformat(),
417
+ })
418
+ except Exception as e:
419
+ self._send_json({"success": False, "error": str(e)}, 500)
420
+ else:
421
+ self._send_json({"error": "Not found"}, 404)
422
+
423
+ def log_message(self, format: str, *args) -> None:
424
+ """Suppress default logging."""
425
+ pass
426
+
427
+ print(f"Starting {BRAND_NAME} relay server on {host}:{port}")
428
+ print("Endpoints: POST /speak, GET /health, GET /list_backends")
429
+ server = HTTPServer((host, port), RelayHandler)
430
+ try:
431
+ server.serve_forever()
432
+ except KeyboardInterrupt:
433
+ print("\nShutting down relay server")
434
+ server.shutdown()
435
+
436
+
284
437
  def main():
285
438
  """Entry point for scitex-audio-mcp command."""
286
439
  run_server(transport="stdio")
scitex/canvas/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <!-- ---
2
2
  !-- Timestamp: 2025-12-08
3
3
  !-- Author: ywatanabe
4
- !-- File: /home/ywatanabe/proj/scitex-code/src/scitex/vis/README.md
4
+ !-- File: /home/ywatanabe/proj/scitex-python/src/scitex/vis/README.md
5
5
  !-- --- -->
6
6
 
7
7
  # scitex.vis - Canvas-Based Figure Composition
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-01-24 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/canvas/editor/_dearpygui/__init__.py
4
+
5
+ """
6
+ DearPyGui editor package.
7
+
8
+ Modular GPU-accelerated figure editor using DearPyGui.
9
+
10
+ Modules:
11
+ - _state: EditorState dataclass for all editor state
12
+ - _utils: Utility functions (checkerboard, constants)
13
+ - _rendering: Figure rendering and highlight drawing
14
+ - _plotting: CSV data plotting
15
+ - _selection: Element selection and hover detection
16
+ - _handlers: Event handlers and callbacks
17
+ - _panels: UI panel creation
18
+ """
19
+
20
+ from ._editor import DearPyGuiEditor
21
+
22
+ __all__ = ["DearPyGuiEditor"]
23
+
24
+
25
+ # EOF
@@ -0,0 +1,147 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-01-24 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/canvas/editor/_dearpygui/_editor.py
4
+
5
+ """
6
+ DearPyGui-based figure editor with GPU-accelerated rendering.
7
+
8
+ Thin orchestrator class that delegates to modular components.
9
+ """
10
+
11
+ from pathlib import Path
12
+ from typing import Any, Dict, Optional
13
+
14
+ from ._state import EditorState
15
+
16
+
17
+ class DearPyGuiEditor:
18
+ """
19
+ GPU-accelerated figure editor using DearPyGui.
20
+
21
+ Features:
22
+ - Modern immediate-mode GUI with GPU acceleration
23
+ - Real-time figure preview
24
+ - Property editors with sliders, color pickers, and input fields
25
+ - Click-to-select traces on preview
26
+ - Save to .manual.json
27
+ - SciTeX style defaults pre-filled
28
+ - Dark/light theme support
29
+ """
30
+
31
+ def __init__(
32
+ self,
33
+ json_path: Path,
34
+ metadata: Dict[str, Any],
35
+ csv_data: Optional[Any] = None,
36
+ png_path: Optional[Path] = None,
37
+ manual_overrides: Optional[Dict[str, Any]] = None,
38
+ ):
39
+ """Initialize the DearPyGui editor.
40
+
41
+ Parameters
42
+ ----------
43
+ json_path : Path
44
+ Path to the JSON metadata file
45
+ metadata : dict
46
+ Figure metadata dictionary
47
+ csv_data : pd.DataFrame, optional
48
+ CSV data for plotting
49
+ png_path : Path, optional
50
+ Path to the PNG file
51
+ manual_overrides : dict, optional
52
+ Manual override settings
53
+ """
54
+ self.state = EditorState.create(
55
+ json_path=json_path,
56
+ metadata=metadata,
57
+ csv_data=csv_data,
58
+ png_path=png_path,
59
+ manual_overrides=manual_overrides,
60
+ )
61
+
62
+ # Backward compatibility properties
63
+ self._texture_id = None
64
+
65
+ @property
66
+ def json_path(self) -> Path:
67
+ """Get JSON path."""
68
+ return self.state.json_path
69
+
70
+ @property
71
+ def metadata(self) -> Dict[str, Any]:
72
+ """Get metadata."""
73
+ return self.state.metadata
74
+
75
+ @property
76
+ def csv_data(self) -> Optional[Any]:
77
+ """Get CSV data."""
78
+ return self.state.csv_data
79
+
80
+ @property
81
+ def current_overrides(self) -> Dict[str, Any]:
82
+ """Get current overrides."""
83
+ return self.state.current_overrides
84
+
85
+ @current_overrides.setter
86
+ def current_overrides(self, value: Dict[str, Any]) -> None:
87
+ """Set current overrides."""
88
+ self.state.current_overrides = value
89
+
90
+ def run(self):
91
+ """Launch the DearPyGui editor."""
92
+ try:
93
+ import dearpygui.dearpygui as dpg
94
+ except ImportError:
95
+ raise ImportError(
96
+ "DearPyGui is required for this editor. "
97
+ "Install with: pip install dearpygui"
98
+ )
99
+
100
+ from ._panels import create_control_panel, create_preview_panel
101
+ from ._rendering import update_preview
102
+
103
+ dpg.create_context()
104
+
105
+ # Configure viewport
106
+ dpg.create_viewport(
107
+ title=f"SciTeX Editor ({self.state.backend_name}) - {self.state.json_path.name}",
108
+ width=1400,
109
+ height=900,
110
+ )
111
+
112
+ # Create texture registry for image preview
113
+ with dpg.texture_registry(show=False):
114
+ # Create initial texture with placeholder
115
+ width, height = 800, 600
116
+ texture_data = [0.2, 0.2, 0.2, 1.0] * (width * height)
117
+ self._texture_id = dpg.add_dynamic_texture(
118
+ width=width,
119
+ height=height,
120
+ default_value=texture_data,
121
+ tag="preview_texture",
122
+ )
123
+ self.state.texture_id = self._texture_id
124
+
125
+ # Create main window
126
+ with dpg.window(label="SciTeX Figure Editor", tag="main_window"):
127
+ with dpg.group(horizontal=True):
128
+ # Left panel: Preview
129
+ create_preview_panel(self.state, dpg)
130
+
131
+ # Right panel: Controls
132
+ create_control_panel(self.state, dpg)
133
+
134
+ # Set main window as primary
135
+ dpg.set_primary_window("main_window", True)
136
+
137
+ # Initial render
138
+ update_preview(self.state, dpg)
139
+
140
+ # Setup and show
141
+ dpg.setup_dearpygui()
142
+ dpg.show_viewport()
143
+ dpg.start_dearpygui()
144
+ dpg.destroy_context()
145
+
146
+
147
+ # EOF