chatspatial 1.1.0__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 (67) hide show
  1. chatspatial/__init__.py +11 -0
  2. chatspatial/__main__.py +141 -0
  3. chatspatial/cli/__init__.py +7 -0
  4. chatspatial/config.py +53 -0
  5. chatspatial/models/__init__.py +85 -0
  6. chatspatial/models/analysis.py +513 -0
  7. chatspatial/models/data.py +2462 -0
  8. chatspatial/server.py +1763 -0
  9. chatspatial/spatial_mcp_adapter.py +720 -0
  10. chatspatial/tools/__init__.py +3 -0
  11. chatspatial/tools/annotation.py +1903 -0
  12. chatspatial/tools/cell_communication.py +1603 -0
  13. chatspatial/tools/cnv_analysis.py +605 -0
  14. chatspatial/tools/condition_comparison.py +595 -0
  15. chatspatial/tools/deconvolution/__init__.py +402 -0
  16. chatspatial/tools/deconvolution/base.py +318 -0
  17. chatspatial/tools/deconvolution/card.py +244 -0
  18. chatspatial/tools/deconvolution/cell2location.py +326 -0
  19. chatspatial/tools/deconvolution/destvi.py +144 -0
  20. chatspatial/tools/deconvolution/flashdeconv.py +101 -0
  21. chatspatial/tools/deconvolution/rctd.py +317 -0
  22. chatspatial/tools/deconvolution/spotlight.py +216 -0
  23. chatspatial/tools/deconvolution/stereoscope.py +109 -0
  24. chatspatial/tools/deconvolution/tangram.py +135 -0
  25. chatspatial/tools/differential.py +625 -0
  26. chatspatial/tools/embeddings.py +298 -0
  27. chatspatial/tools/enrichment.py +1863 -0
  28. chatspatial/tools/integration.py +807 -0
  29. chatspatial/tools/preprocessing.py +723 -0
  30. chatspatial/tools/spatial_domains.py +808 -0
  31. chatspatial/tools/spatial_genes.py +836 -0
  32. chatspatial/tools/spatial_registration.py +441 -0
  33. chatspatial/tools/spatial_statistics.py +1476 -0
  34. chatspatial/tools/trajectory.py +495 -0
  35. chatspatial/tools/velocity.py +405 -0
  36. chatspatial/tools/visualization/__init__.py +155 -0
  37. chatspatial/tools/visualization/basic.py +393 -0
  38. chatspatial/tools/visualization/cell_comm.py +699 -0
  39. chatspatial/tools/visualization/cnv.py +320 -0
  40. chatspatial/tools/visualization/core.py +684 -0
  41. chatspatial/tools/visualization/deconvolution.py +852 -0
  42. chatspatial/tools/visualization/enrichment.py +660 -0
  43. chatspatial/tools/visualization/integration.py +205 -0
  44. chatspatial/tools/visualization/main.py +164 -0
  45. chatspatial/tools/visualization/multi_gene.py +739 -0
  46. chatspatial/tools/visualization/persistence.py +335 -0
  47. chatspatial/tools/visualization/spatial_stats.py +469 -0
  48. chatspatial/tools/visualization/trajectory.py +639 -0
  49. chatspatial/tools/visualization/velocity.py +411 -0
  50. chatspatial/utils/__init__.py +115 -0
  51. chatspatial/utils/adata_utils.py +1372 -0
  52. chatspatial/utils/compute.py +327 -0
  53. chatspatial/utils/data_loader.py +499 -0
  54. chatspatial/utils/dependency_manager.py +462 -0
  55. chatspatial/utils/device_utils.py +165 -0
  56. chatspatial/utils/exceptions.py +185 -0
  57. chatspatial/utils/image_utils.py +267 -0
  58. chatspatial/utils/mcp_utils.py +137 -0
  59. chatspatial/utils/path_utils.py +243 -0
  60. chatspatial/utils/persistence.py +78 -0
  61. chatspatial/utils/scipy_compat.py +143 -0
  62. chatspatial-1.1.0.dist-info/METADATA +242 -0
  63. chatspatial-1.1.0.dist-info/RECORD +67 -0
  64. chatspatial-1.1.0.dist-info/WHEEL +5 -0
  65. chatspatial-1.1.0.dist-info/entry_points.txt +2 -0
  66. chatspatial-1.1.0.dist-info/licenses/LICENSE +21 -0
  67. chatspatial-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,298 @@
