uprobe 0.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 (55) hide show
  1. uprobe/__init__.py +18 -0
  2. uprobe/__main__.py +17 -0
  3. uprobe/core/.readthedocs.yaml +53 -0
  4. uprobe/core/__init__.py +26 -0
  5. uprobe/core/agent/__init__.py +1 -0
  6. uprobe/core/agent/repl_bootstrap.py +193 -0
  7. uprobe/core/agent/session_manager.py +207 -0
  8. uprobe/core/agent/templates/DEFAULT_PROTOCOL.yaml +55 -0
  9. uprobe/core/api.py +364 -0
  10. uprobe/core/attributes/__init__.py +132 -0
  11. uprobe/core/attributes/_attributes.py +239 -0
  12. uprobe/core/cli.py +802 -0
  13. uprobe/core/gen/__init__.py +0 -0
  14. uprobe/core/gen/barcodes.py +108 -0
  15. uprobe/core/gen/fun.py +310 -0
  16. uprobe/core/gen/probe.py +216 -0
  17. uprobe/core/gen/utils.py +25 -0
  18. uprobe/core/process/__init__.py +224 -0
  19. uprobe/core/process/equal_space.py +48 -0
  20. uprobe/core/process/otp.py +155 -0
  21. uprobe/core/process/summary.py +150 -0
  22. uprobe/core/report/__init__.py +9 -0
  23. uprobe/core/report/html.py +854 -0
  24. uprobe/core/report/plot.py +499 -0
  25. uprobe/core/tools/__init__.py +94 -0
  26. uprobe/core/tools/aligner.py +85 -0
  27. uprobe/core/tools/kmerFilter.py +324 -0
  28. uprobe/core/utils.py +165 -0
  29. uprobe/http/__init__.py +1 -0
  30. uprobe/http/__main__.py +4 -0
  31. uprobe/http/routers/__init__.py +0 -0
  32. uprobe/http/routers/agent.py +538 -0
  33. uprobe/http/routers/auth.py +719 -0
  34. uprobe/http/routers/custom_probes.py +98 -0
  35. uprobe/http/routers/genome.py +322 -0
  36. uprobe/http/routers/task.py +548 -0
  37. uprobe/http/routers/user.py +56 -0
  38. uprobe/http/routers/workflow.py +284 -0
  39. uprobe/http/server.py +102 -0
  40. uprobe/http/utils/__init__.py +0 -0
  41. uprobe/http/utils/agent_store.py +386 -0
  42. uprobe/http/utils/file.py +112 -0
  43. uprobe/http/utils/logger.py +89 -0
  44. uprobe/http/utils/paths.py +115 -0
  45. uprobe/http/utils/probe.py +75 -0
  46. uprobe/http/utils/process_pool.py +25 -0
  47. uprobe/http/utils/task_queue.py +54 -0
  48. uprobe/http/utils/uprobe_runner.py +65 -0
  49. uprobe/http/utils/utils.py +12 -0
  50. uprobe-0.1.0.dist-info/METADATA +73 -0
  51. uprobe-0.1.0.dist-info/RECORD +55 -0
  52. uprobe-0.1.0.dist-info/WHEEL +5 -0
  53. uprobe-0.1.0.dist-info/entry_points.txt +3 -0
  54. uprobe-0.1.0.dist-info/licenses/LICENSE +21 -0
  55. uprobe-0.1.0.dist-info/top_level.txt +1 -0
