scmcp-shared 0.2.0__py3-none-any.whl → 0.3.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.
- scmcp_shared/__init__.py +1 -1
- scmcp_shared/cli.py +96 -0
- scmcp_shared/schema/__init__.py +22 -1
- scmcp_shared/schema/io.py +4 -8
- scmcp_shared/schema/pl.py +2 -3
- scmcp_shared/schema/pp.py +15 -15
- scmcp_shared/schema/tl.py +18 -24
- scmcp_shared/schema/util.py +11 -11
- scmcp_shared/server/__init__.py +11 -49
- scmcp_shared/server/base.py +153 -0
- scmcp_shared/server/io.py +71 -61
- scmcp_shared/server/pl.py +288 -294
- scmcp_shared/server/pp.py +320 -358
- scmcp_shared/server/tl.py +393 -402
- scmcp_shared/server/util.py +241 -240
- scmcp_shared/util.py +110 -43
- {scmcp_shared-0.2.0.dist-info → scmcp_shared-0.3.0.dist-info}/METADATA +1 -1
- scmcp_shared-0.3.0.dist-info/RECORD +21 -0
- scmcp_shared/schema/base.py +0 -11
- scmcp_shared-0.2.0.dist-info/RECORD +0 -20
- {scmcp_shared-0.2.0.dist-info → scmcp_shared-0.3.0.dist-info}/WHEEL +0 -0
- {scmcp_shared-0.2.0.dist-info → scmcp_shared-0.3.0.dist-info}/licenses/LICENSE +0 -0
scmcp_shared/__init__.py
CHANGED
scmcp_shared/cli.py
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
import typer
|
2
|
+
from typing import Optional, Union, Type
|
3
|
+
from enum import Enum
|
4
|
+
from .util import add_figure_route, set_env,setup_mcp
|
5
|
+
|
6
|
+
|
7
|
+
class TransportEnum(str, Enum):
|
8
|
+
STDIO = "stdio"
|
9
|
+
SSE = "sse"
|
10
|
+
SHTTP = "shttp"
|
11
|
+
|
12
|
+
@property
|
13
|
+
def transport_value(self) -> str:
|
14
|
+
"""Get the actual transport value to use."""
|
15
|
+
if self == TransportEnum.SHTTP:
|
16
|
+
return "streamable-http"
|
17
|
+
return self.value
|
18
|
+
|
19
|
+
|
20
|
+
class ModuleEnum(str, Enum):
|
21
|
+
"""Base class for module types."""
|
22
|
+
ALL = "all"
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
class MCPCLI:
|
27
|
+
"""Base class for CLI applications with support for dynamic modules and parameters."""
|
28
|
+
|
29
|
+
def __init__(self, name: str, help_text: str, manager=None, modules=ModuleEnum):
|
30
|
+
self.name = name
|
31
|
+
self.modules = modules
|
32
|
+
self.manager = manager
|
33
|
+
self.app = typer.Typer(
|
34
|
+
name=name,
|
35
|
+
help=help_text,
|
36
|
+
add_completion=False,
|
37
|
+
no_args_is_help=True,
|
38
|
+
)
|
39
|
+
self._setup_commands()
|
40
|
+
|
41
|
+
def _setup_commands(self):
|
42
|
+
"""Setup the main commands for the CLI."""
|
43
|
+
self.app.command(name="run", help="Start the server with the specified configuration")(self.run_command())
|
44
|
+
self.app.callback()(self._callback)
|
45
|
+
|
46
|
+
def run_command(self):
|
47
|
+
def _run_command(
|
48
|
+
log_file: Optional[str] = typer.Option(None, "--log-file", help="log file path, use stdout if None"),
|
49
|
+
transport: TransportEnum = typer.Option(TransportEnum.STDIO, "-t", "--transport", help="specify transport type",
|
50
|
+
case_sensitive=False),
|
51
|
+
port: int = typer.Option(8000, "-p", "--port", help="transport port"),
|
52
|
+
host: str = typer.Option("127.0.0.1", "--host", help="transport host"),
|
53
|
+
forward: str = typer.Option(None, "-f", "--forward", help="forward request to another server"),
|
54
|
+
module: list[self.modules] = typer.Option(
|
55
|
+
[self.modules.ALL],
|
56
|
+
"-m",
|
57
|
+
"--module",
|
58
|
+
help="specify module to run"
|
59
|
+
),
|
60
|
+
):
|
61
|
+
"""Start the server with the specified configuration."""
|
62
|
+
if "all" in module:
|
63
|
+
modules = None
|
64
|
+
elif isinstance(module, list) and bool(module):
|
65
|
+
modules = [m.value for m in module]
|
66
|
+
self.mcp = self.manager(self.name, include_modules=modules).mcp
|
67
|
+
|
68
|
+
self.run_mcp(log_file, forward, transport, host, port)
|
69
|
+
return _run_command
|
70
|
+
|
71
|
+
|
72
|
+
def _callback(self):
|
73
|
+
"""Liana MCP CLI root command."""
|
74
|
+
pass
|
75
|
+
|
76
|
+
def run_mcp(self, log_file, forward, transport, host, port):
|
77
|
+
set_env(log_file, forward, transport, host, port)
|
78
|
+
from .logging_config import setup_logger
|
79
|
+
setup_logger(log_file)
|
80
|
+
if transport == "stdio":
|
81
|
+
self.mcp.run()
|
82
|
+
elif transport in ["sse", "shttp"]:
|
83
|
+
add_figure_route(self.mcp)
|
84
|
+
transport = transport.transport_value
|
85
|
+
self.mcp.run(
|
86
|
+
transport=transport,
|
87
|
+
host=host,
|
88
|
+
port=port,
|
89
|
+
log_level="info"
|
90
|
+
)
|
91
|
+
|
92
|
+
def run_cli(self, mcp=None, module_dic=None):
|
93
|
+
"""Run the CLI application."""
|
94
|
+
self.mcp = mcp
|
95
|
+
self.module_dic = module_dic
|
96
|
+
self.app()
|
scmcp_shared/schema/__init__.py
CHANGED
@@ -1 +1,22 @@
|
|
1
|
-
from
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from pydantic import Field, BaseModel,ConfigDict
|
4
|
+
|
5
|
+
|
6
|
+
class AdataModel(BaseModel):
|
7
|
+
"""Input schema for the adata tool."""
|
8
|
+
sampleid: str | None = Field(default=None, description="adata sampleid")
|
9
|
+
adtype: str = Field(default="exp", description="The input adata.X data type for preprocess/analysis/plotting")
|
10
|
+
|
11
|
+
model_config = ConfigDict(
|
12
|
+
extra="ignore"
|
13
|
+
)
|
14
|
+
|
15
|
+
class AdataInfo(BaseModel):
|
16
|
+
"""Input schema for the adata tool."""
|
17
|
+
sampleid: str | None = Field(default=None, description="adata sampleid")
|
18
|
+
adtype: str = Field(default="exp", description="The input adata.X data type for preprocess/analysis/plotting")
|
19
|
+
|
20
|
+
model_config = ConfigDict(
|
21
|
+
extra="ignore"
|
22
|
+
)
|
scmcp_shared/schema/io.py
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
from pydantic import (
|
2
2
|
Field,
|
3
3
|
field_validator,
|
4
|
-
model_validator,
|
4
|
+
model_validator, BaseModel
|
5
5
|
)
|
6
6
|
from typing import Optional, Literal
|
7
|
-
from .base import AdataModel
|
8
7
|
|
9
8
|
|
10
|
-
|
9
|
+
|
10
|
+
class ReadModel(BaseModel):
|
11
11
|
"""Input schema for the read tool."""
|
12
12
|
filename: str = Field(
|
13
13
|
...,
|
@@ -22,10 +22,6 @@ class ReadModel(AdataModel):
|
|
22
22
|
default=None,
|
23
23
|
description="Name of sheet/table in hdf5 or Excel file."
|
24
24
|
)
|
25
|
-
ext: str = Field(
|
26
|
-
default=None,
|
27
|
-
description="Extension that indicates the file type. If None, uses extension of filename."
|
28
|
-
)
|
29
25
|
delimiter: str = Field(
|
30
26
|
default=None,
|
31
27
|
description="Delimiter that separates data within text file. If None, will split at arbitrary number of white spaces, which is different from enforcing splitting at any single white space."
|
@@ -86,7 +82,7 @@ class ReadModel(AdataModel):
|
|
86
82
|
return v
|
87
83
|
|
88
84
|
|
89
|
-
class WriteModel(
|
85
|
+
class WriteModel(BaseModel):
|
90
86
|
"""Input schema for the write tool."""
|
91
87
|
filename: str = Field(
|
92
88
|
description="Path to save the file. If no extension is provided, the default format will be used."
|
scmcp_shared/schema/pl.py
CHANGED
@@ -7,7 +7,6 @@ from pydantic import (
|
|
7
7
|
model_validator,
|
8
8
|
BaseModel
|
9
9
|
)
|
10
|
-
from .base import AdataModel
|
11
10
|
|
12
11
|
# 创建 Mixin 类处理特定功能
|
13
12
|
class LegendMixin:
|
@@ -73,7 +72,7 @@ class FigureSizeMixin:
|
|
73
72
|
|
74
73
|
|
75
74
|
# 基础可视化模型,包含所有可视化工具共享的字段
|
76
|
-
class BaseVisualizationModel(
|
75
|
+
class BaseVisualizationModel(BaseModel, LegendMixin, ColorMappingMixin, FigureSizeMixin):
|
77
76
|
"""基础可视化模型,包含所有可视化工具共享的字段"""
|
78
77
|
pass
|
79
78
|
|
@@ -632,7 +631,7 @@ class RankGenesGroupsModel(BaseVisualizationModel):
|
|
632
631
|
|
633
632
|
|
634
633
|
# 重构 ClusterMapModel
|
635
|
-
class ClusterMapModel(
|
634
|
+
class ClusterMapModel(BaseModel):
|
636
635
|
"""Input schema for the clustermap plotting tool."""
|
637
636
|
|
638
637
|
obs_keys: Optional[str] = Field(
|
scmcp_shared/schema/pp.py
CHANGED
@@ -3,15 +3,15 @@ from pydantic import (
|
|
3
3
|
ValidationInfo,
|
4
4
|
computed_field,
|
5
5
|
field_validator,
|
6
|
-
model_validator,
|
6
|
+
model_validator,BaseModel
|
7
7
|
)
|
8
|
-
|
8
|
+
|
9
9
|
from typing import Optional, Union, List, Dict, Any
|
10
10
|
from typing import Literal
|
11
11
|
import numpy as np
|
12
12
|
|
13
13
|
|
14
|
-
class FilterCells(
|
14
|
+
class FilterCells(BaseModel):
|
15
15
|
"""Input schema for the filter_cells preprocessing tool."""
|
16
16
|
|
17
17
|
min_counts: Optional[int] = Field(
|
@@ -42,7 +42,7 @@ class FilterCells(AdataModel):
|
|
42
42
|
return v
|
43
43
|
|
44
44
|
|
45
|
-
class FilterGenes(
|
45
|
+
class FilterGenes(BaseModel):
|
46
46
|
"""Input schema for the filter_genes preprocessing tool."""
|
47
47
|
|
48
48
|
min_counts: Optional[int] = Field(
|
@@ -73,7 +73,7 @@ class FilterGenes(AdataModel):
|
|
73
73
|
return v
|
74
74
|
|
75
75
|
|
76
|
-
class SubsetCellModel(
|
76
|
+
class SubsetCellModel(BaseModel):
|
77
77
|
"""Input schema for subsetting AnnData objects based on various criteria."""
|
78
78
|
obs_key: Optional[str] = Field(
|
79
79
|
default=None,
|
@@ -109,7 +109,7 @@ class SubsetCellModel(AdataModel):
|
|
109
109
|
)
|
110
110
|
|
111
111
|
|
112
|
-
class SubsetGeneModel(
|
112
|
+
class SubsetGeneModel(BaseModel):
|
113
113
|
"""Input schema for subsetting AnnData objects based on various criteria."""
|
114
114
|
min_counts: Optional[int] = Field(
|
115
115
|
default=None,
|
@@ -145,7 +145,7 @@ class SubsetGeneModel(AdataModel):
|
|
145
145
|
)
|
146
146
|
|
147
147
|
|
148
|
-
class CalculateQCMetrics(
|
148
|
+
class CalculateQCMetrics(BaseModel):
|
149
149
|
"""Input schema for the calculate_qc_metrics preprocessing tool."""
|
150
150
|
|
151
151
|
expr_type: str = Field(
|
@@ -196,7 +196,7 @@ class CalculateQCMetrics(AdataModel):
|
|
196
196
|
|
197
197
|
|
198
198
|
|
199
|
-
class Log1PModel(
|
199
|
+
class Log1PModel(BaseModel):
|
200
200
|
"""Input schema for the log1p preprocessing tool."""
|
201
201
|
|
202
202
|
base: Optional[Union[int, float]] = Field(
|
@@ -233,7 +233,7 @@ class Log1PModel(AdataModel):
|
|
233
233
|
|
234
234
|
|
235
235
|
|
236
|
-
class HighlyVariableGenesModel(
|
236
|
+
class HighlyVariableGenesModel(BaseModel):
|
237
237
|
"""Input schema for the highly_variable_genes preprocessing tool."""
|
238
238
|
|
239
239
|
layer: Optional[str] = Field(
|
@@ -307,7 +307,7 @@ class HighlyVariableGenesModel(AdataModel):
|
|
307
307
|
return v
|
308
308
|
|
309
309
|
|
310
|
-
class RegressOutModel(
|
310
|
+
class RegressOutModel(BaseModel):
|
311
311
|
"""Input schema for the regress_out preprocessing tool."""
|
312
312
|
|
313
313
|
keys: Union[str, List[str]] = Field(
|
@@ -340,7 +340,7 @@ class RegressOutModel(AdataModel):
|
|
340
340
|
raise ValueError("keys must be a string or list of strings")
|
341
341
|
|
342
342
|
|
343
|
-
class ScaleModel(
|
343
|
+
class ScaleModel(BaseModel):
|
344
344
|
"""Input schema for the scale preprocessing tool."""
|
345
345
|
|
346
346
|
zero_center: bool = Field(
|
@@ -376,7 +376,7 @@ class ScaleModel(AdataModel):
|
|
376
376
|
return v
|
377
377
|
|
378
378
|
|
379
|
-
class CombatModel(
|
379
|
+
class CombatModel(BaseModel):
|
380
380
|
"""Input schema for the combat batch effect correction tool."""
|
381
381
|
|
382
382
|
key: str = Field(
|
@@ -405,7 +405,7 @@ class CombatModel(AdataModel):
|
|
405
405
|
return v
|
406
406
|
|
407
407
|
|
408
|
-
class ScrubletModel(
|
408
|
+
class ScrubletModel(BaseModel):
|
409
409
|
"""Input schema for the scrublet doublet prediction tool."""
|
410
410
|
|
411
411
|
adata_sim: Optional[str] = Field(
|
@@ -511,7 +511,7 @@ class ScrubletModel(AdataModel):
|
|
511
511
|
return v.lower()
|
512
512
|
|
513
513
|
|
514
|
-
class NeighborsModel(
|
514
|
+
class NeighborsModel(BaseModel):
|
515
515
|
"""Input schema for the neighbors graph construction tool."""
|
516
516
|
|
517
517
|
n_neighbors: int = Field(
|
@@ -589,7 +589,7 @@ class NeighborsModel(AdataModel):
|
|
589
589
|
return v
|
590
590
|
|
591
591
|
|
592
|
-
class NormalizeTotalModel(
|
592
|
+
class NormalizeTotalModel(BaseModel):
|
593
593
|
"""Input schema for the normalize_total preprocessing tool."""
|
594
594
|
|
595
595
|
target_sum: Optional[float] = Field(
|
scmcp_shared/schema/tl.py
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
from pydantic import Field, field_validator, ValidationInfo
|
1
|
+
from pydantic import Field, field_validator, ValidationInfo, BaseModel
|
2
2
|
from typing import Optional, Union, List, Dict, Any, Tuple, Literal, Mapping
|
3
3
|
|
4
|
-
from .base import AdataModel
|
5
4
|
|
6
|
-
class TSNEModel(
|
5
|
+
class TSNEModel(BaseModel):
|
7
6
|
"""Input schema for the t-SNE dimensionality reduction tool."""
|
8
7
|
n_pcs: Optional[int] = Field(
|
9
8
|
default=None,
|
@@ -60,7 +59,7 @@ class TSNEModel(AdataModel):
|
|
60
59
|
return v.lower()
|
61
60
|
|
62
61
|
|
63
|
-
class UMAPModel(
|
62
|
+
class UMAPModel(BaseModel):
|
64
63
|
"""Input schema for the UMAP dimensionality reduction tool."""
|
65
64
|
|
66
65
|
min_dist: Optional[float] = Field(
|
@@ -146,7 +145,7 @@ class UMAPModel(AdataModel):
|
|
146
145
|
return v.lower()
|
147
146
|
|
148
147
|
|
149
|
-
class DrawGraphModel(
|
148
|
+
class DrawGraphModel(BaseModel):
|
150
149
|
"""Input schema for the force-directed graph drawing tool."""
|
151
150
|
|
152
151
|
layout: str = Field(
|
@@ -200,7 +199,7 @@ class DrawGraphModel(AdataModel):
|
|
200
199
|
return v
|
201
200
|
|
202
201
|
|
203
|
-
class DiffMapModel(
|
202
|
+
class DiffMapModel(BaseModel):
|
204
203
|
"""Input schema for the Diffusion Maps dimensionality reduction tool."""
|
205
204
|
|
206
205
|
n_comps: int = Field(
|
@@ -230,7 +229,7 @@ class DiffMapModel(AdataModel):
|
|
230
229
|
return v
|
231
230
|
|
232
231
|
|
233
|
-
class EmbeddingDensityModel(
|
232
|
+
class EmbeddingDensityModel(BaseModel):
|
234
233
|
"""Input schema for the embedding density calculation tool."""
|
235
234
|
|
236
235
|
basis: str = Field(
|
@@ -258,7 +257,7 @@ class EmbeddingDensityModel(AdataModel):
|
|
258
257
|
return v
|
259
258
|
|
260
259
|
|
261
|
-
class LeidenModel(
|
260
|
+
class LeidenModel(BaseModel):
|
262
261
|
"""Input schema for the Leiden clustering algorithm."""
|
263
262
|
|
264
263
|
resolution: Optional[float] = Field(
|
@@ -301,11 +300,6 @@ class LeidenModel(AdataModel):
|
|
301
300
|
description="Which package's implementation to use."
|
302
301
|
)
|
303
302
|
|
304
|
-
clustering_args: Optional[Dict[str, Any]] = Field(
|
305
|
-
default=None,
|
306
|
-
description="Any further arguments to pass to the clustering algorithm."
|
307
|
-
)
|
308
|
-
|
309
303
|
@field_validator('resolution')
|
310
304
|
def validate_resolution(cls, v: float) -> float:
|
311
305
|
"""Validate resolution is positive"""
|
@@ -330,7 +324,7 @@ class LeidenModel(AdataModel):
|
|
330
324
|
return v
|
331
325
|
|
332
326
|
|
333
|
-
class LouvainModel(
|
327
|
+
class LouvainModel(BaseModel):
|
334
328
|
"""Input schema for the Louvain clustering algorithm."""
|
335
329
|
|
336
330
|
resolution: Optional[float] = Field(
|
@@ -402,7 +396,7 @@ class LouvainModel(AdataModel):
|
|
402
396
|
return v
|
403
397
|
|
404
398
|
|
405
|
-
class DendrogramModel(
|
399
|
+
class DendrogramModel(BaseModel):
|
406
400
|
"""Input schema for the hierarchical clustering dendrogram tool."""
|
407
401
|
|
408
402
|
groupby: str = Field(
|
@@ -467,7 +461,7 @@ class DendrogramModel(AdataModel):
|
|
467
461
|
return v
|
468
462
|
|
469
463
|
|
470
|
-
class DPTModel(
|
464
|
+
class DPTModel(BaseModel):
|
471
465
|
"""Input schema for the Diffusion Pseudotime (DPT) tool."""
|
472
466
|
|
473
467
|
n_dcs: int = Field(
|
@@ -516,7 +510,7 @@ class DPTModel(AdataModel):
|
|
516
510
|
raise ValueError("min_group_size must be between 0 and 1")
|
517
511
|
return v
|
518
512
|
|
519
|
-
class PAGAModel(
|
513
|
+
class PAGAModel(BaseModel):
|
520
514
|
"""Input schema for the Partition-based Graph Abstraction (PAGA) tool."""
|
521
515
|
|
522
516
|
groups: Optional[str] = Field(
|
@@ -544,7 +538,7 @@ class PAGAModel(AdataModel):
|
|
544
538
|
return v
|
545
539
|
|
546
540
|
|
547
|
-
class IngestModel(
|
541
|
+
class IngestModel(BaseModel):
|
548
542
|
"""Input schema for the ingest tool that maps labels and embeddings from reference data to new data."""
|
549
543
|
|
550
544
|
obs: Optional[Union[str, List[str]]] = Field(
|
@@ -593,7 +587,7 @@ class IngestModel(AdataModel):
|
|
593
587
|
return v.lower()
|
594
588
|
|
595
589
|
|
596
|
-
class RankGenesGroupsModel(
|
590
|
+
class RankGenesGroupsModel(BaseModel):
|
597
591
|
"""Input schema for the rank_genes_groups tool."""
|
598
592
|
|
599
593
|
groupby: str = Field(
|
@@ -675,7 +669,7 @@ class RankGenesGroupsModel(AdataModel):
|
|
675
669
|
return v
|
676
670
|
|
677
671
|
|
678
|
-
class FilterRankGenesGroupsModel(
|
672
|
+
class FilterRankGenesGroupsModel(BaseModel):
|
679
673
|
"""Input schema for filtering ranked genes groups."""
|
680
674
|
|
681
675
|
key: Optional[str] = Field(
|
@@ -738,7 +732,7 @@ class FilterRankGenesGroupsModel(AdataModel):
|
|
738
732
|
return v
|
739
733
|
|
740
734
|
|
741
|
-
class MarkerGeneOverlapModel(
|
735
|
+
class MarkerGeneOverlapModel(BaseModel):
|
742
736
|
"""Input schema for the marker gene overlap tool."""
|
743
737
|
|
744
738
|
key: str = Field(
|
@@ -809,7 +803,7 @@ class MarkerGeneOverlapModel(AdataModel):
|
|
809
803
|
return v
|
810
804
|
|
811
805
|
|
812
|
-
class ScoreGenesModel(
|
806
|
+
class ScoreGenesModel(BaseModel):
|
813
807
|
"""Input schema for the score_genes tool that calculates gene scores based on average expression."""
|
814
808
|
|
815
809
|
ctrl_size: int = Field(
|
@@ -852,7 +846,7 @@ class ScoreGenesModel(AdataModel):
|
|
852
846
|
return v
|
853
847
|
|
854
848
|
|
855
|
-
class ScoreGenesCellCycleModel(
|
849
|
+
class ScoreGenesCellCycleModel(BaseModel):
|
856
850
|
"""Input schema for the score_genes_cell_cycle tool that scores cell cycle genes."""
|
857
851
|
|
858
852
|
s_genes: List[str] = Field(
|
@@ -902,7 +896,7 @@ class ScoreGenesCellCycleModel(AdataModel):
|
|
902
896
|
|
903
897
|
|
904
898
|
|
905
|
-
class PCAModel(
|
899
|
+
class PCAModel(BaseModel):
|
906
900
|
"""Input schema for the PCA preprocessing tool."""
|
907
901
|
|
908
902
|
n_comps: Optional[int] = Field(
|
scmcp_shared/schema/util.py
CHANGED
@@ -4,13 +4,13 @@ from pydantic import (
|
|
4
4
|
ValidationInfo,
|
5
5
|
computed_field,
|
6
6
|
field_validator,
|
7
|
-
model_validator,
|
7
|
+
model_validator,BaseModel
|
8
8
|
)
|
9
9
|
from typing import Optional, Union, List, Dict, Any, Callable, Collection, Literal
|
10
|
-
from .base import AdataModel
|
11
10
|
|
12
11
|
|
13
|
-
|
12
|
+
|
13
|
+
class MarkVarModel(BaseModel):
|
14
14
|
"""Determine or mark if each gene meets specific conditions and store results in adata.var as boolean values"""
|
15
15
|
|
16
16
|
var_name: str = Field(
|
@@ -32,15 +32,15 @@ class MarkVarModel(AdataModel):
|
|
32
32
|
)
|
33
33
|
|
34
34
|
|
35
|
-
class ListVarModel(
|
35
|
+
class ListVarModel(BaseModel):
|
36
36
|
"""ListVarModel"""
|
37
37
|
pass
|
38
38
|
|
39
|
-
class ListObsModel(
|
39
|
+
class ListObsModel(BaseModel):
|
40
40
|
"""ListObsModel"""
|
41
41
|
pass
|
42
42
|
|
43
|
-
class VarNamesModel(
|
43
|
+
class VarNamesModel(BaseModel):
|
44
44
|
"""ListObsModel"""
|
45
45
|
var_names: List[str] = Field(
|
46
46
|
default=None,
|
@@ -48,7 +48,7 @@ class VarNamesModel(AdataModel):
|
|
48
48
|
)
|
49
49
|
|
50
50
|
|
51
|
-
class
|
51
|
+
class ConcatBaseModel(BaseModel):
|
52
52
|
"""Model for concatenating AnnData objects"""
|
53
53
|
|
54
54
|
axis: Literal['obs', 0, 'var', 1] = Field(
|
@@ -89,7 +89,7 @@ class ConcatAdataModel(AdataModel):
|
|
89
89
|
)
|
90
90
|
|
91
91
|
|
92
|
-
class DPTIROOTModel(
|
92
|
+
class DPTIROOTModel(BaseModel):
|
93
93
|
"""Input schema for setting the root cell for diffusion pseudotime."""
|
94
94
|
diffmap_key: str = Field(
|
95
95
|
default="X_diffmap",
|
@@ -103,7 +103,7 @@ class DPTIROOTModel(AdataModel):
|
|
103
103
|
)
|
104
104
|
|
105
105
|
|
106
|
-
class CelltypeMapCellTypeModel(
|
106
|
+
class CelltypeMapCellTypeModel(BaseModel):
|
107
107
|
"""Input schema for mapping cluster IDs to cell type names."""
|
108
108
|
cluster_key: str = Field(
|
109
109
|
description="Key in adata.obs containing cluster IDs."
|
@@ -122,14 +122,14 @@ class CelltypeMapCellTypeModel(AdataModel):
|
|
122
122
|
|
123
123
|
|
124
124
|
|
125
|
-
class AddLayerModel(
|
125
|
+
class AddLayerModel(BaseModel):
|
126
126
|
"""Input schema for adding a layer to AnnData object."""
|
127
127
|
layer_name: str = Field(
|
128
128
|
description="Name of the layer to add to adata.layers."
|
129
129
|
)
|
130
130
|
|
131
131
|
|
132
|
-
class QueryOpLogModel(
|
132
|
+
class QueryOpLogModel(BaseModel):
|
133
133
|
"""QueryOpLogModel"""
|
134
134
|
n: int = Field(
|
135
135
|
default=10,
|
scmcp_shared/server/__init__.py
CHANGED
@@ -1,51 +1,13 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from collections.abc import Iterable
|
4
|
-
from typing import Any
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
from .
|
9
|
-
from .
|
10
|
-
from .
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
class AdataState:
|
15
|
-
def __init__(self, add_adtypes=None):
|
16
|
-
self.adata_dic = {"exp": {}, "activity": {}, "cnv": {}, "splicing": {}}
|
17
|
-
if isinstance(add_adtypes, str):
|
18
|
-
self.adata_dic[add_adtypes] = {}
|
19
|
-
elif isinstance(add_adtypes, Iterable):
|
20
|
-
self.adata_dic.update({adtype: {} for adtype in add_adtypes})
|
21
|
-
self.active_id = None
|
22
|
-
self.metadatWa = {}
|
23
|
-
self.cr_kernel = {}
|
24
|
-
self.cr_estimator = {}
|
25
|
-
|
26
|
-
def get_adata(self, sampleid=None, adtype="exp", request=None):
|
27
|
-
if request is not None:
|
28
|
-
kwargs = request.model_dump()
|
29
|
-
sampleid = kwargs.get("sampleid", None)
|
30
|
-
adtype = kwargs.get("adtype", "exp")
|
31
|
-
try:
|
32
|
-
if self.active_id is None:
|
33
|
-
return None
|
34
|
-
sampleid = sampleid or self.active_id
|
35
|
-
return self.adata_dic[adtype][sampleid]
|
36
|
-
except KeyError as e:
|
37
|
-
raise KeyError(f"Key {e} not found in adata_dic[{adtype}].Please check the sampleid or adtype.")
|
38
|
-
except Exception as e:
|
39
|
-
raise Exception(f"fuck {e} {type(e)}")
|
40
|
-
|
41
|
-
def set_adata(self, adata, sampleid=None, sdtype="exp", request=None):
|
42
|
-
if request is not None:
|
43
|
-
kwargs = request.model_dump()
|
44
|
-
sampleid = kwargs.get("sampleid", None)
|
45
|
-
sdtype = kwargs.get("adtype", "exp")
|
46
|
-
sampleid = sampleid or self.active_id
|
47
|
-
if sdtype not in self.adata_dic:
|
48
|
-
self.adata_dic[sdtype] = {}
|
49
|
-
self.adata_dic[sdtype][sampleid] = adata
|
50
|
-
|
51
|
-
|
3
|
+
from collections.abc import Iterable, AsyncIterator
|
4
|
+
from typing import Any, Dict, List, Optional
|
5
|
+
from contextlib import asynccontextmanager
|
6
|
+
import asyncio
|
7
|
+
|
8
|
+
from .base import BaseMCP,AdataState,BaseMCPManager
|
9
|
+
from .io import ScanpyIOMCP, io_mcp
|
10
|
+
from .util import ScanpyUtilMCP
|
11
|
+
from .pl import ScanpyPlottingMCP
|
12
|
+
from .pp import ScanpyPreprocessingMCP
|
13
|
+
from .tl import ScanpyToolsMCP
|