1
+ """
2
+ Embedding computation tools for spatial transcriptomics data.
3
+
4
+ This module provides explicit control over dimensionality reduction and clustering
5
+ computations. While analysis tools compute these lazily using ensure_* functions,
6
+ users can use this tool to control computation parameters directly.
7
+ """
8
+
9
+ from typing import Literal, Optional
10
+
11
+ from pydantic import BaseModel, Field
12
+
13
+ from ..spatial_mcp_adapter import ToolContext
14
+ from ..utils.compute import (
15
+ ensure_diffmap,
16
+ ensure_leiden,
17
+ ensure_louvain,
18
+ ensure_neighbors,
19
+ ensure_pca,
20
+ ensure_spatial_neighbors,
21
+ ensure_umap,
22
+ )
23
+ from ..utils.mcp_utils import mcp_tool_error_handler
24
+
25
+
26
+ class EmbeddingParameters(BaseModel):
27
+ """Parameters for embedding computation."""
28
+
29
+ # What to compute
30
+ compute_pca: bool = Field(
31
+ default=True,
32
+ description="Compute PCA dimensionality reduction",
33
+ )
34
+ compute_neighbors: bool = Field(
35
+ default=True,
36
+ description="Compute k-NN neighbor graph (requires PCA)",
37
+ )
38
+ compute_umap: bool = Field(
39
+ default=True,
40
+ description="Compute UMAP embedding (requires neighbors)",
41
+ )
42
+ compute_clustering: bool = Field(
43
+ default=True,
44
+ description="Compute Leiden clustering (requires neighbors)",
45
+ )
46
+ compute_diffmap: bool = Field(
47
+ default=False,
48
+ description="Compute diffusion map for trajectory analysis (requires neighbors)",
49
+ )
50
+ compute_spatial_neighbors: bool = Field(
51
+ default=True,
52
+ description="Compute spatial neighborhood graph for spatial analysis",
53
+ )
54
+
55
+ # PCA parameters
56
+ n_pcs: int = Field(
57
+ default=30,
58
+ ge=2,
59
+ le=100,
60
+ description="Number of principal components",
61
+ )
62
+ use_highly_variable: bool = Field(
63
+ default=True,
64
+ description="Use only highly variable genes for PCA",
65
+ )
66
+
67
+ # Neighbor graph parameters
68
+ n_neighbors: int = Field(
69
+ default=15,
70
+ ge=2,
71
+ le=100,
72
+ description="Number of neighbors for k-NN graph",
73
+ )
74
+
75
+ # UMAP parameters
76
+ umap_min_dist: float = Field(
77
+ default=0.5,
78
+ ge=0.0,
79
+ le=1.0,
80
+ description="UMAP minimum distance parameter",
81
+ )
82
+
83
+ # Clustering parameters
84
+ clustering_method: Literal["leiden", "louvain"] = Field(
85
+ default="leiden",
86
+ description="Clustering algorithm",
87
+ )
88
+ clustering_resolution: float = Field(
89
+ default=1.0,
90
+ ge=0.1,
91
+ le=2.0,
92
+ description="Clustering resolution (higher = more clusters)",
93
+ )
94
+ clustering_key: str = Field(
95
+ default="leiden",
96
+ description="Key to store clustering results in adata.obs",
97
+ )
98
+
99
+ # Diffusion map parameters
100
+ diffmap_n_comps: int = Field(
101
+ default=15,
102
+ ge=2,
103
+ le=50,
104
+ description="Number of diffusion components",
105
+ )
106
+
107
+ # Spatial neighbor parameters
108
+ spatial_coord_type: Literal["grid", "generic"] = Field(
109
+ default="generic",
110
+ description="Coordinate type: 'grid' for Visium hexagonal, 'generic' for others",
111
+ )
112
+ spatial_n_neighs: int = Field(
113
+ default=6,
114
+ ge=1,
115
+ le=30,
116
+ description="Number of spatial neighbors (for generic coord_type)",
117
+ )
118
+ spatial_n_rings: int = Field(
119
+ default=1,
120
+ ge=1,
121
+ le=3,
122
+ description="Number of rings (for grid coord_type)",
123
+ )
124
+
125
+ # Force recomputation
126
+ force: bool = Field(
127
+ default=False,
128
+ description="Force recomputation even if results already exist",
129
+ )
130
+
131
+ # Random seed
132
+ random_state: int = Field(
133
+ default=0,
134
+ description="Random seed for reproducibility",
135
+ )
136
+
137
+
138
+ class EmbeddingResult(BaseModel):
139
+ """Result of embedding computation."""
140
+
141
+ data_id: str
142
+ computed: list[str]
143
+ skipped: list[str]
144
+ n_clusters: Optional[int] = None
145
+ pca_variance_ratio: Optional[float] = None
146
+
147
+
148
+ @mcp_tool_error_handler()
149
+ async def compute_embeddings(
150
+ data_id: str,
151
+ ctx: ToolContext,
152
+ params: EmbeddingParameters = EmbeddingParameters(),
153
+ ) -> EmbeddingResult:
154
+ """Compute dimensionality reduction, clustering, and neighbor graphs.
155
+
156
+ This tool provides explicit control over embedding computations.
157
+ Analysis tools compute these lazily, but you can use this tool to:
158
+ - Control computation parameters (n_pcs, n_neighbors, resolution)
159
+ - Force recomputation with different parameters
160
+ - Compute specific embeddings without running full preprocessing
161
+
162
+ Args:
163
+ data_id: Dataset ID
164
+ ctx: Tool context
165
+ params: Embedding computation parameters
166
+
167
+ Returns:
168
+ Summary of computed embeddings
169
+ """
170
+ adata = await ctx.get_adata(data_id)
171
+ computed = []
172
+ skipped = []
173
+
174
+ # Handle force recomputation by removing existing results
175
+ if params.force:
176
+ if params.compute_pca and "X_pca" in adata.obsm:
177
+ del adata.obsm["X_pca"]
178
+ if "pca" in adata.uns:
179
+ del adata.uns["pca"]
180
+ if params.compute_neighbors:
181
+ if "neighbors" in adata.uns:
182
+ del adata.uns["neighbors"]
183
+ if "connectivities" in adata.obsp:
184
+ del adata.obsp["connectivities"]
185
+ if "distances" in adata.obsp:
186
+ del adata.obsp["distances"]
187
+ if params.compute_umap and "X_umap" in adata.obsm:
188
+ del adata.obsm["X_umap"]
189
+ if params.compute_clustering and params.clustering_key in adata.obs:
190
+ del adata.obs[params.clustering_key]
191
+ if params.compute_diffmap and "X_diffmap" in adata.obsm:
192
+ del adata.obsm["X_diffmap"]
193
+ if params.compute_spatial_neighbors and "spatial_connectivities" in adata.obsp:
194
+ del adata.obsp["spatial_connectivities"]
195
+ if "spatial_distances" in adata.obsp:
196
+ del adata.obsp["spatial_distances"]
197
+
198
+ # 1. PCA
199
+ if params.compute_pca:
200
+ if ensure_pca(
201
+ adata,
202
+ n_comps=params.n_pcs,
203
+ use_highly_variable=params.use_highly_variable,
204
+ random_state=params.random_state,
205
+ ):
206
+ computed.append("PCA")
207
+ else:
208
+ skipped.append("PCA (already exists)")
209
+
210
+ # 2. Neighbors (requires PCA)
211
+ if params.compute_neighbors:
212
+ if ensure_neighbors(
213
+ adata,
214
+ n_neighbors=params.n_neighbors,
215
+ n_pcs=params.n_pcs,
216
+ random_state=params.random_state,
217
+ ):
218
+ computed.append("neighbors")
219
+ else:
220
+ skipped.append("neighbors (already exists)")
221
+
222
+ # 3. UMAP (requires neighbors)
223
+ if params.compute_umap:
224
+ if ensure_umap(
225
+ adata,
226
+ min_dist=params.umap_min_dist,
227
+ random_state=params.random_state,
228
+ ):
229
+ computed.append("UMAP")
230
+ else:
231
+ skipped.append("UMAP (already exists)")
232
+
233
+ # 4. Clustering (requires neighbors)
234
+ n_clusters = None
235
+ if params.compute_clustering:
236
+ if params.clustering_method == "leiden":
237
+ if ensure_leiden(
238
+ adata,
239
+ resolution=params.clustering_resolution,
240
+ key_added=params.clustering_key,
241
+ random_state=params.random_state,
242
+ ):
243
+ n_clusters = adata.obs[params.clustering_key].nunique()
244
+ computed.append(f"Leiden clustering ({n_clusters} clusters)")
245
+ else:
246
+ skipped.append(f"{params.clustering_key} (already exists)")
247
+ n_clusters = adata.obs[params.clustering_key].nunique()
248
+ else:
249
+ if ensure_louvain(
250
+ adata,
251
+ resolution=params.clustering_resolution,
252
+ key_added=params.clustering_key,
253
+ random_state=params.random_state,
254
+ ):
255
+ n_clusters = adata.obs[params.clustering_key].nunique()
256
+ computed.append(f"Louvain clustering ({n_clusters} clusters)")
257
+ else:
258
+ skipped.append(f"{params.clustering_key} (already exists)")
259
+ n_clusters = adata.obs[params.clustering_key].nunique()
260
+
261
+ # 5. Diffusion map (requires neighbors)
262
+ if params.compute_diffmap:
263
+ if ensure_diffmap(adata, n_comps=params.diffmap_n_comps):
264
+ computed.append("diffusion map")
265
+ else:
266
+ skipped.append("diffusion map (already exists)")
267
+
268
+ # 6. Spatial neighbors
269
+ if params.compute_spatial_neighbors:
270
+ try:
271
+ if ensure_spatial_neighbors(
272
+ adata,
273
+ coord_type=params.spatial_coord_type,
274
+ n_neighs=params.spatial_n_neighs,
275
+ n_rings=params.spatial_n_rings,
276
+ ):
277
+ computed.append("spatial neighbors")
278
+ else:
279
+ skipped.append("spatial neighbors (already exists)")
280
+ except ValueError as e:
281
+ await ctx.warning(f"Could not compute spatial neighbors: {e}")
282
+ skipped.append(f"spatial neighbors (error: {e})")
283
+
284
+ # Get PCA variance ratio if available
285
+ pca_variance_ratio = None
286
+ if "pca" in adata.uns and "variance_ratio" in adata.uns["pca"]:
287
+ pca_variance_ratio = float(adata.uns["pca"]["variance_ratio"].sum())
288
+
289
+ # Store updated data
290
+ await ctx.set_adata(data_id, adata)
291
+
292
+ return EmbeddingResult(
293
+ data_id=data_id,
294
+ computed=computed,
295
+ skipped=skipped,
296
+ n_clusters=n_clusters,
297
+ pca_variance_ratio=pca_variance_ratio,
298
+ )