uprobe/__init__.py ADDED
@@ -0,0 +1,18 @@
1
+ """
2
+ U-Probe package.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any
8
+
9
+ __version__ = "0.1.0"
10
+
11
+ __all__ = ["UProbeAPI", "__version__"]
12
+
13
+ def __getattr__(name: str) -> Any:
14
+ """Lazy attribute access for optional heavy imports."""
15
+ if name == "UProbeAPI":
16
+ from uprobe.core.api import UProbeAPI
17
+ return UProbeAPI
18
+ raise AttributeError(f"module 'uprobe' has no attribute {name!r}")
uprobe/__main__.py ADDED
@@ -0,0 +1,17 @@
1
+
2
+ """
3
+ Main entry point for U-Probe CLI.
4
+ """
5
+ import sys
6
+ import os
7
+ from pathlib import Path
8
+
9
+ # Add project root to sys.path for development
10
+ project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
11
+ sys.path.append(project_root)
12
+
13
+ from uprobe.core.cli import main
14
+
15
+
16
+ if __name__ == '__main__':
17
+ main()
@@ -0,0 +1,53 @@
1
+ # Read the Docs configuration file for U-Probe
2
+ # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
3
+
4
+ version: 2
5
+
6
+ # Set the OS, Python version and other tools you might need
7
+ build:
8
+ os: ubuntu-22.04
9
+ tools:
10
+ python: "3.8"
11
+ # You can also specify other tools and their versions:
12
+ # nodejs: "16"
13
+ # rust: "1.55"
14
+ # golang: "1.17"
15
+
16
+ # Build documentation in the docs/ directory with Sphinx
17
+ sphinx:
18
+ configuration: docs/source/conf.py
19
+ # You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
20
+ # builder: "dirhtml"
21
+ # Fail on all warnings
22
+ fail_on_warning: false
23
+
24
+ # Optionally build your docs in additional formats such as PDF
25
+ formats:
26
+ - pdf
27
+ - epub
28
+
29
+ # Optional but recommended, declare the Python requirements required
30
+ # to build your documentation
31
+ # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
32
+ python:
33
+ install:
34
+ - method: pip
35
+ path: .
36
+ extra_requirements:
37
+ - docs
38
+ - requirements: docs/requirements.txt
39
+
40
+ # Set the version of Python and other tools you might need
41
+ # build:
42
+ # os: ubuntu-22.04
43
+ # tools:
44
+ # python: "3.8"
45
+ # jobs:
46
+ # post_create_environment:
47
+ # # Install poetry
48
+ # - pip install poetry
49
+ # # Tell poetry to not use a virtual environment
50
+ # - poetry config virtualenvs.create false
51
+ # post_install:
52
+ # # Install dependencies
53
+ # - poetry install --with docs
@@ -0,0 +1,26 @@
1
+ """
2
+ U-Probe package.
3
+
4
+ Note:
5
+ This package exposes `UProbeAPI`, but we avoid importing it eagerly to keep
6
+ lightweight entry points (e.g. `python -m uprobe.core.agent.repl_bootstrap`) working
7
+ even in environments where optional runtime dependencies are not installed yet.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any
13
+
14
+ from uprobe import __version__
15
+
16
+ __all__ = ["UProbeAPI"]
17
+
18
+
19
+ def __getattr__(name: str) -> Any:
20
+ """Lazy attribute access for optional heavy imports."""
21
+ if name == "UProbeAPI":
22
+ from .api import UProbeAPI # Local import to avoid import-time deps
23
+
24
+ return UProbeAPI
25
+ raise AttributeError(f"module 'uprobe.core' has no attribute {name!r}")
26
+
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,193 @@
1
+ """
2
+ Bootstrap Pantheon REPL in a workspace with the U-Probe team template.
3
+
4
+ This module intentionally bypasses the existing U-Probe CLI and the
5
+ `uprobe.core.agent.api.UProbeAgentAPI` workflow. Instead, it:
6
+ 1) Copies `uprobe_team.md` into `<workspace>/.pantheon/teams/`
7
+ 2) Launches `python -m pantheon.repl --template <copied_md>`
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import os
14
+ import shutil
15
+ import subprocess
16
+ import sys
17
+ from pathlib import Path
18
+
19
+
20
+ def _build_arg_parser() -> argparse.ArgumentParser:
21
+ """Build CLI argument parser."""
22
+ parser = argparse.ArgumentParser(
23
+ prog="python -m uprobe.core.agent.repl_bootstrap",
24
+ description=(
25
+ "Copy uprobe_team.md into <workspace>/.pantheon/teams/ and launch "
26
+ "Pantheon REPL with --template so uprobe_team is loaded by default."
27
+ ),
28
+ )
29
+ parser.add_argument(
30
+ "--workspace",
31
+ type=str,
32
+ default=str(Path.cwd()),
33
+ help="Workspace directory to install templates into (default: CWD).",
34
+ )
35
+ parser.add_argument(
36
+ "--force",
37
+ action="store_true",
38
+ help="Overwrite existing <workspace>/.pantheon/teams/uprobe_team.md.",
39
+ )
40
+
41
+ # Pass-through options for pantheon.repl (keep it minimal but useful).
42
+ parser.add_argument(
43
+ "--memory-dir",
44
+ type=str,
45
+ default=None,
46
+ help="Pantheon memory dir (passed to pantheon.repl).",
47
+ )
48
+ parser.add_argument(
49
+ "--log-level",
50
+ type=str,
51
+ default=None,
52
+ help="Log level for REPL (DEBUG, INFO, WARNING, ERROR, CRITICAL).",
53
+ )
54
+ parser.add_argument(
55
+ "--quiet",
56
+ action="store_true",
57
+ help="Disable console logging in REPL (passed to pantheon.repl).",
58
+ )
59
+ parser.add_argument(
60
+ "--resync",
61
+ action="store_true",
62
+ help=(
63
+ "Force pantheon.repl to resync templates "
64
+ "(cleans skills/agents/teams/prompts dirs)."
65
+ ),
66
+ )
67
+ parser.add_argument(
68
+ "--chat-id",
69
+ type=str,
70
+ default=None,
71
+ help="Resume a specific chat ID (passed to pantheon.repl).",
72
+ )
73
+ parser.add_argument(
74
+ "repl_args",
75
+ nargs=argparse.REMAINDER,
76
+ help=(
77
+ "Additional arguments passed to pantheon.repl after '--'. "
78
+ "Example: -- --log-level DEBUG"
79
+ ),
80
+ )
81
+ return parser
82
+
83
+
84
+ def _normalize_repl_args(remainder: list[str]) -> list[str]:
85
+ """Normalize argparse REMAINDER (strip leading '--' if present)."""
86
+ if remainder and remainder[0] == "--":
87
+ return remainder[1:]
88
+ return remainder
89
+
90
+
91
+ def _install_team_template(workspace: Path, force: bool) -> Path:
92
+ """Copy uprobe_team.md into <workspace>/.pantheon/teams/ and return dest."""
93
+ src = Path(__file__).resolve().parent / "templates" / "uprobe_team.md"
94
+ if not src.exists():
95
+ raise FileNotFoundError(f"Team template not found: {src}")
96
+
97
+ dest_dir = workspace / ".pantheon" / "teams"
98
+ dest_dir.mkdir(parents=True, exist_ok=True)
99
+
100
+ dest = dest_dir / "uprobe_team.md"
101
+ if dest.exists() and not force:
102
+ return dest
103
+
104
+ shutil.copy2(src, dest)
105
+ return dest
106
+
107
+
108
+ def _install_protocol_template(workspace: Path, force: bool) -> Path:
109
+ """Copy DEFAULT_PROTOCOL.yaml into <workspace>/ and return dest."""
110
+ src = Path(__file__).resolve().parent / "templates" / "DEFAULT_PROTOCOL.yaml"
111
+ if not src.exists():
112
+ raise FileNotFoundError(f"Protocol template not found: {src}")
113
+
114
+ dest = workspace / "DEFAULT_PROTOCOL.yaml"
115
+ # Skip copying DEFAULT_PROTOCOL.yaml to avoid cluttering the workspace
116
+ # if dest.exists() and not force:
117
+ # return dest
118
+ #
119
+ # shutil.copy2(src, dest)
120
+ return dest
121
+
122
+
123
+ def _launch_repl(
124
+ workspace: Path,
125
+ template_path: Path,
126
+ memory_dir: str | None,
127
+ log_level: str | None,
128
+ quiet: bool,
129
+ resync: bool,
130
+ chat_id: str | None,
131
+ extra_args: list[str],
132
+ ) -> int:
133
+ """Launch pantheon.repl with --template and return exit code."""
134
+ # Ensure `pantheon` can be imported even when running in an arbitrary
135
+ # workspace directory during development (repo not installed site-wide).
136
+ repo_root = Path(__file__).resolve().parents[3]
137
+ env = os.environ.copy()
138
+ pythonpath = env.get("PYTHONPATH", "")
139
+ env["PYTHONPATH"] = (
140
+ f"{repo_root}{os.pathsep}{pythonpath}" if pythonpath else str(repo_root)
141
+ )
142
+
143
+ cmd: list[str] = [
144
+ sys.executable,
145
+ "-m",
146
+ "pantheon.repl",
147
+ "--template",
148
+ str(template_path),
149
+ "--workspace",
150
+ str(workspace),
151
+ ]
152
+ if memory_dir:
153
+ cmd += ["--memory-dir", memory_dir]
154
+ if chat_id:
155
+ cmd += ["--chat-id", chat_id]
156
+ if log_level:
157
+ cmd += ["--log-level", log_level]
158
+ if quiet:
159
+ cmd += ["--quiet"]
160
+ if resync:
161
+ cmd += ["--resync"]
162
+ if extra_args:
163
+ cmd += extra_args
164
+
165
+ completed = subprocess.run(cmd, cwd=str(workspace), env=env)
166
+ return int(completed.returncode)
167
+
168
+
169
+ def main(argv: list[str] | None = None) -> int:
170
+ """Main entry point."""
171
+ parser = _build_arg_parser()
172
+ args = parser.parse_args(argv)
173
+
174
+ workspace = Path(args.workspace).expanduser().resolve()
175
+ template_path = _install_team_template(workspace=workspace, force=bool(args.force))
176
+ # _install_protocol_template(workspace=workspace, force=bool(args.force))
177
+
178
+ extra_args = _normalize_repl_args(list(args.repl_args or []))
179
+ return _launch_repl(
180
+ workspace=workspace,
181
+ template_path=template_path,
182
+ memory_dir=args.memory_dir,
183
+ log_level=args.log_level,
184
+ quiet=bool(args.quiet),
185
+ resync=bool(args.resync),
186
+ chat_id=args.chat_id,
187
+ extra_args=extra_args,
188
+ )
189
+
190
+
191
+ if __name__ == "__main__":
192
+ raise SystemExit(main())
193
+
@@ -0,0 +1,207 @@
1
+ import os
2
+ import uuid
3
+ import shutil
4
+ import logging
5
+ from pathlib import Path
6
+ from typing import Dict, Any, Optional, List, BinaryIO
7
+
8
+ from pantheon.chatroom import ChatRoom
9
+ from pantheon.factory.template_io import UnifiedMarkdownParser
10
+ from pantheon.utils.vision import parse_image_mentions
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class AgentSessionManager:
16
+ """Manages agent sessions using Pantheon ChatRoom."""
17
+
18
+ def __init__(self, workspace_root: Path | None = None):
19
+ if workspace_root is None:
20
+ workspace_root = Path(__file__).resolve().parents[3]
21
+ self.workspace_root = workspace_root
22
+ # Use absolute path to the template file in the source code
23
+ self.template_path = Path(__file__).resolve().parent / "templates" / "uprobe_team.md"
24
+ self.protocol_template_path = Path(__file__).resolve().parent / "templates" / "DEFAULT_PROTOCOL.yaml"
25
+ self.pantheon_memory_dir = self.workspace_root / ".pantheon" / "memory"
26
+ self.output_dir = Path(os.environ.get("UPROBE_OUTPUT_DIR", str(self.workspace_root / "outputs")))
27
+ self.output_dir.mkdir(parents=True, exist_ok=True)
28
+ self.chatroom = ChatRoom(memory_dir=str(self.pantheon_memory_dir), workspace_path=str(self.workspace_root))
29
+ self.sessions: Dict[str, Dict[str, Any]] = {}
30
+
31
+ async def initialize(self):
32
+ """Initialize the ChatRoom and background services."""
33
+ self._ensure_team_template_registered()
34
+ # self._ensure_protocol_template_installed()
35
+
36
+ # Set pantheon log level to DEBUG to print full agent execution output
37
+ from pantheon.utils.log import set_level
38
+ set_level("DEBUG")
39
+
40
+ await self.chatroom.run_setup()
41
+
42
+ def _load_team_template(self, model: Optional[str]) -> Dict[str, Any]:
43
+ """Load and configure the team template."""
44
+ # Template is already ensured in initialize(), but we resolve the path again to be safe
45
+ team_template_path = self.workspace_root / ".pantheon" / "teams" / "uprobe_team.md"
46
+ if not team_template_path.exists():
47
+ # Fallback if initialize wasn't called or file was deleted
48
+ team_template_path = self._ensure_team_template_registered()
49
+
50
+ parser = UnifiedMarkdownParser()
51
+ team_config = parser.parse_file(team_template_path)
52
+ team_dict = team_config.to_dict()
53
+ team_dict["type"] = "team"
54
+ team_dict["source_path"] = str(team_template_path)
55
+ if model:
56
+ for agent_cfg in team_dict.get("agents", []):
57
+ if isinstance(agent_cfg, dict):
58
+ agent_cfg["model"] = model
59
+ return team_dict
60
+
61
+ def _ensure_team_template_registered(self) -> Path:
62
+ """Ensure uprobe_team.md is registered under .pantheon/teams."""
63
+ dest_dir = self.workspace_root / ".pantheon" / "teams"
64
+ dest_dir.mkdir(parents=True, exist_ok=True)
65
+ dest = dest_dir / "uprobe_team.md"
66
+ if not dest.exists():
67
+ shutil.copy2(self.template_path, dest)
68
+ return dest
69
+
70
+ def _ensure_protocol_template_installed(self) -> Path:
71
+ """Ensure DEFAULT_PROTOCOL.yaml is present in the workspace root."""
72
+ dest = self.workspace_root / "DEFAULT_PROTOCOL.yaml"
73
+ # Skip copying DEFAULT_PROTOCOL.yaml to avoid cluttering the workspace
74
+ # if not dest.exists() and self.protocol_template_path.exists():
75
+ # shutil.copy2(self.protocol_template_path, dest)
76
+ # logger.info(f"Installed protocol template to {dest}")
77
+ return dest
78
+
79
+ def _build_chat_message(self, content: str) -> List[dict]:
80
+ """Build chat message with image support."""
81
+ return parse_image_mentions(content)
82
+
83
+ async def upload_file(self, session_id: str, file_obj: BinaryIO, filename: str, upload_dir: Optional[Path] = None) -> Dict[str, Any]:
84
+ """Save an uploaded file and return its metadata."""
85
+ if upload_dir is None:
86
+ upload_dir = self.output_dir / "uploads" / session_id
87
+ upload_dir.mkdir(parents=True, exist_ok=True)
88
+ file_id = str(uuid.uuid4())
89
+ safe_filename = Path(filename).name
90
+ file_path = upload_dir / safe_filename
91
+ with open(file_path, "wb") as f:
92
+ shutil.copyfileobj(file_obj, f)
93
+ size = file_path.stat().st_size
94
+ logger.info(f"Saved uploaded file to {file_path}")
95
+ if session_id in self.sessions:
96
+ if "uploads" not in self.sessions[session_id]:
97
+ self.sessions[session_id]["uploads"] = {}
98
+ self.sessions[session_id]["uploads"][file_id] = {"path": str(file_path), "filename": safe_filename, "size": size}
99
+ return {"id": file_id, "filename": safe_filename, "path": str(file_path), "size": size}
100
+
101
+ async def create_session(self, model: str = "gpt-4", api_key: Optional[str] = None, api_base: Optional[str] = None, proxy: Optional[str] = None) -> str:
102
+ """Start a new agent session."""
103
+ if api_key:
104
+ os.environ["OPENAI_API_KEY"] = api_key
105
+ if api_base:
106
+ os.environ["OPENAI_API_BASE"] = api_base
107
+ if proxy:
108
+ os.environ["http_proxy"] = proxy
109
+ os.environ["https_proxy"] = proxy
110
+ await self.initialize()
111
+ create_res = await self.chatroom.create_chat("http-session")
112
+ if not create_res.get("success"):
113
+ raise RuntimeError(f"Failed to create chat: {create_res.get('message')}")
114
+ chat_id = create_res["chat_id"]
115
+ try:
116
+ template = self._load_team_template(model)
117
+ setup_res = await self.chatroom.setup_team_for_chat(chat_id, template)
118
+ if not setup_res.get("success"):
119
+ raise RuntimeError(f"Failed to setup team: {setup_res.get('message')}")
120
+ except Exception as e:
121
+ await self.chatroom.delete_chat(chat_id)
122
+ raise e
123
+ self.sessions[chat_id] = {"chat_id": chat_id, "model": model}
124
+ logger.info(f"Started agent session {chat_id} with model={model}")
125
+ return chat_id
126
+
127
+ async def chat(self, session_id: str, content: str, attachment_ids: Optional[List[str]] = None, process_step_message=None) -> Dict[str, Any]:
128
+ """Send a message to an active session."""
129
+ if session_id not in self.sessions:
130
+ pass
131
+ if attachment_ids is None:
132
+ attachment_ids = []
133
+ if attachment_ids and session_id in self.sessions:
134
+ uploads = self.sessions[session_id].get("uploads", {})
135
+ attached_files = []
136
+ for aid in attachment_ids:
137
+ if aid in uploads:
138
+ attached_files.append(uploads[aid])
139
+ if attached_files:
140
+ file_context = "\n\nUser uploaded the following files:\n"
141
+ for f in attached_files:
142
+ file_context += f"- {f['filename']} (Path: {f['path']})\n"
143
+ content += file_context
144
+ chat_message = self._build_chat_message(content)
145
+ response = await self.chatroom.chat(session_id, chat_message, process_step_message=process_step_message)
146
+ return response
147
+
148
+ async def rewind_and_rerun(self, session_id: str, user_turn_index: int, content: str, attachment_ids: Optional[List[str]] = None, process_step_message=None) -> Dict[str, Any]:
149
+ """Rewind chat history to a user turn and rerun from edited content."""
150
+ if session_id not in self.sessions:
151
+ pass
152
+ if attachment_ids is None:
153
+ attachment_ids = []
154
+ try:
155
+ await self.chatroom.stop_chat(session_id)
156
+ except Exception:
157
+ pass
158
+ memory = self.chatroom.memory_manager.get_memory(session_id)
159
+ user_turns = memory.get_user_turns()
160
+ if user_turn_index < 0 or user_turn_index >= len(user_turns):
161
+ raise ValueError("Invalid user_turn_index")
162
+ target_index = user_turns[user_turn_index][0]
163
+ memory.revert_to_message(target_index)
164
+ try:
165
+ await memory.flush()
166
+ except Exception:
167
+ pass
168
+ return await self.chat(session_id, content, attachment_ids, process_step_message=process_step_message)
169
+
170
+ async def delete_upload(self, session_id: str, attachment_id: str) -> bool:
171
+ """Delete an uploaded file by attachment id."""
172
+ uploads = self.sessions.get(session_id, {}).get("uploads", {})
173
+ meta = uploads.pop(attachment_id, None)
174
+ if not meta:
175
+ return False
176
+ file_path = Path(meta.get("path", ""))
177
+ if file_path.exists():
178
+ file_path.unlink()
179
+ return True
180
+
181
+ async def stop_session(self, session_id: str) -> bool:
182
+ """Stop and clean up a session."""
183
+ if session_id in self.sessions:
184
+ del self.sessions[session_id]
185
+ try:
186
+ await self.chatroom.stop_chat(session_id)
187
+ except Exception:
188
+ pass
189
+ result = await self.chatroom.delete_chat(session_id)
190
+ return result.get("success", False)
191
+
192
+
193
+ _INSTANCE: Optional[AgentSessionManager] = None
194
+
195
+
196
+ def get_session_manager(workspace_root: Path | None = None, output_dir: Path | None = None, memory_dir: Path | None = None) -> AgentSessionManager:
197
+ """Get or create the global session manager instance."""
198
+ global _INSTANCE
199
+ if _INSTANCE is None:
200
+ _INSTANCE = AgentSessionManager(workspace_root=workspace_root)
201
+ if output_dir:
202
+ _INSTANCE.output_dir = output_dir
203
+ _INSTANCE.output_dir.mkdir(parents=True, exist_ok=True)
204
+ if memory_dir:
205
+ _INSTANCE.pantheon_memory_dir = memory_dir
206
+ _INSTANCE.chatroom = ChatRoom(memory_dir=str(_INSTANCE.pantheon_memory_dir), workspace_path=str(_INSTANCE.workspace_root))
207
+ return _INSTANCE
@@ -0,0 +1,55 @@
1
+ name: # protocol name
2
+
3
+ description: Protocol for designing probe type xxx from species xxx
4
+
5
+ genome: # genome name
6
+
7
+ extracts:
8
+ target_region:
9
+ source: # genome | exon | CDS | UTR
10
+ length: # int
11
+ overlap: # int
12
+
13
+ targets:
14
+ - xxx1: # chr:start-end | gene_name | name: string
15
+ - xxx2: # chr:start-end | gene_name | name: string
16
+
17
+ encoding:
18
+ xxx1:
19
+ BC1: xxx
20
+ BC2: xxx
21
+ xxx2:
22
+ BC1: xxx
23
+ BC2: xxx
24
+
25
+ probes:
26
+
27
+ # NOTE:
28
+ # - `attributes` and `post_process` can be omitted.
29
+ # - If they are missing or empty, the CLI will auto-generate defaults
30
+ # based on DNA (source=genome) vs RNA (source=exon/CDS/UTR) mode.
31
+ attributes:
32
+
33
+
34
+ post_process:
35
+ filters:
36
+
37
+ # clean
38
+ avoid_otp:
39
+
40
+ # equal_space
41
+ equal_space:
42
+
43
+ # remove overlap
44
+ remove_overlap:
45
+ location_interval: 0
46
+
47
+ sorts:
48
+
49
+
50
+
51
+ summary:
52
+ report_name: dna_report # template name # class: dna_report or rna_report
53
+ attributes:
54
+
55
+