๊ธฐ๋ฅ ์์ธ ๋ณด๊ณ
10. ์น UI ๊ธฐ๋ฅ ๋ถ์ ๋ณด๊ณ ์ (FastAPI + React + Mol*)
๋ฃจํธ:
AgenticAI4SCIENCE_pyrosetta_track/repos/ai4sci-kaeri๋์:backend/(FastAPI),frontend/src/(React 19 + Vite + Mol*) ๊ท์น: ๋ชจ๋ ์ฌ์ค์file_path:line์ธ์ฉ. ๋ฏธํ์ธ ํญ๋ชฉ์ "๋ฏธ๊ฒ์ฆ" ๋ช ์. ์ฝ๊ธฐ์ ์ฉ ๋ถ์.
โ ๋์ ์๋ฆฌ
A. ๋ฐฑ์๋ ์ํคํ ์ฒ (FastAPI)
- ์ฑ ํฉํ ๋ฆฌ:
create_app()์ด ๋ชจ๋ ๋ผ์ฐํฐ๋ฅผ/apiํ๋ฆฌํฝ์ค๋ก ๋ฑ๋ก (backend/main.py:45-135). ๋ชจ๋ ๋ ๋ฒจapp = create_app()์ผ๋กuvicorn backend.main:app๊ธฐ๋ (backend/main.py:139,backend/main.py:7). - CORS:
http://localhost:5173(Vite dev),http://localhost:8787(API) ๋ง ํ์ฉ, ๋ฉ์๋GET/POST/PUT/DELETE/OPTIONS(backend/main.py:65-70). - ํ์ค ์๋ฌ ์๋ต:
{"error","detail","status_code"}ํ์์ผ๋ก ์ ์ญ ์์ธ(500)ยทHTTPExceptionยทValidationError(422) ํธ๋ค๋ฌ ์ ์ (backend/main.py:73-108). - ํฌํธ: ๊ธฐ๋ณธ
8787,API_PORTenv ๋ก override (backend/state.py:56).
B. StatusEmitter โ JSON ํ์ผ โ ํด๋ง (ํต์ฌ ๋ฐ์ดํฐ ํ๋ฆ)
์ด ์์คํ ์ ๋ผ์ด๋ธ ๋ชจ๋ํฐ๋ง์ SSE/WebSocket ํธ์๊ฐ ์๋๋ผ "ํ์ผ + ํด๋ง" ์ผ๋ก ๋์ํ๋ค.
-
์ฐ๊ธฐ(Writer): ํ์ดํ๋ผ์ธ(
scripts/run_pyrosetta_flow.py)์ดStatusEmitter์ธ์คํด์ค๋ฅผ ํตํด ์ํ๋ฅผ ๊ฐฑ์ . - ์ํ ํ์ผ ๊ธฐ๋ณธ ๊ฒฝ๋ก/tmp/pipeline_local_status.json(backend/status_emitter.py:30-34, envPIPELINE_STATUS_FILE). - ์ํ dict ์คํค๋ง:steps(13๊ฐ ํ์ดํ๋ผ์ธ ๋จ๊ณ),agents(5๊ฐ),rosetta_substeps(7๊ฐ),timeline,candidates,qc_gates,convergence,best_candidate,molecules,visualization_images,completed๋ฑ (backend/status_emitter.py:91-114). - ๋ชจ๋ ๊ฐฑ์ ๋ฉ์๋(update_step,update_agent,set_candidates๋ฑ)๊ฐ ๋งค๋ฒflush()ํธ์ถ (backend/status_emitter.py:117-134). - flush() ๋ ์์์ ์ฐ๊ธฐ:fcntl.flock๋ฐฐํ ๋ฝ + temp write +rename์ผ๋ก ๋์ ์์ปค์ torn-write ๋ฐฉ์ง (backend/status_emitter.py:117-133, C5 fix ์ฃผ์). -
์ฝ๊ธฐ(Reader):
GET /api/status(backend/routers/status.py:157-164). -read_status()๊ฐ ํ์ผ mtime ์บ์๋ก I/O ์ ์ฝํ๋server_time์ ์บ์ hit ์์๋ ๋งค๋ฒ ๊ฐฑ์ (backend/state.py:127-153). -is_active_runํ์ ํ๋ ์ฃผ์ :(not completed) AND (steps ๋๋ candidates ์กด์ฌ)(backend/state.py:101-124). - ํ์ผ ๋ถ์ฌ ์{"error":"no_status_file","connected":False}๋ฐํ (backend/state.py:136-137). - on-the-fly enrichment: candidates ์ 6๊ฐ ์ฝ๋ฆฌ/๊ตฌ์กฐ ํ๋(instability_index, gravy, net_charge_ph74, selectivity_margin, fwkt_contact, chelator_site_available)๋ฅผ ์๋ต ์์ ์ ๋จธ์ง (backend/routers/status.py:60-164). โคโฅ์ sequence-only ํด๋ฆฌ์คํฑ์ด๋ผ HEURISTIC ์ผ๋ก ๋ช ์ (backend/routers/status.py:73-76,138-150). -
ํด๋ง(Frontend):
usePipelineStatus(2000)ํ ์ด 2์ด ๊ฐ๊ฒฉ์ผ๋กfetch('/api/status')(frontend/src/hooks/usePipelineStatus.ts:262-296,329-343). -AbortController๋ก ์ด์ ์์ฒญ ์ทจ์ (usePipelineStatus.ts:263-265). -no_status_file/connected:false๊ฐ์ง ์INITIAL_STATE๋ก ๋ฆฌ์ โ Live ๋์๋ณด๋๋ก ์ค์ธ ๋ฐฉ์ง (usePipelineStatus.ts:274-283). - snake_case BE โ camelCase FE ๋งคํ์parseStatusData()(usePipelineStatus.ts:170-236). - ๋์์/api/runsํด๋ง์ผ๋ก ์์นด์ด๋ธ ๋ชฉ๋ก ๊ฐฑ์ ,switchRun()์ผ๋ก ๊ณผ๊ฑฐ run ์กฐํ(/api/runs/{run_id}) (usePipelineStatus.ts:252-260,298-326).
ํด๋ง ์ค๋ณต(์ค๊ณ์ ์๋):
usePipelineStatus๊ฐ/api/status(2s),useExperiment๊ฐ/api/experiment/status(3s) ๋ฅผ ๋ณ๋ ํด๋ง. ์ ์๋ ๋์๋ณด๋ ์๊ฐํ, ํ์๋ start/stop ์ ์ด์ฉ (usePipelineStatus.ts:238-245,frontend/src/hooks/useExperiment.ts:74-93).
C. ์คํ ์ ์ด (Experiment Control)
POST /api/experiment/run์ดsubprocess.Popen์ผ๋กscripts/run_pyrosetta_flow.py๋ฅผbio-toolsconda python ์ผ๋ก ๊ธฐ๋ (backend/routers/experiment.py:205-293,145-155).- ํ
ํ๋ฆฟ PDB ๊ธฐ๋ณธ๊ฐ
SSTR2_SST14_complex_boltz_1.pdb(experiment.py:220-222). - ์์ ์ฆ์
phase:"initializing"์ต์ status ํ์ผ ๊ธฐ๋ก (experiment.py:84-98,273). - watchdog ์ค๋ ๋: ๊ธฐ๋ณธ 1์๊ฐ(
EXPERIMENT_MAX_RUNTIME) ์ด๊ณผ ์SIGTERMโSIGKILL(experiment.py:30-31,112-142). - ์ ์/์ค์ง/ํ์์์ ์ข
๋ฃ ์
_auto_archive()๋กarchives/{run_id}_dashboard.json์ ์ฅ (experiment.py:34-70,296-317). GET /api/experiment/models: vLLM(port 8000/v1/models) ์ฐ์ ์กฐํ, ์คํจ ์ ollama ํด๋ฐฑ, ์ต์ข ํด๋ฐฑ["qwen3-32b"](experiment.py:163-189).- FE
useExperiment๊ฐ config/models ๋ก๋ + start/stop + ์ํ ํด๋ง ๋ด๋น (useExperiment.ts:53-141).
D. ์์ด์ ํธ ๋ก๊ทธ โ REST + SSE
์ฌ๊ธฐ์๋ง ์ง์ง SSE(Server-Sent Events) ๊ฐ ์กด์ฌํ๋ค.
GET /api/agents/{run_id}/log:runs_local/{run_id}/experiment_log.jsonl๋ฑ์์ ๋์ ๋ก๊ทธ 1ํ ๋ฐํ (backend/routers/agents.py:176-181,60-75).GET /api/agents/{run_id}/stream:text/event-stream์๋ต,_tail_log_file์ด jsonl ํ์ผ์ 0.25s ๊ฐ๊ฒฉ tail ํ๋ฉฐ ์ ๋ผ์ธ์ ํ๋ก ํธ์, 10s ๋ฌด์ด๋ฒคํธ ์: pingkeepalive (agents.py:193-225,133-173).- path traversal ๋ฐฉ์ด:
..,/,\ํ ํฐ ๊ฑฐ๋ถ (agents.py:68-74,111-114). - ์์ด์ ํธ 6์ข
๊ณ ์ ์ ์(planner/builder/qcranker/diversity/critic/reporter) (
agents.py:40-47). ๋จ, FEAgentMonitor๊ฐ ์๋นํ๋ ์์ด์ ํธ๋statusJSON ์ 5์ข (status_emitter.py:58-64)์ด๋ฉฐ ์ด SSE ๋ก๊ทธ 6์ข ๊ณผ ๋ณ๊ฐ โ ๋ ์์ด์ ํธ ํํ์ด ๋ถ๋ฆฌ๋์ด ์์(๋ฏธ๊ฒ์ฆ: FE ๊ฐ/api/agents/.../stream์ ์ค์ ๊ตฌ๋ ํ๋ ์ปดํฌ๋ํธ๋ ๋ณธ ๋ถ์ ํ์ผ ๋ฒ์์์ ํ์ธ ์ ํจ).
E. Mol* 3D ๊ตฌ์กฐ ๋ทฐ์ด
- PDB ์๋น:
GET /api/structures/{rel_path}๊ฐruns/,runs/pyrosetta_flow/,runs_local/,data/4๊ฐ ๋ฒ ์ด์ค์์.pdb๋ง ์๋น (backend/routers/static.py:20-39). ๋ณด์:relative_to๊ฒฝ๋ก ํ์ถ ์ฐจ๋จ + symlink ๊ฑฐ๋ถ (static.py:24-31). ์ด๋ฏธ์ง๋/api/images/{rel_path}(static.py:42-60). - Mol* ๋ก๋ฉ:
MoleculeViewer์ปดํฌ๋ํธ๊ฐPluginContext์ด๊ธฐํ โmountAsyncโbuilders.data.download(Asset.Url(pdbUrl))โparseTrajectory(..., PdbProvider)โ preset ์ ์ฉ (frontend/src/components/MoleculeViewer.tsx:50-91). - 4๊ฐ์ง view mode(Complex/Cartoon/Ball&Stick/Surface)๋ฅผ Mol* preset ์ผ๋ก ๋งคํ (
MoleculeViewer.tsx:240-265). - dark theme ๋ฐฐ๊ฒฝ(
0x0f172a), ์นด๋ฉ๋ผ ๋ฆฌ์ , fullscreen, ESC ๋ซ๊ธฐ, focus trap ์ ๊ทผ์ฑ (MoleculeViewer.tsx:57,112-129,41). pdbUrl์ candidatepdb_path(=source) ์์/api/structures/...๋ก ๋ณํ (SelectivityExplorerPage.tsx:423-447,CandidatePage.tsx:81). ์ ๋ ํฐ๋นํฐ receptor ๊ตฌ์กฐ๋ ๋ณ๋GET /api/selectivity/structure/{name}๋ก CIF/PDB ์๋น (backend/routers/selectivity.py:96-115).
F. ์ ๋ ํฐ๋นํฐ ๋ผ์ฐํฐ
GET /api/selectivity/receptors: SSTR1/2/3/4/5 CIF ํ์ผ ์กด์ฌ ์ฌ๋ถยทํฌ๊ธฐ ๋ฐํ, 0๊ฐ ๋ก๋ ์ error ๋ก๊ทธ (selectivity.py:39-68).POST /api/selectivity/upload: receptor ๊ตฌ์กฐ ํ์ผ ์ ๋ก๋ (selectivity.py:71-93).- ์
๋ ํฐ๋นํฐ ๋ถ์ job ์ in-memory
_JOBSdict ์ ๋ณด๊ด,status.py๊ฐ ์๋ฃ job ์ margin ์ candidate ์ ๋จธ์ง (selectivity.py:120,status.py:39-57).
โก ์ํฅ (Impact)
- ๋จ์ผ SSOT ํจํด์ ์ฅ์ : ์ปดํจํธ ๋ ์ด์ด(ํ์ดํ๋ผ์ธ ์์ปค)์ ํํ ๋ ์ด์ด(์น)๊ฐ JSON ํ์ผ 1๊ฐ๋ก๋ง ๊ฒฐํฉ โ ๋ฐฑ์๋๊ฐ ์ฃฝ์ด๋ ํ์ผ์ด ๋จ๊ณ , ํ์ดํ๋ผ์ธ์ด ์ฃฝ์ด๋ ๋ง์ง๋ง ์ํ๊ฐ ๋ณด์กด๋จ. flock ์์ ์ฐ๊ธฐ๋ก ๋์์ฑ ์์ (
status_emitter.py:117-133,state.py:71-90). - ํด๋ง ๋น์ฉ:
/api/status(2s) +/api/runs(2s) +/api/experiment/status(3s) ๊ฐ ํด๋ผ์ด์ธํธ๋ง๋ค ์์ ๋ฐ์. mtime ์บ์๋ก ํ์ผ ํ์ฑ์ ์ ์ฝ๋๋ HTTP ์๋ณตยทenrichment ๊ณ์ฐ(_enrich_candidates)์ ๋งค ํด๋ง๋ง๋ค ์ํ (status.py:60-164) โ ํ๋ณด ์ ์ฆ๊ฐ ์ ์๋ต ์ง์ฐ ๊ฐ๋ฅ(๋ฏธ๊ฒ์ฆ: ๋ถํ ์ธก์ ์์). - ์์นด์ด๋ธ ๊ธฐ๋ฐ ํ๊ณ : ์๋ฃ run ์ด ์๋์ผ๋ก
archives/{run_id}_dashboard.json+ PDB ์ฌ๋ณธ์ผ๋ก ๋ณด์กด๋์ด ๊ณผ๊ฑฐ run ์ฌํ ๊ฐ๋ฅ (status_emitter.py:397-425,experiment.py:34-70). - ๋ฌดํ ๋ฐ๊ตด ์์ง๊ณผ์ ๋จ์ (์ค๋): ์๋ โฃ ์ฐธ์กฐ โ ๋ณธ UI ๋
/api/experiment/*(์ ํ run) ๋ง ์ ์ดํ๋ฉฐ, ๋ฌดํ ๋ฐ๊ตด ์์ง(pyrosetta_flow/continuous.py)์ ์ถ๋ ฅ์ ์ ํ ํ์ยท์ ์ดํ์ง ๋ชปํจ.
โข ๊ด๋ จ Action Item
๊ทผ๊ฑฐ:
MEMORY.mdโsstr2-ui-goal๋ฉ๋ชจ๋ฆฌ("์ฐจ๊ธฐ GOAL(๋ฏธ์ฐฉ์): ๋ฌดํ์์ง์ฉ ํ ์น ๋ชจ๋ํฐ๋งยท์ ์ด UI"), UI goal ํ๋กฌํํธ_workspace/GOAL_UI_2026-06-10.md.
- [UI ์ฐ๋, ์ต์ฐ์ ] ๋ฌดํ์์ง โ ์น ์ฐ๊ฒฐ: ๋ฐฑ์๋์
/api/discovery/*๋ผ์ฐํฐ๋ฅผ ์ ์คํ์ฌruns/pyrosetta_flow/discovery_status.json/global_selectivity_leaderboard.json์ ์๋นํ๊ณ , FE ํด๋ง ํ (useDiscovery๋ฑ)์ ์ถ๊ฐ. (ํ์ฌ 0๊ฐ โ โฃ ์ฐธ์กฐ) - [UI ์ฐ๋] ๋ฌดํ์์ง ์ ์ด:
_workspace/discovery_control.json์ ํตํ ์์/์ ์ง/ํ๋ผ๋ฏธํฐ ์กฐ์ ์๋ํฌ์ธํธ. (ํ์ฌ ํ์ผ๋ง ์กด์ฌ, ์น ์ ์ด ๊ฒฝ๋ก ์์) - [UI goal Phase] ์ ๋ฉด ์ฌ๋์์ธ: UI goal(L3)์ด "๋ฌดํ์์ง ์ฐ๊ฒฐ + ์ ๋ฉด ์ฌ๋์์ธ"์ ํจ๊ป ์๊ตฌ โ ํ์ฌ 13๋จ๊ณ ์ ํ ํ์ดํ๋ผ์ธ ์๊ฐํ(
status_emitter.py:42-56)๊ฐ ๋ฌดํ epoch ๋ชจ๋ธ๊ณผ ์ ๋ณด๊ตฌ์กฐ๊ฐ ๋ง์ง ์์. - [๋ฆฌํฉํ ๋ง ํ๋ณด] ์์ด์ ํธ ํํ ์ด์ํ ํด์:
statusJSON 5-์์ด์ ํธ(status_emitter.py:58-64) vs/api/agentsSSE 6-์์ด์ ํธ(agents.py:40-47) ํตํฉ ๊ฒํ . - [์ฑ๋ฅ] ํด๋ง โ SSE ํตํฉ ๊ฒํ : ๊ธฐ์กด SSE ์ธํ๋ผ(
agents.py:193-225)๋ฅผ status ์ ๋ฐ์ผ๋ก ํ์ฅํ๋ฉด 3์ค ํด๋ง ์ ๊ฑฐ ๊ฐ๋ฅ(usePipelineStatus.ts:238-245์ TODO ์ฃผ์๊ณผ ์ผ์น).
โฃ ์์ฑ๋ ์ถ์ : ์ฝ 70% (์ ํ ํ์ดํ๋ผ์ธ UI ๊ธฐ์ค ํ๋ถ, ๋ฌดํ์์ง ์ฐ๋ 0%)
๊ทผ๊ฑฐ โ ํ๋ถํ ๋ถ๋ถ (์์ฑ)
- ๋ฐฑ์๋ ๋ผ์ฐํฐ 20+๊ฐ ๋ฑ๋ก(
main.py:113-133): status/experiment/agents/selectivity/static ์ธ admet/analysis/validation/cluster/stability/benchmark/wetlab/flexpepdock/binding_pocket/strategies/silo_a ๋ฑ. - ํ๋ก ํธ 15๊ฐ ํ์ด์ง ๋ผ์ฐํธ + 40+ ์ปดํฌ๋ํธ (
frontend/src/App.tsx:277-364,frontend/src/components/๋ค์). - ํต์ฌ ๋ชจ๋ํฐ๋ง ๊ธฐ๋ฅ ์๋: StatusEmitter ์์ ์ฐ๊ธฐ, mtime ์บ์ ํด๋ง, candidate 6-field enrichment, ์์นด์ด๋ธ ์๋ ๋ณด์กด, Mol* 4-mode 3D ๋ทฐ์ด, ์์ด์ ํธ SSE ์คํธ๋ฆผ, ์ ๋ ํฐ๋นํฐ receptor ๊ด๋ฆฌ, ์คํ start/stop/watchdog.
- ํ
์คํธ ์์ฐ ์กด์ฌ:
backend/tests/18๊ฐ,frontend/src/**/__tests__/10+๊ฐ.
๊ทผ๊ฑฐ โ ๋ฏธ์์ฑ / ๋จ์ (ํต์ฌ ๊ฐ์ )
- ๋ฌดํ์์ง ๋ฏธ์ฐ๊ฒฐ (๊ฒ์ฆ ์๋ฃ):
grep -rn "discovery" backend/โ 0๊ฑด.grep -rn "discovery" frontend/src/โ 0๊ฑด.grep -rn "api/discovery"(py/ts/tsx) โ 0๊ฑด.- ์ฆ
backend/main.py๋ผ์ฐํฐ ๋ชฉ๋ก(main.py:113-133)์/api/discovery/*์์. - ๋ฐ๋ฉด ๋ฌดํ์์ง ์ถ๋ ฅ ํ์ผ์ ์ค์ฌ:
runs/pyrosetta_flow/discovery_status.json(21KB, ํค:running, epochs_done, elapsed_seconds, global_best_delta_margin, passing_count, diversity_level, top, last_epoch, history),runs/pyrosetta_flow/global_selectivity_leaderboard.json(25KB), ์ ์ด ํ์ผ_workspace/discovery_control.jsonโ ๋ชจ๋ ์ด๋ค ๋ผ์ฐํฐ๋ ์ฝ์ง ์์. - ์์ง ๋ณธ์ฒด
pyrosetta_flow/continuous.py๋ ์กด์ฌ(์น๊ณผ ๋ ๋ฆฝ ์คํ). - ๊ฒฐ๋ก : ๋ฌดํ ๋ฐ๊ตด ์์ง์ ์น UI ์ ์์ ํ ๋ถ๋ฆฌ๋์ด ์์ผ๋ฉฐ, ํ์ฌ ์น์์ ๋ฌดํ์์ง ์ํ๋ฅผ ๋ณด๊ฑฐ๋ ์ ์ดํ ๋ฐฉ๋ฒ์ด ์๋ค.
MEMORY.md๊ฐ UI goal ์ "์ฐจ๊ธฐ GOAL(๋ฏธ์ฐฉ์)"๋ก ๋ช ์.
โค ํ์ ์ ๊ฐ์น (๊ณผํ ๋ชจ๋ํฐ๋ง UX)
- ๊ณ์ฐ๊ณผํ ์ฌํ์ฑ UX: run โ ์๋ ์์นด์ด๋ธ(JSON+PDB ์ฌ๋ณธ) โ
switchRun์ผ๋ก ๊ณผ๊ฑฐ run ์ฌ์กฐํ(usePipelineStatus.ts:298-326,status_emitter.py:397-425)๋ in-silico ์คํ์ ์ฌํ์ฑยท๊ฐ์ฌ ์ถ์ ์ UI ๋ ๋ฒจ์์ ๊ตฌํํ ์ฌ๋ก. - ํด๋ฆฌ์คํฑ ์ ์ง์ฑ: enrichment 6ํ๋ ์ค sequence-only ์ถ์ ์น(fwkt_contact, chelator_site)๋ฅผ HEURISTIC ์ผ๋ก ๋ผ๋ฒจ๋ง(
status.py:73-76)ํ๊ณ , FE ๊ฐ fail reason ์ ๊ณผํ์ ํ๊ตญ์ด ์ค๋ช ์ผ๋ก ๋ณํ(CandidateTable.tsx:13-54) โ "๊ณ์ฐ ๋ถ๊ฐ๋ฅ์ ๊ฐ๋ฅํ ์ฒํ์ง ์๋๋ค"๋ ํ๋ก์ ํธ ์์น(CLAUDE.md VR-cycle-09)์ UX ๊ตฌํ. - ๋ค์ค ํํ ๋ถ์ ์๊ฐํ: Mol* 4-mode(complex/cartoon/ball&stick/surface) preset(
MoleculeViewer.tsx:240-265)์ผ๋ก SSTR2-ํฉํ์ด๋ ๋ณตํฉ์ฒด์ ๊ฒฐํฉ ์ธํฐํ์ด์ค ๊ฒํ ์ง์. - ์์ด์ ํธ ์์ฌ๊ฒฐ์ ํฌ๋ช
์ฑ: planner hypothesis / critic proposed_changes(oldโnew+rationale) / reporter summary ๋ฅผ ํจ๋๋ก ๋
ธ์ถ(
AgentMonitor.tsx:119-191) โ agentic ๊ณผํ ํ์ดํ๋ผ์ธ์ ์์ฌ๊ฒฐ์ ๊ณผ์ ๊ฐ์ํ.
โฅ ์ฌ์ฉ๋ฒ (๊ธฐ๋)
๋ฏธ๊ฒ์ฆ: ์๋ ๋ช ๋ น์
backend/main.py:7,frontend/package.json:7,vite.config.ts:14-21๊ธฐ๋ฐ ์ถ๋ก . ์ค์ ์คํ ๋ก๊ทธ๋ ๋ณธ ๋ถ์ ๋ฒ์ ๋ฐ.
๋ฐฑ์๋ (ํฌํธ 8787):
# ๋ ํฌ ๋ฃจํธ(ai4sci-kaeri)์์
uvicorn backend.main:app --host 0.0.0.0 --port 8787 --reload # backend/main.py:7
# ๋๋
python -m backend.main # backend/main.py:142-144
- ์ํ ํ์ผ ๊ฒฝ๋ก override:
PIPELINE_STATUS_FILE=/tmp/pipeline_local_status.json(state.py:40-45). - ๋ชจ๋ธ ๋ชฉ๋ก์ vLLM
localhost:8000๊ฐ ๋ ์์ด์ผ ์ ์(์์ผ๋ฉด ollamaโdefault ํด๋ฐฑ) (experiment.py:163-189).
ํ๋ก ํธ์๋ (ํฌํธ 5173, Vite dev):
cd frontend
npm install
npm run dev # vite โ package.json:7
- Vite dev ์๋ฒ๊ฐ
/api์์ฒญ์http://127.0.0.1:8787๋ก ํ๋ก์ (vite.config.ts:14-21). - ํ๋ก๋์
:
npm run buildโnpm run preview(package.json:8,10).
๋ธ๋ผ์ฐ์ : http://localhost:5173 ์ ์ โ / ๊ฐ /console ๋ก ๋ฆฌ๋ค์ด๋ ํธ (App.tsx:278).
โฆ ํ์ ์ด์ (Why)
- agentic in-silico ์คํฌ๋ฆฌ๋์ ์์ญ~์๋ฐฑ epoch/iterationยท๋ค์ ํ๋ณด๋ฅผ ์์ฑ โ CLI ๋ก๊ทธ๋ง์ผ๋ก๋ ์๋ ด ์ถ์ธยทํ๋ณด ๋ญํนยท๊ฒ์ดํธ ํต๊ณผ์จยท3D ๊ฒฐํฉ ๊ตฌ์กฐ๋ฅผ ์ค์๊ฐ ํ์ ๋ถ๊ฐ. ์น UI ๋ ์ด๋ฅผ ๋จ์ผ ํ๋ฉด ๋ชจ๋ํฐ๋ง์ผ๋ก ์์ถ.
- ํ์ดํ๋ผ์ธ(์ปดํจํธ,
bio-toolsconda)๊ณผ ํํ(์น)์ JSON ํ์ผ๋ก ๋์ปคํ๋ง(status_emitter.py:30-34)ํจ์ผ๋ก์จ, GPU ์์ปค ํ๊ฒฝ๊ณผ ๋ฌด๊ดํ๊ฒ ์ด๋์๋ ๋ธ๋ผ์ฐ์ ๋ก ๊ด์ฐฐยท์ ์ด(start/stop) ๊ฐ๋ฅ. - ๊ทธ๋ฌ๋ โฃ์์ ํ์ธํ๋ฏ ๋ฌดํ ๋ฐ๊ตด ์์ง(ํ ํ๋ก์ ํธ์ ํต์ฌ ์ฐ์ถ๋ฌผ)์ ์์ง ์ด ๋ชจ๋ํฐ๋ง ์ฒด๊ณ ๋ฐ์ ์์ด, ๋ฌดํ์์ง์ ์น์ ์ฐ๊ฒฐํ๋ ๊ฒ์ด UI goal ์ ์กด์ฌ ์ด์ ์ด์ ๋ค์ ์ฐ์ ์์๋ค.
๊ฒ์ฆ ์ธ์ฉ ๋ชฉ๋ก (file_path:line)
๋ฐฑ์๋
- backend/main.py:7 โ uvicorn ๊ธฐ๋ ์ปค๋งจ๋ (docstring)
- backend/main.py:45-135 โ create_app, ๋ผ์ฐํฐ ๋ฑ๋ก 20+๊ฐ
- backend/main.py:65-70 โ CORS (5173/8787)
- backend/main.py:73-108 โ ํ์ค ์๋ฌ ํธ๋ค๋ฌ
- backend/main.py:113-133 โ ๋ผ์ฐํฐ ๋ชฉ๋ก(discovery ๋ถ์ฌ ๊ทผ๊ฑฐ)
- backend/main.py:139,142-144 โ module-level app, main
- backend/status_emitter.py:30-34 โ STATUS_FILE ๊ฒฝ๋ก(/tmp/pipeline_local_status.json)
- backend/status_emitter.py:42-56 โ DEFAULT_STEPS 13๊ฐ(์ ํ ํ์ดํ๋ผ์ธ)
- backend/status_emitter.py:58-64 โ DEFAULT_AGENTS 5๊ฐ
- backend/status_emitter.py:91-114 โ ์ํ dict ์คํค๋ง
- backend/status_emitter.py:117-133 โ flush() flock ์์ ์ฐ๊ธฐ
- backend/status_emitter.py:199-224 โ set_candidates ๋ณํฉยทddG ์ฌ๋ญํฌ
- backend/status_emitter.py:397-425 โ _save_archive(PDB ์ฌ๋ณธ ํฌํจ)
- backend/state.py:40-49 โ STATUS_FILE/ARCHIVE_DIR env
- backend/state.py:56 โ PORT 8787
- backend/state.py:71-90 โ atomic_write_json
- backend/state.py:101-124 โ _with_runtime_fields(is_active_run/server_time)
- backend/state.py:127-153 โ read_status mtime ์บ์
- backend/routers/status.py:39-57 โ selectivity margin lookup
- backend/routers/status.py:60-164 โ _enrich_candidates 6ํ๋(ํด๋ฆฌ์คํฑ ๋ผ๋ฒจ 73-76)
- backend/routers/status.py:157-164 โ GET /api/status
- backend/routers/status.py:187-225 โ /api/runs, /api/runs/{run_id}
- backend/routers/experiment.py:30-31,112-142 โ watchdog
- backend/routers/experiment.py:34-70,296-317 โ _auto_archive
- backend/routers/experiment.py:84-98,273 โ initializing status
- backend/routers/experiment.py:163-189 โ /experiment/models(vLLMโollamaโdefault)
- backend/routers/experiment.py:205-293 โ POST /experiment/run(subprocess)
- backend/routers/agents.py:40-47 โ SSE ์์ด์ ํธ 6์ข
- backend/routers/agents.py:60-75,111-114 โ ๋ก๊ทธ ๊ฒฝ๋กยทpath traversal ๋ฐฉ์ด
- backend/routers/agents.py:133-173 โ _tail_log_file
- backend/routers/agents.py:176-181 โ GET /agents/{run_id}/log
- backend/routers/agents.py:193-225 โ GET /agents/{run_id}/stream (SSE)
- backend/routers/static.py:20-39 โ GET /api/structures/{rel_path} (.pdb)
- backend/routers/static.py:42-60 โ GET /api/images/{rel_path}
- backend/routers/selectivity.py:39-68 โ /selectivity/receptors
- backend/routers/selectivity.py:71-93 โ /selectivity/upload
- backend/routers/selectivity.py:96-115 โ /selectivity/structure/{name} (CIF/PDB)
- backend/routers/selectivity.py:120 โ _JOBS in-memory
ํ๋ก ํธ์๋
- frontend/src/hooks/usePipelineStatus.ts:170-236 โ parseStatusData(snakeโcamel)
- frontend/src/hooks/usePipelineStatus.ts:238-245 โ ํด๋ง ์ค๋ณต NOTE
- frontend/src/hooks/usePipelineStatus.ts:262-296 โ fetchLiveStatus(/api/status)
- frontend/src/hooks/usePipelineStatus.ts:274-283 โ no_status_file ๋ฆฌ์
- frontend/src/hooks/usePipelineStatus.ts:298-326 โ switchRun(/api/runs/{id})
- frontend/src/hooks/usePipelineStatus.ts:329-343 โ 2s setInterval ํด๋ง
- frontend/src/hooks/useExperiment.ts:53-141 โ config/models/start/stop/3s ํด๋ง
- frontend/src/components/MoleculeViewer.tsx:50-91 โ Mol* init/PDB ๋ก๋
- frontend/src/components/MoleculeViewer.tsx:240-265 โ view mode preset ๋งคํ
- frontend/src/components/AgentMonitor.tsx:119-191 โ ReportPanel(plan/critic/reporter)
- frontend/src/components/CandidateTable.tsx:13-54 โ humanizeFailReason(๊ณผํ์ ์ค๋ช
)
- frontend/src/components/ConvergenceGraph.tsx:56-60 โ ๋น ๋ฐ์ดํฐ ๊ฐ๋
- frontend/src/pages/SelectivityExplorerPage.tsx:423-447 โ pdb_pathโ/api/structures URL
- frontend/src/App.tsx:277-364 โ 15๊ฐ ๋ผ์ฐํธ
- frontend/vite.config.ts:14-21 โ /api โ 127.0.0.1:8787 ํ๋ก์
- frontend/package.json:7,8,10 โ dev/build/preview ์คํฌ๋ฆฝํธ
- frontend/package.json:20 โ molstar ^5.6.1
๋ฌดํ์์ง ๋ฏธ์ฐ๊ฒฐ ๊ทผ๊ฑฐ (grep/ls ๊ฒ์ฆ)
- grep -rn "discovery" backend/ โ 0๊ฑด
- grep -rn "discovery" frontend/src/ โ 0๊ฑด
- grep -rn "api/discovery" (py/ts/tsx) โ 0๊ฑด
- runs/pyrosetta_flow/discovery_status.json (21KB, ์ค์ฌ, ๋ฏธ์๋น)
- runs/pyrosetta_flow/global_selectivity_leaderboard.json (25KB, ์ค์ฌ, ๋ฏธ์๋น)
- _workspace/discovery_control.json (์ค์ฌ, ์น ์ ์ด ๊ฒฝ๋ก ์์)
- pyrosetta_flow/continuous.py (์์ง ๋ณธ์ฒด, ์น ๋
๋ฆฝ ์คํ)
- MEMORY.md sstr2-ui-goal โ UI goal "๋ฏธ์ฐฉ์" ๋ช
์