Skip to content

Commit c79c587

Browse files
committed
fix: adapt backend PyTC integration to v2 CLI and preset layout
1 parent ef251d8 commit c79c587

3 files changed

Lines changed: 319 additions & 229 deletions

File tree

server_api/main.py

Lines changed: 78 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -90,33 +90,88 @@ def health():
9090

9191

9292
BASE_DIR = pathlib.Path(__file__).resolve().parent.parent
93-
PYTC_CONFIG_ROOT = BASE_DIR / "pytorch_connectomics" / "configs"
94-
PYTC_BUILD_FILE = (
95-
BASE_DIR / "pytorch_connectomics" / "connectomics" / "model" / "build.py"
93+
PYTC_ROOT = BASE_DIR / "pytorch_connectomics"
94+
PYTC_CONFIG_ROOTS = (
95+
PYTC_ROOT / "tutorials",
96+
PYTC_ROOT / "configs",
9697
)
98+
PYTC_CONFIG_SUFFIXES = (".yaml", ".yml")
99+
100+
101+
def _iter_existing_config_roots():
102+
for root in PYTC_CONFIG_ROOTS:
103+
if root.exists() and root.is_dir():
104+
yield root
97105

98106

99107
def _list_pytc_configs() -> List[str]:
100-
if not PYTC_CONFIG_ROOT.exists():
101-
return []
102-
return sorted(
103-
[
104-
str(path.relative_to(PYTC_CONFIG_ROOT)).replace("\\", "/")
105-
for path in PYTC_CONFIG_ROOT.rglob("*.yaml")
106-
]
108+
configs = []
109+
for root in _iter_existing_config_roots():
110+
for suffix in PYTC_CONFIG_SUFFIXES:
111+
for path in root.rglob(f"*{suffix}"):
112+
configs.append(str(path.relative_to(PYTC_ROOT)).replace("\\", "/"))
113+
return sorted(set(configs))
114+
115+
116+
def _is_relative_to(path: pathlib.Path, root: pathlib.Path) -> bool:
117+
try:
118+
path.relative_to(root)
119+
return True
120+
except ValueError:
121+
return False
122+
123+
124+
def _is_valid_config_path(path: pathlib.Path) -> bool:
125+
if not path.is_file():
126+
return False
127+
if path.suffix.lower() not in PYTC_CONFIG_SUFFIXES:
128+
return False
129+
if not _is_relative_to(path, PYTC_ROOT.resolve()):
130+
return False
131+
return any(
132+
_is_relative_to(path, root.resolve()) for root in _iter_existing_config_roots()
107133
)
108134

109135

136+
def _resolve_requested_config(path: str) -> Optional[pathlib.Path]:
137+
if not path:
138+
return None
139+
140+
normalized = path.replace("\\", "/").strip()
141+
if not normalized or normalized.startswith("/"):
142+
return None
143+
if ".." in pathlib.PurePosixPath(normalized).parts:
144+
return None
145+
146+
candidates = [(PYTC_ROOT / normalized).resolve()]
147+
for root in _iter_existing_config_roots():
148+
candidates.append((root / normalized).resolve())
149+
150+
for candidate in candidates:
151+
if _is_valid_config_path(candidate):
152+
return candidate
153+
return None
154+
155+
110156
def _read_model_architectures() -> List[str]:
111-
if not PYTC_BUILD_FILE.exists():
112-
return []
113-
text = PYTC_BUILD_FILE.read_text(encoding="utf-8", errors="ignore")
114-
match = re.search(r"MODEL_MAP\s*=\s*{(.*?)}", text, re.S)
115-
if not match:
116-
return []
117-
block = match.group(1)
118-
keys = re.findall(r"'([^']+)'\s*:", block)
119-
return sorted(set(keys))
157+
# Prefer runtime registry from the installed connectomics package.
158+
try:
159+
from connectomics.models.arch import list_architectures
160+
161+
architectures = list_architectures()
162+
if architectures:
163+
return sorted(set(architectures))
164+
except Exception:
165+
pass
166+
167+
# Fallback: parse decorator registrations from source files.
168+
pattern = re.compile(r"""@register_architecture\(\s*['"]([^'"]+)['"]\s*\)""")
169+
architectures = []
170+
arch_root = PYTC_ROOT / "connectomics" / "models" / "arch"
171+
for py_file in arch_root.rglob("*.py"):
172+
text = py_file.read_text(encoding="utf-8", errors="ignore")
173+
architectures.extend(pattern.findall(text))
174+
return sorted(set(architectures))
120175

121176

122177
@app.get("/pytc/configs")
@@ -131,17 +186,12 @@ def list_pytc_configs():
131186
def get_pytc_config(path: str):
132187
if not path:
133188
raise HTTPException(status_code=400, detail="Config path is required.")
134-
if ".." in path or path.startswith("/"):
135-
raise HTTPException(status_code=400, detail="Invalid config path.")
136-
requested = (PYTC_CONFIG_ROOT / path).resolve()
137-
if not str(requested).startswith(str(PYTC_CONFIG_ROOT.resolve())):
138-
raise HTTPException(status_code=400, detail="Invalid config path.")
139-
if not requested.is_file():
189+
requested = _resolve_requested_config(path)
190+
if requested is None:
140191
raise HTTPException(status_code=404, detail="Config not found.")
141-
if requested.suffix.lower() != ".yaml":
142-
raise HTTPException(status_code=400, detail="Config must be a YAML file.")
143192
content = requested.read_text(encoding="utf-8", errors="ignore")
144-
return {"path": path, "content": content}
193+
canonical_path = str(requested.relative_to(PYTC_ROOT)).replace("\\", "/")
194+
return {"path": canonical_path, "content": content}
145195

146196

147197
@app.get("/pytc/architectures")

server_pytc/main.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,7 @@ async def get_tensorboard_url():
9898
async def start_model_inference(req: Request):
9999
req = await req.json()
100100
print("start model inference")
101-
# log_dir = req["log_dir"]
102-
start_inference(req)
101+
return start_inference(req)
103102

104103

105104
@app.post("/stop_model_inference")

0 commit comments

Comments
 (0)