From a6c66995e3911c8d5f7dbfeb97517a7e3076c808 Mon Sep 17 00:00:00 2001 From: yuyr Date: Mon, 5 Jan 2026 17:04:10 +0800 Subject: [PATCH] =?UTF-8?q?V3.6=20=E5=A2=9E=E5=8A=A0W&B=20server=EF=BC=8C?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E9=85=8D=E7=BD=AE=E4=BB=BB=E5=8A=A1=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E9=83=BD=E5=BC=80wandb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- specs/mvp/remain_problems.md | 27 + specs/mvp/sw_arch.excalidraw | 6496 +++++++++++------ specs/mvp/v3.5/v3.5_changes.md | 67 + specs/mvp/v3.6/README.md | 9 + .../mvp/v3.6/Snipaste_2026-01-05_10-56-34.png | Bin 0 -> 72243 bytes specs/mvp/v3.6/requirements.md | 3 + specs/mvp/v3.6/v3.6_design.md | 280 + specs/mvp/v3.6/v3.6_dev_plan.md | 194 + specs/mvp/v3.6/v3.6_progress.md | 42 + specs/mvp/v3.6/v3.6_summary.md | 126 + specs/mvp/v3.6/wandb.md | 70 + src/mvp/configs/dev.yaml | 10 + src/mvp/configs/dev_v30.yaml | 9 +- src/mvp/docker-compose.yaml | 21 + src/mvp/py/argus/ray/ray_job_tool.py | 44 +- src/mvp/py/argus/service/app.py | 20 +- src/mvp/py/argus/service/config.py | 34 + src/mvp/py/argus/service/scheduler.py | 92 +- src/mvp/py/argus/service/ui.py | 139 +- src/mvp/py/tests/test_app.py | 31 + src/mvp/py/tests/test_scheduler.py | 276 +- src/mvp/py/tests/test_service_config.py | 48 + src/mvp/py/tests/test_ui.py | 30 + src/mvp/scripts/00_prereq_check.sh | 4 + src/mvp/scripts/60_start_api.sh | 3 + 25 files changed, 5852 insertions(+), 2223 deletions(-) create mode 100644 specs/mvp/remain_problems.md create mode 100644 specs/mvp/v3.5/v3.5_changes.md create mode 100644 specs/mvp/v3.6/README.md create mode 100644 specs/mvp/v3.6/Snipaste_2026-01-05_10-56-34.png create mode 100644 specs/mvp/v3.6/requirements.md create mode 100644 specs/mvp/v3.6/v3.6_design.md create mode 100644 specs/mvp/v3.6/v3.6_dev_plan.md create mode 100644 specs/mvp/v3.6/v3.6_progress.md create mode 100644 specs/mvp/v3.6/v3.6_summary.md create mode 100644 specs/mvp/v3.6/wandb.md diff --git a/specs/mvp/remain_problems.md b/specs/mvp/remain_problems.md new file mode 100644 index 0000000..44510aa --- /dev/null +++ b/specs/mvp/remain_problems.md @@ -0,0 +1,27 @@ + +# v3.6 +wandb 映射目录是/vol 固定问题: +查过官方文档/公开资料后结论是:wandb/local(W&B local server 容器)没有提供“把服务端持 + 久化根目录从 /vol 改成别的路径”的官方环境变量/启动参数。官方用法一直是假设你把持久化卷 + 挂到容器内的固定路径 /vol(例如 -v :/vol)。(github.com (https://github.com/ + wandb/server)) + + 需要注意区分两类“目录”: + + - 服务端(wandb/local 容器):持久化目录是容器内固定 /vol,用于保存实例元数据、账号/初 + 始化信息等(license 也可以用 env 配,但数据目录仍是 /vol)。(github.com (https:// + github.com/wandb/server)) + - 训练侧(wandb Python SDK / VERL 任务):WANDB_DIR、WANDB_DATA_DIR 等环境变量只影响“客 + 户端本地生成文件/缓存”,不改变服务端容器的数据落盘路径。(docs.wandb.ai (https:// + docs.wandb.ai/platform/hosting/env-vars)) + + 所以如果你现在的约束是“只能挂 ../../shared:/private,不能再额外挂 ../../shared/common/ + wandb:/vol”,要把 W&B 服务端数据落到 shared 下面,现实可行的路子是: + + - 自定义 W&B 容器 entrypoint(或 wrapper)在启动前做一次 ln -s /private/common/wandb / + vol(或 bind-mount 到 /vol),让服务仍然写 /vol,但实际落到 /private/common/wandb。 + 这属于“容器层改造”,不是 W&B 官方参数。 + + 如果你允许 compose 再加一条 volume,那最简单仍是:保留 ../../shared:/private,再额外 + 加 ../../shared/common/wandb:/vol(服务端就无需任何改造)。 + diff --git a/specs/mvp/sw_arch.excalidraw b/specs/mvp/sw_arch.excalidraw index 3504791..c026691 100644 --- a/specs/mvp/sw_arch.excalidraw +++ b/specs/mvp/sw_arch.excalidraw @@ -2398,8 +2398,8 @@ "type": "text", "x": 154.90147538843206, "y": 1942.0901138836757, - "width": 781.6194458007812, - "height": 50, + "width": 562.5595703125, + "height": 25, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "transparent", @@ -2413,20 +2413,20 @@ "index": "b02", "roundness": null, "seed": 1960456801, - "version": 746, - "versionNonce": 54710305, + "version": 748, + "versionNonce": 1287253379, "isDeleted": false, "boundElements": [], - "updated": 1766548609119, + "updated": 1767579887038, "link": null, "locked": false, - "text": "v3.5 Advanced task (customized param, reward function, verl version/code, etc) \n & resubmit from last checkpoint & ib network supporting & model serving", + "text": "v3.5 Advanced task (customized param, reward function)", "fontSize": 20, "fontFamily": 5, "textAlign": "left", "verticalAlign": "top", "containerId": null, - "originalText": "v3.5 Advanced task (customized param, reward function, verl version/code, etc) \n & resubmit from last checkpoint & ib network supporting & model serving", + "originalText": "v3.5 Advanced task (customized param, reward function)", "autoResize": true, "lineHeight": 1.25 }, @@ -2789,7 +2789,7 @@ "height": 55.000000000000014, "angle": 0, "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", + "backgroundColor": "#ffc9c9", "fillStyle": "solid", "strokeWidth": 2, "strokeStyle": "solid", @@ -2800,8 +2800,8 @@ "index": "b0D", "roundness": null, "seed": 1968738561, - "version": 894, - "versionNonce": 2004715169, + "version": 895, + "versionNonce": 425863533, "isDeleted": false, "boundElements": [ { @@ -2809,7 +2809,7 @@ "id": "eON0BC4wPRqMcWvu_Thde" } ], - "updated": 1766542978938, + "updated": 1767579953447, "link": null, "locked": false }, @@ -2890,9 +2890,9 @@ { "id": "D_32yPttd9giCH42fMVwj", "type": "text", - "x": 605.9793228386666, + "x": 611.8193268059517, "y": 2307.3761703818636, - "width": 146.57989501953125, + "width": 134.89988708496094, "height": 75, "angle": 0, "strokeColor": "#1e1e1e", @@ -2907,20 +2907,20 @@ "index": "b0G", "roundness": null, "seed": 726423471, - "version": 723, - "versionNonce": 754280481, + "version": 740, + "versionNonce": 1995148323, "isDeleted": false, "boundElements": [], - "updated": 1766548439582, + "updated": 1767579913843, "link": null, "locked": false, - "text": "Advanced\nVerlTaskSpec \n(code, ckpt, ib)", + "text": "Advanced\nVerlTaskSpec \n(python cmd)", "fontSize": 20, "fontFamily": 5, "textAlign": "center", "verticalAlign": "middle", "containerId": "D6Mbsmb7s6kKZ3ue7w8KF", - "originalText": "Advanced VerlTaskSpec \n(code, ckpt, ib)", + "originalText": "Advanced VerlTaskSpec \n(python cmd)", "autoResize": true, "lineHeight": 1.25 }, @@ -3331,10 +3331,10 @@ { "id": "hfyGYREhwfOiJ0NmS7nWJ", "type": "rectangle", - "x": 145.70805709406656, - "y": 2619.319767745958, + "x": 184.78887213940638, + "y": 4494.097308130087, "width": 647.3203086953981, - "height": 526.3260713720084, + "height": 641.2700333334831, "angle": 0, "strokeColor": "#1e1e1e", "backgroundColor": "transparent", @@ -3350,19 +3350,19 @@ "type": 3 }, "seed": 1547009185, - "version": 833, - "versionNonce": 123286017, + "version": 1063, + "versionNonce": 2017335811, "isDeleted": false, "boundElements": [], - "updated": 1766545303838, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "kcnXooRmgv12d40l7DX1I", "type": "rectangle", - "x": 351.3615872250116, - "y": 2799.9864242400986, + "x": 390.4424022703514, + "y": 4674.763964624228, "width": 156.66668701171875, "height": 85, "angle": 0, @@ -3378,8 +3378,8 @@ "index": "b0T", "roundness": null, "seed": 142557313, - "version": 581, - "versionNonce": 1369576865, + "version": 781, + "versionNonce": 1578230179, "isDeleted": false, "boundElements": [ { @@ -3395,15 +3395,15 @@ "type": "arrow" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "-kTPtQM7ZjAJg_O72uvP0", "type": "text", - "x": 372.3049542294061, - "y": 2817.4864242400986, + "x": 411.38576927474594, + "y": 4692.263964624228, "width": 114.77995300292969, "height": 50, "angle": 0, @@ -3419,11 +3419,11 @@ "index": "b0U", "roundness": null, "seed": 849709153, - "version": 571, - "versionNonce": 921745793, + "version": 771, + "versionNonce": 1363916099, "isDeleted": false, "boundElements": [], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false, "text": "ray job tool\n(ray client)", @@ -3439,8 +3439,8 @@ { "id": "syy4HE220r-q_RMUUSsel", "type": "rectangle", - "x": 596.3615872250116, - "y": 2799.4864242400986, + "x": 635.4424022703514, + "y": 4674.263964624228, "width": 156.66668701171875, "height": 85, "angle": 0, @@ -3456,8 +3456,8 @@ "index": "b0V", "roundness": null, "seed": 1954010177, - "version": 718, - "versionNonce": 61717793, + "version": 918, + "versionNonce": 1838907523, "isDeleted": false, "boundElements": [ { @@ -3469,15 +3469,15 @@ "type": "arrow" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "CA5CuK3pSeEixp1YwKENQ", "type": "text", - "x": 607.2449871883905, - "y": 2816.9864242400986, + "x": 646.3258022337303, + "y": 4691.763964624228, "width": 134.89988708496094, "height": 50, "angle": 0, @@ -3493,11 +3493,11 @@ "index": "b0W", "roundness": null, "seed": 30778401, - "version": 722, - "versionNonce": 136016129, + "version": 922, + "versionNonce": 1073641507, "isDeleted": false, "boundElements": [], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false, "text": "VerlTaskSpec \nyaml", @@ -3513,8 +3513,8 @@ { "id": "BfGeVEKVLbnnxWatX-CuX", "type": "arrow", - "x": 597.694930730871, - "y": 2837.9864242400986, + "x": 636.7757457762108, + "y": 4712.763964624228, "width": 90.33331298828125, "height": 4, "angle": 0, @@ -3532,11 +3532,11 @@ "type": 2 }, "seed": 1722710017, - "version": 1460, - "versionNonce": 1568273583, + "version": 2068, + "versionNonce": 243331309, "isDeleted": false, "boundElements": [], - "updated": 1766545238799, + "updated": 1767580422053, "link": null, "locked": false, "points": [ @@ -3552,12 +3552,12 @@ "lastCommittedPoint": null, "startBinding": { "elementId": "syy4HE220r-q_RMUUSsel", - "focus": 0.16118805172016915, + "focus": 0.1611880517201622, "gap": 1.333343505859375 }, "endBinding": { "elementId": "kcnXooRmgv12d40l7DX1I", - "focus": 0.06393742185082188, + "focus": 0.06393742185083832, "gap": 1 }, "startArrowhead": null, @@ -3567,8 +3567,8 @@ { "id": "XwaSYITVD0m2YORWBiATj", "type": "text", - "x": 324.49455587811565, - "y": 2586.6531112518173, + "x": 363.57537092345547, + "y": 4461.430651635947, "width": 480.5596923828125, "height": 25, "angle": 0, @@ -3584,11 +3584,11 @@ "index": "b0Y", "roundness": null, "seed": 84004833, - "version": 652, - "versionNonce": 1828651841, + "version": 852, + "versionNonce": 399615747, "isDeleted": false, "boundElements": [], - "updated": 1766546120049, + "updated": 1767580421422, "link": null, "locked": false, "text": "v4.0 observability (prom, grafana, alert, ELK, etc)", @@ -3604,8 +3604,8 @@ { "id": "hO23c7cUKniC0ShSkbYHY", "type": "rectangle", - "x": 537.1855352981856, - "y": 2642.153019699083, + "x": 576.2663503435255, + "y": 4516.930560083212, "width": 210.1763265850289, "height": 55.000000000000014, "angle": 0, @@ -3621,8 +3621,8 @@ "index": "b0Z", "roundness": null, "seed": 1390357441, - "version": 870, - "versionNonce": 1752066177, + "version": 1070, + "versionNonce": 1034669731, "isDeleted": false, "boundElements": [ { @@ -3630,15 +3630,15 @@ "id": "WvC3BnzbmedjQgc_rcGOG" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "WvC3BnzbmedjQgc_rcGOG", "type": "text", - "x": 589.4237382635517, - "y": 2657.153019699083, + "x": 628.5045533088916, + "y": 4531.930560083212, "width": 105.69992065429688, "height": 25, "angle": 0, @@ -3654,11 +3654,11 @@ "index": "b0a", "roundness": null, "seed": 1483082657, - "version": 909, - "versionNonce": 443527265, + "version": 1109, + "versionNonce": 1629487683, "isDeleted": false, "boundElements": [], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false, "text": "API server", @@ -3674,8 +3674,8 @@ { "id": "QTuaauYBrKurb9Uym-vJ0", "type": "rectangle", - "x": 351.19493073087096, - "y": 2713.153019699083, + "x": 390.2757457762108, + "y": 4587.930560083212, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -3691,8 +3691,8 @@ "index": "b0b", "roundness": null, "seed": 1841688449, - "version": 1100, - "versionNonce": 817691713, + "version": 1300, + "versionNonce": 1527562723, "isDeleted": false, "boundElements": [ { @@ -3700,15 +3700,15 @@ "id": "wds-TjwVp7NzSUYdwGMzH" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "wds-TjwVp7NzSUYdwGMzH", "type": "text", - "x": 367.39679926156066, - "y": 2718.153019699083, + "x": 406.4776143069005, + "y": 4592.930560083212, "width": 122.23991394042969, "height": 50, "angle": 0, @@ -3724,11 +3724,11 @@ "index": "b0c", "roundness": null, "seed": 960386913, - "version": 1159, - "versionNonce": 1261816865, + "version": 1359, + "versionNonce": 1686146435, "isDeleted": false, "boundElements": [], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false, "text": "task\nmanagement ", @@ -3744,8 +3744,8 @@ { "id": "h-InDEDqJ6h9usuQPtO1A", "type": "rectangle", - "x": 535.3024693165407, - "y": 2713.7273280117965, + "x": 574.3832843618806, + "y": 4588.504868395925, "width": 207.59447102997498, "height": 60, "angle": 0, @@ -3761,8 +3761,8 @@ "index": "b0d", "roundness": null, "seed": 1830732609, - "version": 1191, - "versionNonce": 1640603649, + "version": 1391, + "versionNonce": 280618275, "isDeleted": false, "boundElements": [ { @@ -3770,15 +3770,15 @@ "id": "B1uYGWk1HK8JtizGFaaGC" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "B1uYGWk1HK8JtizGFaaGC", "type": "text", - "x": 551.5697747167821, - "y": 2718.7273280117965, + "x": 590.650589762122, + "y": 4593.504868395925, "width": 175.0598602294922, "height": 50, "angle": 0, @@ -3794,11 +3794,11 @@ "index": "b0e", "roundness": null, "seed": 1170649889, - "version": 1281, - "versionNonce": 1115158497, + "version": 1481, + "versionNonce": 120015043, "isDeleted": false, "boundElements": [], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false, "text": "node management\n(ssh, ray cluster) ", @@ -3814,8 +3814,8 @@ { "id": "XS4hxHD7hkjqqdIPoHNyM", "type": "rectangle", - "x": 872.6485818367171, - "y": 2851.7015263882736, + "x": 911.7293968820569, + "y": 4726.479066772403, "width": 163.1357446724801, "height": 106.45708709635272, "angle": 0, @@ -3831,8 +3831,8 @@ "index": "b0f", "roundness": null, "seed": 1202038529, - "version": 516, - "versionNonce": 390245327, + "version": 716, + "versionNonce": 2103916643, "isDeleted": false, "boundElements": [ { @@ -3840,15 +3840,15 @@ "id": "ilnIrrhwIwdl5yMP0J_-o" } ], - "updated": 1766545564008, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "ilnIrrhwIwdl5yMP0J_-o", "type": "text", - "x": 903.5664984234454, - "y": 2879.93006993645, + "x": 942.6473134687852, + "y": 4754.707610320578, "width": 101.29991149902344, "height": 50, "angle": 0, @@ -3864,11 +3864,11 @@ "index": "b0g", "roundness": null, "seed": 1027284705, - "version": 579, - "versionNonce": 291944867, + "version": 779, + "versionNonce": 1900557315, "isDeleted": false, "boundElements": [], - "updated": 1766652384276, + "updated": 1767580421422, "link": null, "locked": false, "text": "ray worker\nnode", @@ -3884,8 +3884,8 @@ { "id": "qYCyZgffVShRdxYkZZEQC", "type": "rectangle", - "x": 873.4503851219463, - "y": 2658.663290367263, + "x": 912.5312001672861, + "y": 4533.440830751391, "width": 163.1357446724801, "height": 85, "angle": 0, @@ -3901,8 +3901,8 @@ "index": "b0h", "roundness": null, "seed": 1964415681, - "version": 537, - "versionNonce": 235739009, + "version": 737, + "versionNonce": 583580579, "isDeleted": false, "boundElements": [ { @@ -3910,15 +3910,15 @@ "id": "7cAjOCsL26YdZ9huXHSvG" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "7cAjOCsL26YdZ9huXHSvG", "type": "text", - "x": 904.3683017086746, - "y": 2676.163290367263, + "x": 943.4491167540144, + "y": 4550.940830751391, "width": 101.29991149902344, "height": 50, "angle": 0, @@ -3934,11 +3934,11 @@ "index": "b0i", "roundness": null, "seed": 1071094433, - "version": 603, - "versionNonce": 1021609667, + "version": 803, + "versionNonce": 1310150467, "isDeleted": false, "boundElements": [], - "updated": 1766652382577, + "updated": 1767580421422, "link": null, "locked": false, "text": "ray worker\nnode", @@ -3954,8 +3954,8 @@ { "id": "Ov7Ctp7PvJITFoQy7Yb4E", "type": "rectangle", - "x": 354.00044019695827, - "y": 2639.9401270333396, + "x": 393.0812552422981, + "y": 4514.717667417468, "width": 147.4799234681481, "height": 55.000000000000014, "angle": 0, @@ -3971,8 +3971,8 @@ "index": "b0j", "roundness": null, "seed": 774741633, - "version": 988, - "versionNonce": 867953473, + "version": 1188, + "versionNonce": 630353635, "isDeleted": false, "boundElements": [ { @@ -3980,15 +3980,15 @@ "id": "hkWZM79d15pmmlJnpM5GC" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "hkWZM79d15pmmlJnpM5GC", "type": "text", - "x": 396.5104290916768, - "y": 2654.9401270333396, + "x": 435.59124413701664, + "y": 4529.717667417468, "width": 62.45994567871094, "height": 25, "angle": 0, @@ -4004,11 +4004,11 @@ "index": "b0k", "roundness": null, "seed": 249046625, - "version": 1038, - "versionNonce": 243194657, + "version": 1238, + "versionNonce": 197696131, "isDeleted": false, "boundElements": [], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false, "text": "WebUI", @@ -4024,8 +4024,8 @@ { "id": "hJ1qbW1rf0n6-WrDss2Ro", "type": "rectangle", - "x": 597.672221957931, - "y": 2915.035517049753, + "x": 636.7530370032708, + "y": 4789.813057433881, "width": 156.66668701171875, "height": 85, "angle": 0, @@ -4041,8 +4041,8 @@ "index": "b0l", "roundness": null, "seed": 749122113, - "version": 770, - "versionNonce": 1067673025, + "version": 970, + "versionNonce": 225989155, "isDeleted": false, "boundElements": [ { @@ -4054,15 +4054,15 @@ "type": "arrow" } ], - "updated": 1766545307006, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "w68ybhw27soUMyLsMu-vA", "type": "text", - "x": 602.7156179540248, - "y": 2920.035517049753, + "x": 641.7964329993646, + "y": 4794.813057433881, "width": 146.57989501953125, "height": 75, "angle": 0, @@ -4078,11 +4078,11 @@ "index": "b0m", "roundness": null, "seed": 1282982433, - "version": 784, - "versionNonce": 2124728655, + "version": 984, + "versionNonce": 462427587, "isDeleted": false, "boundElements": [], - "updated": 1766548451240, + "updated": 1767580421422, "link": null, "locked": false, "text": "Advanced\nVerlTaskSpec \n(code, ckpt, ib)", @@ -4098,8 +4098,8 @@ { "id": "OJ2YvI_4pyTp3tvnog5HM", "type": "arrow", - "x": 597.2084151610145, - "y": 2959.4005095639527, + "x": 636.2892302063543, + "y": 4834.178049948081, "width": 80.66214281697523, "height": 84.85848269127973, "angle": 0, @@ -4117,11 +4117,11 @@ "type": 2 }, "seed": 1799214593, - "version": 307, - "versionNonce": 389666031, + "version": 831, + "versionNonce": 74337101, "isDeleted": false, "boundElements": [], - "updated": 1766545238799, + "updated": 1767580422053, "link": null, "locked": false, "points": [ @@ -4137,14 +4137,10 @@ "lastCommittedPoint": null, "startBinding": { "elementId": "hJ1qbW1rf0n6-WrDss2Ro", - "focus": -0.6785882314731592, + "focus": -0.6785882314731579, "gap": 1 }, - "endBinding": { - "elementId": "kcnXooRmgv12d40l7DX1I", - "focus": -0.4748599898404937, - "gap": 8.517998107308927 - }, + "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "elbowed": false @@ -4152,8 +4148,8 @@ { "id": "ozgVxBsj7snNecNunrp11", "type": "rectangle", - "x": 168.79639165083321, - "y": 2713.0580483353347, + "x": 207.87720669617303, + "y": 4587.835588719463, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -4169,8 +4165,8 @@ "index": "b0o", "roundness": null, "seed": 1005940193, - "version": 1162, - "versionNonce": 919389825, + "version": 1362, + "versionNonce": 443066531, "isDeleted": false, "boundElements": [ { @@ -4178,15 +4174,15 @@ "id": "i3wz2t7mbs8b39oPir_Gf" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "i3wz2t7mbs8b39oPir_Gf", "type": "text", - "x": 184.99826018152297, - "y": 2718.0580483353347, + "x": 224.07907522686276, + "y": 4592.835588719463, "width": 122.23991394042969, "height": 50, "angle": 0, @@ -4202,11 +4198,11 @@ "index": "b0p", "roundness": null, "seed": 1705970113, - "version": 1230, - "versionNonce": 156388961, + "version": 1430, + "versionNonce": 87137347, "isDeleted": false, "boundElements": [], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false, "text": "user\nmanagement ", @@ -4222,8 +4218,8 @@ { "id": "-6-EuXJhEm2mWx5ISTwFk", "type": "rectangle", - "x": 174.85778806975028, - "y": 2906.6428799880864, + "x": 213.93860311509013, + "y": 4781.420420372215, "width": 154.6436510018092, "height": 85, "angle": 0, @@ -4239,8 +4235,8 @@ "index": "b0q", "roundness": null, "seed": 25831841, - "version": 1394, - "versionNonce": 630970945, + "version": 1594, + "versionNonce": 2111511523, "isDeleted": false, "boundElements": [ { @@ -4248,15 +4244,15 @@ "id": "GabFf-T0sdW_Oq8DrJ2I3" } ], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "GabFf-T0sdW_Oq8DrJ2I3", "type": "text", - "x": 195.05965660044004, - "y": 2911.6428799880864, + "x": 234.14047164577983, + "y": 4786.420420372215, "width": 114.23991394042969, "height": 75, "angle": 0, @@ -4272,11 +4268,11 @@ "index": "b0r", "roundness": null, "seed": 1488342401, - "version": 1476, - "versionNonce": 1015605793, + "version": 1676, + "versionNonce": 276014979, "isDeleted": false, "boundElements": [], - "updated": 1766545237730, + "updated": 1767580421422, "link": null, "locked": false, "text": "data\nmanagement\nSFTPGo ", @@ -4292,8 +4288,8 @@ { "id": "OkOW_h1bpRwbXvHRE98qO", "type": "rectangle", - "x": 182.78414498765986, - "y": 3050.6268158384833, + "x": 229.40224214693444, + "y": 5023.3894549921515, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -4309,8 +4305,8 @@ "index": "b0s", "roundness": null, "seed": 905236833, - "version": 1230, - "versionNonce": 1563580431, + "version": 1466, + "versionNonce": 1127857955, "isDeleted": false, "boundElements": [ { @@ -4318,15 +4314,15 @@ "id": "dr1KmTO-yRI3g5vju74ZK" } ], - "updated": 1766545349965, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "dr1KmTO-yRI3g5vju74ZK", "type": "text", - "x": 205.13600741483398, - "y": 3068.1268158384833, + "x": 251.75410457410862, + "y": 5040.8894549921515, "width": 109.93992614746094, "height": 25, "angle": 0, @@ -4342,11 +4338,11 @@ "index": "b0t", "roundness": null, "seed": 1089862977, - "version": 1310, - "versionNonce": 656720751, + "version": 1546, + "versionNonce": 1352393411, "isDeleted": false, "boundElements": [], - "updated": 1766545320817, + "updated": 1767580421422, "link": null, "locked": false, "text": "prometheus", @@ -4362,8 +4358,8 @@ { "id": "PwNRlpy3F_V5w-3sfAYAQ", "type": "rectangle", - "x": 380.47625420486156, - "y": 3048.761908698168, + "x": 427.0943513641362, + "y": 5021.524547851837, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -4379,8 +4375,8 @@ "index": "b0u", "roundness": null, "seed": 1448322511, - "version": 1274, - "versionNonce": 130692193, + "version": 1510, + "versionNonce": 557355619, "isDeleted": false, "boundElements": [ { @@ -4388,15 +4384,15 @@ "id": "kWS_uGtXbrBusArKpVdGc" } ], - "updated": 1766545349965, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "kWS_uGtXbrBusArKpVdGc", "type": "text", - "x": 421.01811907344194, - "y": 3066.261908698168, + "x": 467.63621623271655, + "y": 5039.024547851837, "width": 73.55992126464844, "height": 25, "angle": 0, @@ -4412,11 +4408,11 @@ "index": "b0v", "roundness": null, "seed": 1134302191, - "version": 1363, - "versionNonce": 2067682977, + "version": 1599, + "versionNonce": 98455043, "isDeleted": false, "boundElements": [], - "updated": 1766545330509, + "updated": 1767580421422, "link": null, "locked": false, "text": "grafana", @@ -4429,1392 +4425,11 @@ "autoResize": true, "lineHeight": 1.25 }, - { - "id": "Kh_k-i4xvHEjEJb8_WQGe", - "type": "rectangle", - "x": 144.7756888977941, - "y": 3318.702674993213, - "width": 647.3203086953981, - "height": 641.0568098019863, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b0y", - "roundness": { - "type": 3 - }, - "seed": 1341573071, - "version": 944, - "versionNonce": 857091553, - "isDeleted": false, - "boundElements": [], - "updated": 1766545521716, - "link": null, - "locked": false - }, - { - "id": "_W--_CG68zeuj3T62ETPL", - "type": "rectangle", - "x": 350.4292190287391, - "y": 3499.3693314873535, - "width": 156.66668701171875, - "height": 85, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b0z", - "roundness": null, - "seed": 545977839, - "version": 661, - "versionNonce": 1136311439, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "ta4lpkZtQQzWOHjvV7FUZ" - }, - { - "id": "EHQ4kp8hzyDTaf37J9ZtT", - "type": "arrow" - }, - { - "id": "bkx6LQav2SUWAUBD5ulLa", - "type": "arrow" - } - ], - "updated": 1766545389705, - "link": null, - "locked": false - }, - { - "id": "ta4lpkZtQQzWOHjvV7FUZ", - "type": "text", - "x": 371.37258603313364, - "y": 3516.8693314873535, - "width": 114.77995300292969, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b10", - "roundness": null, - "seed": 1186226191, - "version": 651, - "versionNonce": 158487215, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "ray job tool\n(ray client)", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "_W--_CG68zeuj3T62ETPL", - "originalText": "ray job tool\n(ray client)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "ff2ONOPa1y8jPmi5h653t", - "type": "rectangle", - "x": 595.4292190287391, - "y": 3498.8693314873535, - "width": 156.66668701171875, - "height": 85, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b11", - "roundness": null, - "seed": 1416332847, - "version": 800, - "versionNonce": 250191489, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "Sp8TUJYedU9gJGAI-AxfH" - }, - { - "id": "EHQ4kp8hzyDTaf37J9ZtT", - "type": "arrow" - } - ], - "updated": 1766546214569, - "link": null, - "locked": false - }, - { - "id": "Sp8TUJYedU9gJGAI-AxfH", - "type": "text", - "x": 606.312618992118, - "y": 3516.3693314873535, - "width": 134.89988708496094, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b12", - "roundness": null, - "seed": 144238671, - "version": 802, - "versionNonce": 2080899887, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "VerlTaskSpec \nyaml", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "ff2ONOPa1y8jPmi5h653t", - "originalText": "VerlTaskSpec \nyaml", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "EHQ4kp8hzyDTaf37J9ZtT", - "type": "arrow", - "x": 596.7625625345985, - "y": 3537.3693314873535, - "width": 90.33331298828125, - "height": 4, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b13", - "roundness": { - "type": 2 - }, - "seed": 823461487, - "version": 1696, - "versionNonce": 1657674593, - "isDeleted": false, - "boundElements": [], - "updated": 1766545390322, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -90.33331298828125, - 4 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "ff2ONOPa1y8jPmi5h653t", - "focus": 0.1611880517201695, - "gap": 1.333343505859375 - }, - "endBinding": { - "elementId": "_W--_CG68zeuj3T62ETPL", - "focus": 0.06393742185081497, - "gap": 1 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false - }, - { - "id": "L4pG2mhd5e-0Pd1Pi3Rtf", - "type": "text", - "x": 323.0959182098221, - "y": 3285.1034795550304, - "width": 248.73980712890625, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b14", - "roundness": null, - "seed": 1286103183, - "version": 773, - "versionNonce": 1540512385, - "isDeleted": false, - "boundElements": [], - "updated": 1766546254231, - "link": null, - "locked": false, - "text": "v4.5 observerbility (wanb)", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "left", - "verticalAlign": "top", - "containerId": null, - "originalText": "v4.5 observerbility (wanb)", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "-nJJwacshyGBduJHI6hXt", - "type": "rectangle", - "x": 536.2531671019132, - "y": 3341.535926946338, - "width": 210.1763265850289, - "height": 55.000000000000014, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b15", - "roundness": null, - "seed": 1829467823, - "version": 950, - "versionNonce": 1744925615, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "jDNKsROHTO2oMIWzI3OsA" - } - ], - "updated": 1766545389705, - "link": null, - "locked": false - }, - { - "id": "jDNKsROHTO2oMIWzI3OsA", - "type": "text", - "x": 588.4913700672791, - "y": 3356.535926946338, - "width": 105.69992065429688, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b16", - "roundness": null, - "seed": 1491519695, - "version": 990, - "versionNonce": 1578444239, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "API server", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "-nJJwacshyGBduJHI6hXt", - "originalText": "API server", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "up-AtpppOYdyyCNXNlwgr", - "type": "rectangle", - "x": 350.2625625345985, - "y": 3412.535926946338, - "width": 154.6436510018092, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b17", - "roundness": null, - "seed": 1842534127, - "version": 1180, - "versionNonce": 1462604783, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "jXoEPKCyuLdoL1i4yThdA" - } - ], - "updated": 1766545389705, - "link": null, - "locked": false - }, - { - "id": "jXoEPKCyuLdoL1i4yThdA", - "type": "text", - "x": 366.4644310652883, - "y": 3417.535926946338, - "width": 122.23991394042969, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b18", - "roundness": null, - "seed": 1113071887, - "version": 1240, - "versionNonce": 190004751, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "task\nmanagement ", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "up-AtpppOYdyyCNXNlwgr", - "originalText": "task management ", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "7zXodXAZupZo9ox9VxG2c", - "type": "rectangle", - "x": 534.3701011202683, - "y": 3413.1102352590515, - "width": 207.59447102997498, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b19", - "roundness": null, - "seed": 133113647, - "version": 1271, - "versionNonce": 494838831, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "ghLH4zRCaGh2ml_wjH-TO" - } - ], - "updated": 1766545389705, - "link": null, - "locked": false - }, - { - "id": "ghLH4zRCaGh2ml_wjH-TO", - "type": "text", - "x": 550.6374065205097, - "y": 3418.1102352590515, - "width": 175.0598602294922, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1A", - "roundness": null, - "seed": 755001679, - "version": 1361, - "versionNonce": 1033389647, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "node management\n(ssh, ray cluster) ", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "7zXodXAZupZo9ox9VxG2c", - "originalText": "node management\n(ssh, ray cluster) ", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "_jFvOMU6QE09vt670MiYY", - "type": "rectangle", - "x": 872.4811085720062, - "y": 3547.260099029891, - "width": 163.1357446724801, - "height": 106.45708709635272, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1B", - "roundness": null, - "seed": 1055156079, - "version": 602, - "versionNonce": 1494939727, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "MlWwMns8GvsbUSPuAkpxn" - } - ], - "updated": 1766545582405, - "link": null, - "locked": false - }, - { - "id": "MlWwMns8GvsbUSPuAkpxn", - "type": "text", - "x": 903.3990251587345, - "y": 3575.4886425780674, - "width": 101.29991149902344, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1C", - "roundness": null, - "seed": 199467407, - "version": 665, - "versionNonce": 461995779, - "isDeleted": false, - "boundElements": [], - "updated": 1766652390408, - "link": null, - "locked": false, - "text": "ray worker\nnode", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "_jFvOMU6QE09vt670MiYY", - "originalText": "ray worker node", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "tzm932P2Nq-2wiRjYiVPx", - "type": "rectangle", - "x": 872.5180169256738, - "y": 3358.046197614518, - "width": 163.1357446724801, - "height": 85, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1D", - "roundness": null, - "seed": 87463855, - "version": 617, - "versionNonce": 1553962159, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "UI2QBOWsvPD_E4TkC2r6A" - } - ], - "updated": 1766545389705, - "link": null, - "locked": false - }, - { - "id": "UI2QBOWsvPD_E4TkC2r6A", - "type": "text", - "x": 903.4359335124021, - "y": 3375.546197614518, - "width": 101.29991149902344, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1E", - "roundness": null, - "seed": 1627769295, - "version": 684, - "versionNonce": 379757869, - "isDeleted": false, - "boundElements": [], - "updated": 1766652387677, - "link": null, - "locked": false, - "text": "ray worker\nnode", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "tzm932P2Nq-2wiRjYiVPx", - "originalText": "ray worker node", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "vzmun3KBgH1bWxrCLhdr4", - "type": "rectangle", - "x": 353.0680720006858, - "y": 3339.3230342805946, - "width": 147.4799234681481, - "height": 55.000000000000014, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1F", - "roundness": null, - "seed": 2087303151, - "version": 1068, - "versionNonce": 24605935, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "VEsmYxCGuRpYe7MsXVGH4" - } - ], - "updated": 1766545389705, - "link": null, - "locked": false - }, - { - "id": "VEsmYxCGuRpYe7MsXVGH4", - "type": "text", - "x": 395.5780608954044, - "y": 3354.3230342805946, - "width": 62.45994567871094, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1G", - "roundness": null, - "seed": 2104156687, - "version": 1119, - "versionNonce": 1941503759, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "WebUI", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "vzmun3KBgH1bWxrCLhdr4", - "originalText": "WebUI", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "kqsUdl0CACk1QR5EJIsUM", - "type": "rectangle", - "x": 596.7398537616585, - "y": 3614.4184242970077, - "width": 156.66668701171875, - "height": 85, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1H", - "roundness": null, - "seed": 889309231, - "version": 852, - "versionNonce": 1692812815, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "48jcZ2N3xXiaxLPFd7pd-" - }, - { - "id": "bkx6LQav2SUWAUBD5ulLa", - "type": "arrow" - } - ], - "updated": 1766546214569, - "link": null, - "locked": false - }, - { - "id": "48jcZ2N3xXiaxLPFd7pd-", - "type": "text", - "x": 607.6232537250374, - "y": 3631.9184242970077, - "width": 134.89988708496094, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1I", - "roundness": null, - "seed": 1092020815, - "version": 882, - "versionNonce": 1330794145, - "isDeleted": false, - "boundElements": [], - "updated": 1766548533430, - "link": null, - "locked": false, - "text": "Advanced\nVerlTaskSpec ", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "kqsUdl0CACk1QR5EJIsUM", - "originalText": "Advanced VerlTaskSpec ", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "bkx6LQav2SUWAUBD5ulLa", - "type": "arrow", - "x": 596.276046964742, - "y": 3658.7834168112076, - "width": 80.66214281697523, - "height": 84.85848269127973, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1J", - "roundness": { - "type": 2 - }, - "seed": 1854064751, - "version": 543, - "versionNonce": 1496676129, - "isDeleted": false, - "boundElements": [], - "updated": 1766545390322, - "link": null, - "locked": false, - "points": [ - [ - 0, - 0 - ], - [ - -80.66214281697523, - -84.85848269127973 - ] - ], - "lastCommittedPoint": null, - "startBinding": { - "elementId": "kqsUdl0CACk1QR5EJIsUM", - "focus": -0.6785882314731533, - "gap": 1 - }, - "endBinding": { - "elementId": "_W--_CG68zeuj3T62ETPL", - "focus": -0.4748599898404899, - "gap": 8.517998107308927 - }, - "startArrowhead": null, - "endArrowhead": "arrow", - "elbowed": false - }, - { - "id": "IOTv_Hn-HGdkkJrYt0s5v", - "type": "rectangle", - "x": 167.86402345456077, - "y": 3412.4409555825896, - "width": 154.6436510018092, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1K", - "roundness": null, - "seed": 1527636623, - "version": 1242, - "versionNonce": 676115887, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "m_jx2UrHXZ0NfOapDPm-L" - } - ], - "updated": 1766545389705, - "link": null, - "locked": false - }, - { - "id": "m_jx2UrHXZ0NfOapDPm-L", - "type": "text", - "x": 184.06589198525052, - "y": 3417.4409555825896, - "width": 122.23991394042969, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1L", - "roundness": null, - "seed": 1467005103, - "version": 1310, - "versionNonce": 933767119, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "user\nmanagement ", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "IOTv_Hn-HGdkkJrYt0s5v", - "originalText": "user management ", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "YTMJ4-LAXakruC_IZ4VMV", - "type": "rectangle", - "x": 173.92541987347784, - "y": 3606.0257872353413, - "width": 154.6436510018092, - "height": 85, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1M", - "roundness": null, - "seed": 156486351, - "version": 1474, - "versionNonce": 1259361775, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "3zvswnkz729ixr6DjNPxl" - } - ], - "updated": 1766545389705, - "link": null, - "locked": false - }, - { - "id": "3zvswnkz729ixr6DjNPxl", - "type": "text", - "x": 194.1272884041676, - "y": 3611.0257872353413, - "width": 114.23991394042969, - "height": 75, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1N", - "roundness": null, - "seed": 1297062127, - "version": 1556, - "versionNonce": 965591055, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "data\nmanagement\nSFTPGo ", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "YTMJ4-LAXakruC_IZ4VMV", - "originalText": "data management\nSFTPGo ", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "KLsJgrEgTOZMoS63wjzN_", - "type": "rectangle", - "x": 181.8517767913874, - "y": 3750.009723085738, - "width": 154.6436510018092, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1O", - "roundness": null, - "seed": 1491903247, - "version": 1311, - "versionNonce": 1980786799, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "ep3LNDs6SOtIx1TDhkW8G" - } - ], - "updated": 1766545503640, - "link": null, - "locked": false - }, - { - "id": "ep3LNDs6SOtIx1TDhkW8G", - "type": "text", - "x": 204.20363921856153, - "y": 3767.509723085738, - "width": 109.93992614746094, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1P", - "roundness": null, - "seed": 480471343, - "version": 1390, - "versionNonce": 1553614927, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "prometheus", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "KLsJgrEgTOZMoS63wjzN_", - "originalText": "prometheus", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "v560fy5BrT8Z52JNzT8xm", - "type": "rectangle", - "x": 379.5438860085891, - "y": 3748.144815945423, - "width": 154.6436510018092, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1Q", - "roundness": null, - "seed": 1708944207, - "version": 1355, - "versionNonce": 2145114625, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "ooj5Xb_9jcdjO3WZ1Vijt" - } - ], - "updated": 1766545503640, - "link": null, - "locked": false - }, - { - "id": "ooj5Xb_9jcdjO3WZ1Vijt", - "type": "text", - "x": 420.0857508771695, - "y": 3765.644815945423, - "width": 73.55992126464844, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1R", - "roundness": null, - "seed": 1089481071, - "version": 1443, - "versionNonce": 1473364111, - "isDeleted": false, - "boundElements": [], - "updated": 1766545389705, - "link": null, - "locked": false, - "text": "grafana", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "v560fy5BrT8Z52JNzT8xm", - "originalText": "grafana", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "SBmp6XU2_8KPazefCsLpu", - "type": "rectangle", - "x": 581.8987326329435, - "y": 3746.2797380573393, - "width": 154.6436510018092, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1S", - "roundness": null, - "seed": 455549839, - "version": 1403, - "versionNonce": 466490881, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "0EqDUcXu-9N73qqFrRs52" - } - ], - "updated": 1766546131803, - "link": null, - "locked": false - }, - { - "id": "0EqDUcXu-9N73qqFrRs52", - "type": "text", - "x": 640.5905685098246, - "y": 3763.7797380573393, - "width": 37.259979248046875, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1T", - "roundness": null, - "seed": 616159663, - "version": 1511, - "versionNonce": 703097903, - "isDeleted": false, - "boundElements": [], - "updated": 1766546128096, - "link": null, - "locked": false, - "text": "ELK", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "SBmp6XU2_8KPazefCsLpu", - "originalText": "ELK", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "uhMYcKaMG2rZhI00IcBSD", - "type": "rectangle", - "x": 355.7896175628582, - "y": 2294.6225286985473, - "width": 156.66668701171875, - "height": 85, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1U", - "roundness": null, - "seed": 183438625, - "version": 747, - "versionNonce": 743207841, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "tG5qUF40N98-XcVs10hIV" - } - ], - "updated": 1766545460643, - "link": null, - "locked": false - }, - { - "id": "tG5qUF40N98-XcVs10hIV", - "type": "text", - "x": 399.91299250182306, - "y": 2312.1225286985473, - "width": 68.41993713378906, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1V", - "roundness": null, - "seed": 984793345, - "version": 776, - "versionNonce": 1133276417, - "isDeleted": false, - "boundElements": [], - "updated": 1766545474186, - "link": null, - "locked": false, - "text": "model\nServing", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "uhMYcKaMG2rZhI00IcBSD", - "originalText": "model\nServing", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "WqZBuHMkJ_xXpYPfrQPkl", - "type": "rectangle", - "x": 361.14377704466176, - "y": 2918.757745757631, - "width": 156.66668701171875, - "height": 85, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1W", - "roundness": null, - "seed": 1897690607, - "version": 814, - "versionNonce": 1206380609, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "zyzk1x5dPQV0NxUlyzpVt" - } - ], - "updated": 1766545485370, - "link": null, - "locked": false - }, - { - "id": "zyzk1x5dPQV0NxUlyzpVt", - "type": "text", - "x": 405.2671519836266, - "y": 2936.257745757631, - "width": 68.41993713378906, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1X", - "roundness": null, - "seed": 152428559, - "version": 842, - "versionNonce": 391770127, - "isDeleted": false, - "boundElements": [], - "updated": 1766545482988, - "link": null, - "locked": false, - "text": "model\nServing", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "WqZBuHMkJ_xXpYPfrQPkl", - "originalText": "model\nServing", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "HLOBaDvlTUpD2yQy4lUZI", - "type": "rectangle", - "x": 358.0842673445005, - "y": 3614.0260906693875, - "width": 156.66668701171875, - "height": 85, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1Y", - "roundness": null, - "seed": 704275425, - "version": 874, - "versionNonce": 746143073, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "2fK4AUySK3XyYyYntFqtC" - } - ], - "updated": 1766545494104, - "link": null, - "locked": false - }, - { - "id": "2fK4AUySK3XyYyYntFqtC", - "type": "text", - "x": 402.20764228346536, - "y": 3631.5260906693875, - "width": 68.41993713378906, - "height": 50, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1Z", - "roundness": null, - "seed": 1940209601, - "version": 902, - "versionNonce": 1141696833, - "isDeleted": false, - "boundElements": [], - "updated": 1766545494104, - "link": null, - "locked": false, - "text": "model\nServing", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "HLOBaDvlTUpD2yQy4lUZI", - "originalText": "model\nServing", - "autoResize": true, - "lineHeight": 1.25 - }, { "id": "DHdIrKA2SmPRr2rTtJJ8E", "type": "rectangle", - "x": 901.0073882308212, - "y": 2951.1443487304227, + "x": 940.088203276161, + "y": 4825.921889114552, "width": 106.83929336612698, "height": 60, "angle": 0, @@ -5830,8 +4445,8 @@ "index": "b1a", "roundness": null, "seed": 1809008399, - "version": 1385, - "versionNonce": 2086522159, + "version": 1585, + "versionNonce": 831231395, "isDeleted": false, "boundElements": [ { @@ -5839,15 +4454,15 @@ "id": "HbJRSZyHsD6NLPzLVoa9C" } ], - "updated": 1766545568758, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "HbJRSZyHsD6NLPzLVoa9C", "type": "text", - "x": 912.3370614641777, - "y": 2968.6443487304227, + "x": 951.4178765095176, + "y": 4843.421889114552, "width": 84.17994689941406, "height": 25, "angle": 0, @@ -5863,11 +4478,11 @@ "index": "b1b", "roundness": null, "seed": 1662356783, - "version": 1476, - "versionNonce": 682250063, + "version": 1676, + "versionNonce": 1936648515, "isDeleted": false, "boundElements": [], - "updated": 1766545568758, + "updated": 1767580421422, "link": null, "locked": false, "text": "exporter", @@ -5883,8 +4498,8 @@ { "id": "SYvgt1uhLv6KU1Snpyha0", "type": "rectangle", - "x": 902.7282442681425, - "y": 2740.039825032305, + "x": 941.8090593134823, + "y": 4614.817365416434, "width": 106.83929336612698, "height": 60, "angle": 0, @@ -5900,8 +4515,8 @@ "index": "b1c", "roundness": null, "seed": 1250827695, - "version": 1399, - "versionNonce": 852631169, + "version": 1599, + "versionNonce": 820431075, "isDeleted": false, "boundElements": [ { @@ -5909,15 +4524,15 @@ "id": "zbUHQA7llONcizbRp8kkI" } ], - "updated": 1766545566574, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "zbUHQA7llONcizbRp8kkI", "type": "text", - "x": 914.057917501499, - "y": 2757.539825032305, + "x": 953.1387325468388, + "y": 4632.317365416434, "width": 84.17994689941406, "height": 25, "angle": 0, @@ -5933,11 +4548,11 @@ "index": "b1d", "roundness": null, "seed": 1568444367, - "version": 1490, - "versionNonce": 183281249, + "version": 1690, + "versionNonce": 528594051, "isDeleted": false, "boundElements": [], - "updated": 1766545566574, + "updated": 1767580421422, "link": null, "locked": false, "text": "exporter", @@ -5950,151 +4565,11 @@ "autoResize": true, "lineHeight": 1.25 }, - { - "id": "fPC0GPrONm31BqGfJwBv-", - "type": "rectangle", - "x": 896.1313493540686, - "y": 3649.472220848862, - "width": 106.83929336612698, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1e", - "roundness": null, - "seed": 857988591, - "version": 1451, - "versionNonce": 266393807, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "LS0wSTonqjRNneUbZVukD" - } - ], - "updated": 1766545590131, - "link": null, - "locked": false - }, - { - "id": "LS0wSTonqjRNneUbZVukD", - "type": "text", - "x": 907.4610225874251, - "y": 3666.972220848862, - "width": 84.17994689941406, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1f", - "roundness": null, - "seed": 1710245903, - "version": 1541, - "versionNonce": 477998447, - "isDeleted": false, - "boundElements": [], - "updated": 1766545577140, - "link": null, - "locked": false, - "text": "exporter", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "fPC0GPrONm31BqGfJwBv-", - "originalText": "exporter", - "autoResize": true, - "lineHeight": 1.25 - }, - { - "id": "89v45dCwlOq01K4woKzly", - "type": "rectangle", - "x": 897.85220539139, - "y": 3438.367697150744, - "width": 106.83929336612698, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1g", - "roundness": null, - "seed": 1944743471, - "version": 1465, - "versionNonce": 2110857121, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "H-xVGBDkmPVLm7G5Tmz19" - } - ], - "updated": 1766545590131, - "link": null, - "locked": false - }, - { - "id": "H-xVGBDkmPVLm7G5Tmz19", - "type": "text", - "x": 909.1818786247466, - "y": 3455.867697150744, - "width": 84.17994689941406, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1h", - "roundness": null, - "seed": 670748751, - "version": 1555, - "versionNonce": 1438014895, - "isDeleted": false, - "boundElements": [], - "updated": 1766545577140, - "link": null, - "locked": false, - "text": "exporter", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "89v45dCwlOq01K4woKzly", - "originalText": "exporter", - "autoResize": true, - "lineHeight": 1.25 - }, { "id": "ND0kaXREeIHTrNlSqY34p", "type": "rectangle", - "x": 582.8208303270419, - "y": 3048.283098956208, + "x": 629.4389274863165, + "y": 5021.045738109877, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -6110,8 +4585,8 @@ "index": "b1i", "roundness": null, "seed": 814939215, - "version": 1330, - "versionNonce": 519559151, + "version": 1566, + "versionNonce": 1056844835, "isDeleted": false, "boundElements": [ { @@ -6119,15 +4594,15 @@ "id": "e8-1MnFz0QWrPJXZ-ZnQ3" } ], - "updated": 1766546085637, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "e8-1MnFz0QWrPJXZ-ZnQ3", "type": "text", - "x": 641.5126662039231, - "y": 3065.783098956208, + "x": 688.1307633631977, + "y": 5038.545738109877, "width": 37.259979248046875, "height": 25, "angle": 0, @@ -6143,11 +4618,11 @@ "index": "b1j", "roundness": null, "seed": 2024577647, - "version": 1423, - "versionNonce": 135499201, + "version": 1659, + "versionNonce": 323661763, "isDeleted": false, "boundElements": [], - "updated": 1766546094558, + "updated": 1767580421422, "link": null, "locked": false, "text": "ELK", @@ -6160,81 +4635,11 @@ "autoResize": true, "lineHeight": 1.25 }, - { - "id": "Q-6aV4mSfbyEwllp_M7u2", - "type": "rectangle", - "x": 183.94027304345434, - "y": 3846.0441434972986, - "width": 546.6403289665554, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "#ffc9c9", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1k", - "roundness": null, - "seed": 1627548065, - "version": 1641, - "versionNonce": 511334241, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "ZKpraEqfq0gLY7JI2xzC6" - } - ], - "updated": 1766546166116, - "link": null, - "locked": false - }, - { - "id": "ZKpraEqfq0gLY7JI2xzC6", - "type": "text", - "x": 391.4104924583727, - "y": 3863.5441434972986, - "width": 131.69989013671875, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b1l", - "roundness": null, - "seed": 522620289, - "version": 1768, - "versionNonce": 1020210383, - "isDeleted": false, - "boundElements": [], - "updated": 1766546159215, - "link": null, - "locked": false, - "text": "weight & bias", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "Q-6aV4mSfbyEwllp_M7u2", - "originalText": "weight & bias", - "autoResize": true, - "lineHeight": 1.25 - }, { "id": "UOlgJuhEaQm81KRHgyY-0", "type": "rectangle", - "x": 157.29031698446732, - "y": 4084.8474727592948, + "x": 179.07220592595837, + "y": 5244.209019024574, "width": 647.3203086953981, "height": 717.4711059065564, "angle": 0, @@ -6252,19 +4657,19 @@ "type": 3 }, "seed": 790237583, - "version": 1075, - "versionNonce": 1932557135, + "version": 1166, + "versionNonce": 1146533539, "isDeleted": false, "boundElements": [], - "updated": 1766546448759, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "ExBkrVpyvo_OXAog8ivWn", "type": "rectangle", - "x": 362.9438471154123, - "y": 4265.514129253435, + "x": 384.7257360569034, + "y": 5424.875675518715, "width": 156.66668701171875, "height": 85, "angle": 0, @@ -6280,8 +4685,8 @@ "index": "b1n", "roundness": null, "seed": 1478428591, - "version": 728, - "versionNonce": 1800969423, + "version": 819, + "versionNonce": 573434435, "isDeleted": false, "boundElements": [ { @@ -6297,15 +4702,15 @@ "type": "arrow" } ], - "updated": 1766546319431, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "hqH-l-CJ15NjmjoZtiq-9", "type": "text", - "x": 383.88721411980686, - "y": 4283.014129253435, + "x": 405.6691030612979, + "y": 5442.375675518715, "width": 114.77995300292969, "height": 50, "angle": 0, @@ -6321,11 +4726,11 @@ "index": "b1o", "roundness": null, "seed": 492845519, - "version": 718, - "versionNonce": 2062166767, + "version": 809, + "versionNonce": 303933923, "isDeleted": false, "boundElements": [], - "updated": 1766546319431, + "updated": 1767580076534, "link": null, "locked": false, "text": "ray job tool\n(ray client)", @@ -6341,8 +4746,8 @@ { "id": "kurhiArg2sM7XFjEdTyrI", "type": "rectangle", - "x": 607.9438471154124, - "y": 4265.014129253435, + "x": 629.7257360569034, + "y": 5424.375675518715, "width": 156.66668701171875, "height": 85, "angle": 0, @@ -6358,8 +4763,8 @@ "index": "b1p", "roundness": null, "seed": 603334639, - "version": 867, - "versionNonce": 501759311, + "version": 958, + "versionNonce": 380685603, "isDeleted": false, "boundElements": [ { @@ -6371,15 +4776,15 @@ "type": "arrow" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "7k75o8I7t4NCGdyrtenGs", "type": "text", - "x": 618.8272470787913, - "y": 4282.514129253435, + "x": 640.6091360202823, + "y": 5441.875675518715, "width": 134.89988708496094, "height": 50, "angle": 0, @@ -6395,11 +4800,11 @@ "index": "b1q", "roundness": null, "seed": 1508463119, - "version": 869, - "versionNonce": 1806809967, + "version": 960, + "versionNonce": 2114007235, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "VerlTaskSpec \nyaml", @@ -6415,8 +4820,8 @@ { "id": "qO3lO1bGwrfyKuWKhDJ1J", "type": "arrow", - "x": 609.2771906212718, - "y": 4303.514129253435, + "x": 631.0590795627628, + "y": 5462.875675518715, "width": 90.33331298828125, "height": 4, "angle": 0, @@ -6434,11 +4839,11 @@ "type": 2 }, "seed": 1479686191, - "version": 1893, - "versionNonce": 301239841, + "version": 2170, + "versionNonce": 668673901, "isDeleted": false, "boundElements": [], - "updated": 1766546320485, + "updated": 1767580076859, "link": null, "locked": false, "points": [ @@ -6454,12 +4859,12 @@ "lastCommittedPoint": null, "startBinding": { "elementId": "kurhiArg2sM7XFjEdTyrI", - "focus": 0.16118805172017495, + "focus": 0.16118805172015774, "gap": 1.333343505859375 }, "endBinding": { "elementId": "ExBkrVpyvo_OXAog8ivWn", - "focus": 0.06393742185082298, + "focus": 0.06393742185084282, "gap": 1 }, "startArrowhead": null, @@ -6469,8 +4874,8 @@ { "id": "oZv8__WhJ3c5fkGqXVl2_", "type": "text", - "x": 335.61054629649533, - "y": 4051.2482773211123, + "x": 357.3924352379864, + "y": 5210.609823586392, "width": 385.8797302246094, "height": 25, "angle": 0, @@ -6486,11 +4891,11 @@ "index": "b1s", "roundness": null, "seed": 79256143, - "version": 886, - "versionNonce": 682329921, + "version": 977, + "versionNonce": 571188131, "isDeleted": false, "boundElements": [], - "updated": 1766546372109, + "updated": 1767580076534, "link": null, "locked": false, "text": "v5.0 operability (statistics, sop, agent)", @@ -6506,8 +4911,8 @@ { "id": "WNodxBjaE-CYp8dK1fMQN", "type": "rectangle", - "x": 548.7677951885864, - "y": 4107.68072471242, + "x": 570.5496841300775, + "y": 5267.042270977699, "width": 210.1763265850289, "height": 55.000000000000014, "angle": 0, @@ -6523,8 +4928,8 @@ "index": "b1t", "roundness": null, "seed": 1951873135, - "version": 1017, - "versionNonce": 1038945263, + "version": 1108, + "versionNonce": 884815683, "isDeleted": false, "boundElements": [ { @@ -6532,15 +4937,15 @@ "id": "G2DrVh_u1lhQlxAkO611N" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "G2DrVh_u1lhQlxAkO611N", "type": "text", - "x": 601.0059981539524, - "y": 4122.68072471242, + "x": 622.7878870954435, + "y": 5282.042270977699, "width": 105.69992065429688, "height": 25, "angle": 0, @@ -6556,11 +4961,11 @@ "index": "b1u", "roundness": null, "seed": 215241359, - "version": 1058, - "versionNonce": 1533261327, + "version": 1149, + "versionNonce": 255547107, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "API server", @@ -6576,8 +4981,8 @@ { "id": "XxNyZL0flCwhAO9zT3UQy", "type": "rectangle", - "x": 362.7771906212717, - "y": 4178.68072471242, + "x": 384.55907956276275, + "y": 5338.042270977699, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -6593,8 +4998,8 @@ "index": "b1v", "roundness": null, "seed": 1726556335, - "version": 1247, - "versionNonce": 79698991, + "version": 1338, + "versionNonce": 538215043, "isDeleted": false, "boundElements": [ { @@ -6602,15 +5007,15 @@ "id": "O2dtUuIqb0fs_uv7D29cW" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "O2dtUuIqb0fs_uv7D29cW", "type": "text", - "x": 378.97905915196145, - "y": 4183.68072471242, + "x": 400.7609480934525, + "y": 5343.042270977699, "width": 122.23991394042969, "height": 50, "angle": 0, @@ -6626,11 +5031,11 @@ "index": "b1w", "roundness": null, "seed": 1883126479, - "version": 1308, - "versionNonce": 770358863, + "version": 1399, + "versionNonce": 473635, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "task\nmanagement ", @@ -6646,8 +5051,8 @@ { "id": "UK3M2EByn3haJdkH9Z21T", "type": "rectangle", - "x": 546.8847292069415, - "y": 4179.255033025133, + "x": 568.6666181484326, + "y": 5338.616579290413, "width": 207.59447102997498, "height": 60, "angle": 0, @@ -6663,8 +5068,8 @@ "index": "b1x", "roundness": null, "seed": 1298925807, - "version": 1338, - "versionNonce": 968629359, + "version": 1429, + "versionNonce": 738328003, "isDeleted": false, "boundElements": [ { @@ -6672,15 +5077,15 @@ "id": "_erL3NkeChZxbmMXnWyQ8" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "_erL3NkeChZxbmMXnWyQ8", "type": "text", - "x": 563.1520346071829, - "y": 4184.255033025133, + "x": 584.933923548674, + "y": 5343.616579290413, "width": 175.0598602294922, "height": 50, "angle": 0, @@ -6696,11 +5101,11 @@ "index": "b1y", "roundness": null, "seed": 2080051983, - "version": 1428, - "versionNonce": 469546639, + "version": 1519, + "versionNonce": 2133020003, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "node management\n(ssh, ray cluster) ", @@ -6716,8 +5121,8 @@ { "id": "lQT5-sydwGN_vbadu61qE", "type": "rectangle", - "x": 884.9957366586793, - "y": 4313.4048967959725, + "x": 906.7776256001704, + "y": 5472.766443061252, "width": 163.1357446724801, "height": 106.45708709635272, "angle": 0, @@ -6733,8 +5138,8 @@ "index": "b1z", "roundness": null, "seed": 878958895, - "version": 669, - "versionNonce": 996478127, + "version": 760, + "versionNonce": 1824106755, "isDeleted": false, "boundElements": [ { @@ -6742,15 +5147,15 @@ "id": "yqwCJHk3GkcnyEzFHSU2z" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "yqwCJHk3GkcnyEzFHSU2z", "type": "text", - "x": 915.9136532454077, - "y": 4341.633440344149, + "x": 937.6955421868987, + "y": 5500.994986609428, "width": 101.29991149902344, "height": 50, "angle": 0, @@ -6766,11 +5171,11 @@ "index": "b20", "roundness": null, "seed": 202300239, - "version": 733, - "versionNonce": 786567267, + "version": 824, + "versionNonce": 2051248291, "isDeleted": false, "boundElements": [], - "updated": 1766652405206, + "updated": 1767580076534, "link": null, "locked": false, "text": "ray worker\nnode", @@ -6786,8 +5191,8 @@ { "id": "n74P3TAm78jOVPnYqFccD", "type": "rectangle", - "x": 885.0326450123471, - "y": 4124.1909953806, + "x": 906.8145339538381, + "y": 5283.5525416458795, "width": 163.1357446724801, "height": 85, "angle": 0, @@ -6803,8 +5208,8 @@ "index": "b21", "roundness": null, "seed": 1465162095, - "version": 684, - "versionNonce": 389999855, + "version": 775, + "versionNonce": 2037351491, "isDeleted": false, "boundElements": [ { @@ -6812,15 +5217,15 @@ "id": "BTIKScHoPW_m2eS-Ro8GX" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "BTIKScHoPW_m2eS-Ro8GX", "type": "text", - "x": 915.9505615990754, - "y": 4141.6909953806, + "x": 937.7324505405664, + "y": 5301.0525416458795, "width": 101.29991149902344, "height": 50, "angle": 0, @@ -6836,11 +5241,11 @@ "index": "b22", "roundness": null, "seed": 1266520975, - "version": 750, - "versionNonce": 1196796365, + "version": 841, + "versionNonce": 35390435, "isDeleted": false, "boundElements": [], - "updated": 1766652402009, + "updated": 1767580076534, "link": null, "locked": false, "text": "ray worker\nnode", @@ -6856,8 +5261,8 @@ { "id": "L9owxY-Ly_UhJE3U0jLgC", "type": "rectangle", - "x": 365.582700087359, - "y": 4105.467832046676, + "x": 387.36458902885005, + "y": 5264.829378311956, "width": 147.4799234681481, "height": 55.000000000000014, "angle": 0, @@ -6873,8 +5278,8 @@ "index": "b23", "roundness": null, "seed": 1623345583, - "version": 1135, - "versionNonce": 1238220079, + "version": 1226, + "versionNonce": 743935875, "isDeleted": false, "boundElements": [ { @@ -6882,15 +5287,15 @@ "id": "8S3xYcC5-56RoTln81Vps" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "8S3xYcC5-56RoTln81Vps", "type": "text", - "x": 408.09268898207756, - "y": 4120.467832046676, + "x": 429.8745779235686, + "y": 5279.829378311956, "width": 62.45994567871094, "height": 25, "angle": 0, @@ -6906,11 +5311,11 @@ "index": "b24", "roundness": null, "seed": 1951087567, - "version": 1187, - "versionNonce": 580665167, + "version": 1278, + "versionNonce": 174106403, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "WebUI", @@ -6926,8 +5331,8 @@ { "id": "r5vmCFPIVwxZGcbV8EtDP", "type": "rectangle", - "x": 609.2544818483318, - "y": 4380.56322206309, + "x": 631.0363707898229, + "y": 5539.924768328369, "width": 156.66668701171875, "height": 85, "angle": 0, @@ -6943,8 +5348,8 @@ "index": "b25", "roundness": null, "seed": 707848687, - "version": 919, - "versionNonce": 2123726191, + "version": 1010, + "versionNonce": 1655032515, "isDeleted": false, "boundElements": [ { @@ -6956,15 +5361,15 @@ "type": "arrow" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "BPGRZQXUil0Bmwl2yz1n9", "type": "text", - "x": 620.1378818117107, - "y": 4398.06322206309, + "x": 641.9197707532018, + "y": 5557.424768328369, "width": 134.89988708496094, "height": 50, "angle": 0, @@ -6980,11 +5385,11 @@ "index": "b26", "roundness": null, "seed": 259773455, - "version": 932, - "versionNonce": 170600623, + "version": 1023, + "versionNonce": 700807779, "isDeleted": false, "boundElements": [], - "updated": 1766548543557, + "updated": 1767580076534, "link": null, "locked": false, "text": "Advanced\nVerlTaskSpec ", @@ -7000,8 +5405,8 @@ { "id": "DS3tViVPwVs_FojIMqBSU", "type": "arrow", - "x": 608.7906750514153, - "y": 4424.9282145772895, + "x": 630.5725639929063, + "y": 5584.289760842569, "width": 80.66214281697523, "height": 84.85848269127973, "angle": 0, @@ -7019,11 +5424,11 @@ "type": 2 }, "seed": 2082765359, - "version": 740, - "versionNonce": 1441675745, + "version": 1017, + "versionNonce": 2023747629, "isDeleted": false, "boundElements": [], - "updated": 1766546320485, + "updated": 1767580076859, "link": null, "locked": false, "points": [ @@ -7039,14 +5444,10 @@ "lastCommittedPoint": null, "startBinding": { "elementId": "r5vmCFPIVwxZGcbV8EtDP", - "focus": -0.6785882314731602, + "focus": -0.6785882314731624, "gap": 1 }, - "endBinding": { - "elementId": "ExBkrVpyvo_OXAog8ivWn", - "focus": -0.4748599898404967, - "gap": 8.517998107308927 - }, + "endBinding": null, "startArrowhead": null, "endArrowhead": "arrow", "elbowed": false @@ -7054,8 +5455,8 @@ { "id": "AUK2tr7zEw-7pPSMh0Ric", "type": "rectangle", - "x": 180.37865154123398, - "y": 4178.5857533486715, + "x": 202.16054048272503, + "y": 5337.947299613951, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -7071,8 +5472,8 @@ "index": "b28", "roundness": null, "seed": 890461263, - "version": 1309, - "versionNonce": 132107759, + "version": 1400, + "versionNonce": 1564791107, "isDeleted": false, "boundElements": [ { @@ -7080,15 +5481,15 @@ "id": "B-9nH3-XOZNOEeCLd2Fu-" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "B-9nH3-XOZNOEeCLd2Fu-", "type": "text", - "x": 196.58052007192373, - "y": 4183.5857533486715, + "x": 218.36240901341478, + "y": 5342.947299613951, "width": 122.23991394042969, "height": 50, "angle": 0, @@ -7104,11 +5505,11 @@ "index": "b29", "roundness": null, "seed": 1310784111, - "version": 1377, - "versionNonce": 497956879, + "version": 1468, + "versionNonce": 935309539, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "user\nmanagement ", @@ -7124,8 +5525,8 @@ { "id": "rfGCTpcFlf1T-8NxoJELc", "type": "rectangle", - "x": 186.44004796015105, - "y": 4372.170585001423, + "x": 208.2219369016421, + "y": 5531.532131266703, "width": 154.6436510018092, "height": 85, "angle": 0, @@ -7141,8 +5542,8 @@ "index": "b2A", "roundness": null, "seed": 1276499087, - "version": 1541, - "versionNonce": 1584029231, + "version": 1632, + "versionNonce": 721263747, "isDeleted": false, "boundElements": [ { @@ -7150,15 +5551,15 @@ "id": "12ZSYQo1l8nLuktF_M13y" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "12ZSYQo1l8nLuktF_M13y", "type": "text", - "x": 206.6419164908408, - "y": 4377.170585001423, + "x": 228.42380543233185, + "y": 5536.532131266703, "width": 114.23991394042969, "height": 75, "angle": 0, @@ -7174,11 +5575,11 @@ "index": "b2B", "roundness": null, "seed": 111831727, - "version": 1623, - "versionNonce": 486326351, + "version": 1714, + "versionNonce": 1119687715, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "data\nmanagement\nSFTPGo ", @@ -7194,8 +5595,8 @@ { "id": "kvfUPQgQ5eG_Fxn-tGm59", "type": "rectangle", - "x": 194.36640487806062, - "y": 4516.15452085182, + "x": 217.25149426722095, + "y": 5786.938428579538, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -7211,8 +5612,8 @@ "index": "b2C", "roundness": null, "seed": 1250376911, - "version": 1378, - "versionNonce": 993910383, + "version": 1501, + "versionNonce": 965575715, "isDeleted": false, "boundElements": [ { @@ -7220,15 +5621,15 @@ "id": "a5mX2A_-o1LoWTFedzdqg" } ], - "updated": 1766546319432, + "updated": 1767580554066, "link": null, "locked": false }, { "id": "a5mX2A_-o1LoWTFedzdqg", "type": "text", - "x": 216.71826730523475, - "y": 4533.65452085182, + "x": 239.60335669439507, + "y": 5804.438428579538, "width": 109.93992614746094, "height": 25, "angle": 0, @@ -7244,11 +5645,11 @@ "index": "b2D", "roundness": null, "seed": 994364143, - "version": 1457, - "versionNonce": 1274427535, + "version": 1580, + "versionNonce": 841942979, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580554066, "link": null, "locked": false, "text": "prometheus", @@ -7264,8 +5665,8 @@ { "id": "_M4VtkTCC_G6rAalQU_8m", "type": "rectangle", - "x": 392.0585140952623, - "y": 4514.289613711505, + "x": 414.9436034844226, + "y": 5785.0735214392225, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -7281,8 +5682,8 @@ "index": "b2E", "roundness": null, "seed": 1969984783, - "version": 1422, - "versionNonce": 1960726191, + "version": 1545, + "versionNonce": 253695843, "isDeleted": false, "boundElements": [ { @@ -7290,15 +5691,15 @@ "id": "xQD6qQi-05xLjq0nbHiqd" } ], - "updated": 1766546319432, + "updated": 1767580554066, "link": null, "locked": false }, { "id": "xQD6qQi-05xLjq0nbHiqd", "type": "text", - "x": 432.6003789638427, - "y": 4531.789613711505, + "x": 455.485468353003, + "y": 5802.5735214392225, "width": 73.55992126464844, "height": 25, "angle": 0, @@ -7314,11 +5715,11 @@ "index": "b2F", "roundness": null, "seed": 1886657327, - "version": 1511, - "versionNonce": 278831311, + "version": 1634, + "versionNonce": 1588343555, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580554066, "link": null, "locked": false, "text": "grafana", @@ -7334,8 +5735,8 @@ { "id": "aCuSAi4oJ_KJdRKAHJ5y_", "type": "rectangle", - "x": 594.4133607196168, - "y": 4512.424535823421, + "x": 617.2984501087772, + "y": 5783.208443551139, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -7351,8 +5752,8 @@ "index": "b2G", "roundness": null, "seed": 549903695, - "version": 1470, - "versionNonce": 282046191, + "version": 1593, + "versionNonce": 1632950947, "isDeleted": false, "boundElements": [ { @@ -7360,15 +5761,15 @@ "id": "25Rm9RG-UnIb_WWemRDJY" } ], - "updated": 1766546319432, + "updated": 1767580554066, "link": null, "locked": false }, { "id": "25Rm9RG-UnIb_WWemRDJY", "type": "text", - "x": 653.1051965964979, - "y": 4529.924535823421, + "x": 675.9902859856581, + "y": 5800.708443551139, "width": 37.259979248046875, "height": 25, "angle": 0, @@ -7384,11 +5785,11 @@ "index": "b2H", "roundness": null, "seed": 1505516399, - "version": 1579, - "versionNonce": 536475919, + "version": 1702, + "versionNonce": 1137151555, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580554066, "link": null, "locked": false, "text": "ELK", @@ -7404,8 +5805,8 @@ { "id": "aVk5d_J2lP8Z8cTxcLino", "type": "rectangle", - "x": 370.59889543117373, - "y": 4380.170888435469, + "x": 392.3807843726648, + "y": 5539.532434700749, "width": 156.66668701171875, "height": 85, "angle": 0, @@ -7421,8 +5822,8 @@ "index": "b2I", "roundness": null, "seed": 1872565647, - "version": 941, - "versionNonce": 60161839, + "version": 1034, + "versionNonce": 686514829, "isDeleted": false, "boundElements": [ { @@ -7430,15 +5831,15 @@ "id": "-7mLziiSO10QZS0oA7Zmp" } ], - "updated": 1766546319432, + "updated": 1767580076859, "link": null, "locked": false }, { "id": "-7mLziiSO10QZS0oA7Zmp", "type": "text", - "x": 414.7222703701386, - "y": 4397.670888435469, + "x": 436.5041593116296, + "y": 5557.032434700749, "width": 68.41993713378906, "height": 50, "angle": 0, @@ -7454,11 +5855,11 @@ "index": "b2J", "roundness": null, "seed": 430017455, - "version": 969, - "versionNonce": 1040990543, + "version": 1060, + "versionNonce": 907837731, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "model\nServing", @@ -7474,8 +5875,8 @@ { "id": "Kwettsi3WP-Qkpn71oRV_", "type": "rectangle", - "x": 908.6459774407417, - "y": 4415.617018614944, + "x": 930.4278663822328, + "y": 5574.9785648802235, "width": 106.83929336612698, "height": 60, "angle": 0, @@ -7491,8 +5892,8 @@ "index": "b2K", "roundness": null, "seed": 334642639, - "version": 1518, - "versionNonce": 1890926447, + "version": 1609, + "versionNonce": 668128355, "isDeleted": false, "boundElements": [ { @@ -7500,15 +5901,15 @@ "id": "Ws50jhW1M-jJ3lnoEAvY2" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "Ws50jhW1M-jJ3lnoEAvY2", "type": "text", - "x": 919.9756506740981, - "y": 4433.117018614944, + "x": 941.7575396155892, + "y": 5592.4785648802235, "width": 84.17994689941406, "height": 25, "angle": 0, @@ -7524,11 +5925,11 @@ "index": "b2L", "roundness": null, "seed": 27765743, - "version": 1609, - "versionNonce": 2111404431, + "version": 1700, + "versionNonce": 65032195, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "exporter", @@ -7544,8 +5945,8 @@ { "id": "uYm1lMA4TITqp8EHogQDU", "type": "rectangle", - "x": 910.3668334780632, - "y": 4204.512494916826, + "x": 932.1487224195542, + "y": 5363.874041182105, "width": 106.83929336612698, "height": 60, "angle": 0, @@ -7561,8 +5962,8 @@ "index": "b2M", "roundness": null, "seed": 248141327, - "version": 1532, - "versionNonce": 1790013359, + "version": 1623, + "versionNonce": 1677547427, "isDeleted": false, "boundElements": [ { @@ -7570,15 +5971,15 @@ "id": "tX9OTModeIVpu_FonBF3E" } ], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "tX9OTModeIVpu_FonBF3E", "type": "text", - "x": 921.6965067114196, - "y": 4222.012494916826, + "x": 943.4783956529106, + "y": 5381.374041182105, "width": 84.17994689941406, "height": 25, "angle": 0, @@ -7594,11 +5995,11 @@ "index": "b2N", "roundness": null, "seed": 1381153839, - "version": 1623, - "versionNonce": 23437775, + "version": 1714, + "versionNonce": 911764291, "isDeleted": false, "boundElements": [], - "updated": 1766546319432, + "updated": 1767580076534, "link": null, "locked": false, "text": "exporter", @@ -7611,81 +6012,11 @@ "autoResize": true, "lineHeight": 1.25 }, - { - "id": "-OJqAXeLe21OC9Q6YQYNB", - "type": "rectangle", - "x": 196.45490113012755, - "y": 4612.18894126338, - "width": 546.6403289665554, - "height": 60, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b2O", - "roundness": null, - "seed": 1881759311, - "version": 1709, - "versionNonce": 2123334031, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "8eYn92JxE4ky_uFVEGphM" - } - ], - "updated": 1766546376993, - "link": null, - "locked": false - }, - { - "id": "8eYn92JxE4ky_uFVEGphM", - "type": "text", - "x": 403.9251205450459, - "y": 4629.68894126338, - "width": 131.69989013671875, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b2P", - "roundness": null, - "seed": 2066136175, - "version": 1835, - "versionNonce": 968145423, - "isDeleted": false, - "boundElements": [], - "updated": 1766546319432, - "link": null, - "locked": false, - "text": "weight & bias", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "-OJqAXeLe21OC9Q6YQYNB", - "originalText": "weight & bias", - "autoResize": true, - "lineHeight": 1.25 - }, { "id": "yGh_yLim4aBl8oGqW_oU1", "type": "rectangle", - "x": 200.4218292527276, - "y": 4713.67831875264, + "x": 222.20371819421865, + "y": 5873.0398650179195, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -7701,8 +6032,8 @@ "index": "b2Q", "roundness": null, "seed": 170223553, - "version": 1420, - "versionNonce": 1579534113, + "version": 1511, + "versionNonce": 1904143907, "isDeleted": false, "boundElements": [ { @@ -7710,15 +6041,15 @@ "id": "lqdb6MLxScVe5ns9aqeeu" } ], - "updated": 1766546445481, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "lqdb6MLxScVe5ns9aqeeu", "type": "text", - "x": 229.18370297140564, - "y": 4731.17831875264, + "x": 250.96559191289668, + "y": 5890.5398650179195, "width": 97.11990356445312, "height": 25, "angle": 0, @@ -7734,11 +6065,11 @@ "index": "b2R", "roundness": null, "seed": 1132775329, - "version": 1508, - "versionNonce": 1918695521, + "version": 1599, + "versionNonce": 2086875587, "isDeleted": false, "boundElements": [], - "updated": 1766546410776, + "updated": 1767580076534, "link": null, "locked": false, "text": "statistics", @@ -7754,8 +6085,8 @@ { "id": "TtGKkGSFi4MbYg3ZfyNiG", "type": "rectangle", - "x": 395.5031174592006, - "y": 4713.67831875264, + "x": 417.28500640069166, + "y": 5873.0398650179195, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -7771,8 +6102,8 @@ "index": "b2S", "roundness": null, "seed": 62346383, - "version": 1479, - "versionNonce": 1431730031, + "version": 1570, + "versionNonce": 356385123, "isDeleted": false, "boundElements": [ { @@ -7780,15 +6111,15 @@ "id": "G7gy-YlXMFxKiO7gmm6tl" } ], - "updated": 1766546445481, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "G7gy-YlXMFxKiO7gmm6tl", "type": "text", - "x": 426.81496371205833, - "y": 4731.17831875264, + "x": 448.5968526535494, + "y": 5890.5398650179195, "width": 92.01995849609375, "height": 25, "angle": 0, @@ -7804,11 +6135,11 @@ "index": "b2T", "roundness": null, "seed": 1310452399, - "version": 1576, - "versionNonce": 1626997793, + "version": 1667, + "versionNonce": 1432214787, "isDeleted": false, "boundElements": [], - "updated": 1766546429018, + "updated": 1767580076534, "link": null, "locked": false, "text": "sop tools", @@ -7824,8 +6155,8 @@ { "id": "JldxRrrtgsQXtVb-OKmEY", "type": "rectangle", - "x": 597.7764406036847, - "y": 4713.678318752639, + "x": 619.5583295451758, + "y": 5873.039865017919, "width": 154.6436510018092, "height": 60, "angle": 0, @@ -7841,8 +6172,8 @@ "index": "b2U", "roundness": null, "seed": 1283509455, - "version": 1515, - "versionNonce": 721566465, + "version": 1606, + "versionNonce": 675366051, "isDeleted": false, "boundElements": [ { @@ -7850,15 +6181,15 @@ "id": "YbDIjM6HTsYLldkdyo8ed" } ], - "updated": 1766546445481, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "YbDIjM6HTsYLldkdyo8ed", "type": "text", - "x": 647.6282877720698, - "y": 4731.178318752639, + "x": 669.4101767135609, + "y": 5890.539865017919, "width": 54.93995666503906, "height": 25, "angle": 0, @@ -7874,11 +6205,11 @@ "index": "b2V", "roundness": null, "seed": 1737251567, - "version": 1618, - "versionNonce": 1250251887, + "version": 1709, + "versionNonce": 93763651, "isDeleted": false, "boundElements": [], - "updated": 1766546440005, + "updated": 1767580076534, "link": null, "locked": false, "text": "agent", @@ -8104,8 +6435,8 @@ { "id": "Z88ttfOpQUtGXPUZOTNLh", "type": "rectangle", - "x": 1046.301230542016, - "y": 2690.267563284097, + "x": 1085.3820455873556, + "y": 4565.045103668226, "width": 78.84661364258261, "height": 239.8912269868912, "angle": 0, @@ -8121,8 +6452,8 @@ "index": "b2l", "roundness": null, "seed": 995417473, - "version": 1195, - "versionNonce": 1412336609, + "version": 1395, + "versionNonce": 512081763, "isDeleted": false, "boundElements": [ { @@ -8130,15 +6461,15 @@ "id": "JUS2JvDrkx5GXQjxkI78l" } ], - "updated": 1766549504559, + "updated": 1767580421422, "link": null, "locked": false }, { "id": "JUS2JvDrkx5GXQjxkI78l", "type": "text", - "x": 1058.1145596411393, - "y": 2797.7131767775427, + "x": 1097.1953746864792, + "y": 4672.4907171616715, "width": 55.21995544433594, "height": 25, "angle": 0, @@ -8154,11 +6485,11 @@ "index": "b2m", "roundness": null, "seed": 314541409, - "version": 1211, - "versionNonce": 883072717, + "version": 1411, + "versionNonce": 452844291, "isDeleted": false, "boundElements": [], - "updated": 1766632675825, + "updated": 1767580421422, "link": null, "locked": false, "text": "GPFS", @@ -8171,81 +6502,11 @@ "autoResize": true, "lineHeight": 1.25 }, - { - "id": "YMHES-uetzrMEDwFkCHdH", - "type": "rectangle", - "x": 1045.7959490351118, - "y": 3391.1356675943966, - "width": 78.84661364258261, - "height": 239.8912269868912, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b2n", - "roundness": null, - "seed": 1580012385, - "version": 1257, - "versionNonce": 206700609, - "isDeleted": false, - "boundElements": [ - { - "type": "text", - "id": "OlaiVvlhdGvnYHx9Km9Qv" - } - ], - "updated": 1766549511124, - "link": null, - "locked": false - }, - { - "id": "OlaiVvlhdGvnYHx9Km9Qv", - "type": "text", - "x": 1057.6092781342352, - "y": 3498.5812810878424, - "width": 55.21995544433594, - "height": 25, - "angle": 0, - "strokeColor": "#1e1e1e", - "backgroundColor": "transparent", - "fillStyle": "solid", - "strokeWidth": 2, - "strokeStyle": "solid", - "roughness": 1, - "opacity": 100, - "groupIds": [], - "frameId": null, - "index": "b2o", - "roundness": null, - "seed": 2094399297, - "version": 1274, - "versionNonce": 1657497347, - "isDeleted": false, - "boundElements": [], - "updated": 1766632678507, - "link": null, - "locked": false, - "text": "GPFS", - "fontSize": 20, - "fontFamily": 5, - "textAlign": "center", - "verticalAlign": "middle", - "containerId": "YMHES-uetzrMEDwFkCHdH", - "originalText": "GPFS", - "autoResize": true, - "lineHeight": 1.25 - }, { "id": "py2gWjP2fsxBZN-9ttfBh", "type": "rectangle", - "x": 1058.9340084182502, - "y": 4150.114846216833, + "x": 1080.7158973597411, + "y": 5309.476392482113, "width": 78.84661364258261, "height": 239.8912269868912, "angle": 0, @@ -8261,8 +6522,8 @@ "index": "b2p", "roundness": null, "seed": 105076673, - "version": 1253, - "versionNonce": 1238206881, + "version": 1344, + "versionNonce": 854339171, "isDeleted": false, "boundElements": [ { @@ -8270,15 +6531,15 @@ "id": "5tfhe4hwG92sJ3lHY77pG" } ], - "updated": 1766549517790, + "updated": 1767580076534, "link": null, "locked": false }, { "id": "5tfhe4hwG92sJ3lHY77pG", "type": "text", - "x": 1070.7473375173736, - "y": 4257.560459710278, + "x": 1092.5292264588647, + "y": 5416.922005975558, "width": 55.21995544433594, "height": 25, "angle": 0, @@ -8294,11 +6555,11 @@ "index": "b2q", "roundness": null, "seed": 1717876641, - "version": 1270, - "versionNonce": 1662877741, + "version": 1361, + "versionNonce": 1455319555, "isDeleted": false, "boundElements": [], - "updated": 1766632682521, + "updated": 1767580076534, "link": null, "locked": false, "text": "GPFS", @@ -8725,6 +6986,3869 @@ "startArrowhead": null, "endArrowhead": "arrow", "elbowed": false + }, + { + "id": "Bq_0q7B5_bYjojECcPofk", + "type": "rectangle", + "x": 162.52212001415893, + "y": 2569.0105125800087, + "width": 647.3203086953981, + "height": 541.9127073088301, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2y", + "roundness": { + "type": 3 + }, + "seed": 1738026701, + "version": 795, + "versionNonce": 304617965, + "isDeleted": false, + "boundElements": [], + "updated": 1767579964783, + "link": null, + "locked": false + }, + { + "id": "roCJxNYJCfJBTojl34uS9", + "type": "rectangle", + "x": 368.17565014510393, + "y": 2749.6771690741493, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b2z", + "roundness": null, + "seed": 396545325, + "version": 544, + "versionNonce": 1924172387, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "5qQ3sPG-4rdSwLjfMvNBb" + }, + { + "id": "ab64chV82iAi9ohRHllAw", + "type": "arrow" + }, + { + "id": "Fbi6WgLMToDz_YcJHD1-4", + "type": "arrow" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "5qQ3sPG-4rdSwLjfMvNBb", + "type": "text", + "x": 389.11901714949846, + "y": 2767.1771690741493, + "width": 114.77995300292969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b30", + "roundness": null, + "seed": 2106236813, + "version": 535, + "versionNonce": 1435977219, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "ray job tool\n(ray client)", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "roCJxNYJCfJBTojl34uS9", + "originalText": "ray job tool\n(ray client)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "9Y8GQT_WwcqR54CpPj3b4", + "type": "rectangle", + "x": 613.1756501451039, + "y": 2749.1771690741493, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b31", + "roundness": null, + "seed": 1334388205, + "version": 681, + "versionNonce": 1789051107, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "qdUsisM9pTt_1BiqB5FPm" + }, + { + "id": "ab64chV82iAi9ohRHllAw", + "type": "arrow" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "qdUsisM9pTt_1BiqB5FPm", + "type": "text", + "x": 624.0590501084828, + "y": 2766.6771690741493, + "width": 134.89988708496094, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b32", + "roundness": null, + "seed": 1084743757, + "version": 685, + "versionNonce": 788442243, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "VerlTaskSpec \nyaml", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "9Y8GQT_WwcqR54CpPj3b4", + "originalText": "VerlTaskSpec \nyaml", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ab64chV82iAi9ohRHllAw", + "type": "arrow", + "x": 614.5089936509632, + "y": 2787.6771690741493, + "width": 90.33331298828125, + "height": 4, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b33", + "roundness": { + "type": 2 + }, + "seed": 1723879085, + "version": 1351, + "versionNonce": 1905876493, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868974, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -90.33331298828125, + 4 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "9Y8GQT_WwcqR54CpPj3b4", + "focus": 0.16118805172017045, + "gap": 1.333343505859375 + }, + "endBinding": { + "elementId": "roCJxNYJCfJBTojl34uS9", + "focus": 0.06393742185081701, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "Hz-edBJF0k8DaHhTL77rC", + "type": "text", + "x": 168.45183342388265, + "y": 2504.440205385617, + "width": 663.6594848632812, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b34", + "roundness": null, + "seed": 1618735373, + "version": 879, + "versionNonce": 1897116397, + "isDeleted": false, + "boundElements": [], + "updated": 1767580197637, + "link": null, + "locked": false, + "text": "v3.6 Weight & bias (share user account, individual project for user) ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "v3.6 Weight & bias (share user account, individual project for user) ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ujGw7lf_sdda3LV-WNX2f", + "type": "rectangle", + "x": 553.9995982182779, + "y": 2591.8437645331337, + "width": 210.1763265850289, + "height": 55.000000000000014, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b35", + "roundness": null, + "seed": 995379053, + "version": 833, + "versionNonce": 1748947715, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "cDgcky47ylIEJcA_i0rtZ" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "cDgcky47ylIEJcA_i0rtZ", + "type": "text", + "x": 606.237801183644, + "y": 2606.8437645331337, + "width": 105.69992065429688, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b36", + "roundness": null, + "seed": 429052365, + "version": 872, + "versionNonce": 1603500707, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "API server", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ujGw7lf_sdda3LV-WNX2f", + "originalText": "API server", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "rLq2UhchXubdrBSBGfEsH", + "type": "rectangle", + "x": 368.0089936509633, + "y": 2662.8437645331337, + "width": 154.6436510018092, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b37", + "roundness": null, + "seed": 407965741, + "version": 1063, + "versionNonce": 1093807683, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "N-xeGnbrBx7DNjp16jVLB" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "N-xeGnbrBx7DNjp16jVLB", + "type": "text", + "x": 384.21086218165306, + "y": 2667.8437645331337, + "width": 122.23991394042969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b38", + "roundness": null, + "seed": 1970832013, + "version": 1122, + "versionNonce": 1341746659, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "task\nmanagement ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "rLq2UhchXubdrBSBGfEsH", + "originalText": "task management ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "fikW3kqRKF_8F9KRvq_xo", + "type": "rectangle", + "x": 552.116532236633, + "y": 2663.418072845848, + "width": 207.59447102997498, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b39", + "roundness": null, + "seed": 1975691501, + "version": 1154, + "versionNonce": 1861728643, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "wjxoMONmstqEZIgFo-GAO" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "wjxoMONmstqEZIgFo-GAO", + "type": "text", + "x": 568.3838376368744, + "y": 2668.418072845848, + "width": 175.0598602294922, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3A", + "roundness": null, + "seed": 179897165, + "version": 1244, + "versionNonce": 460034339, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "node management\n(ssh, ray cluster) ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "fikW3kqRKF_8F9KRvq_xo", + "originalText": "node management\n(ssh, ray cluster) ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Xp3E2mlrzvEYzPOjSCrcs", + "type": "rectangle", + "x": 888.3153373725095, + "y": 2744.791796938897, + "width": 163.1357446724801, + "height": 106.45708709635272, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3B", + "roundness": null, + "seed": 951390637, + "version": 451, + "versionNonce": 650343619, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "tGEUStBUZGhYlFlEgPiNG" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "tGEUStBUZGhYlFlEgPiNG", + "type": "text", + "x": 919.2332539592378, + "y": 2773.020340487073, + "width": 101.29991149902344, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3C", + "roundness": null, + "seed": 1021334541, + "version": 513, + "versionNonce": 815653987, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "ray worker\nnode", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Xp3E2mlrzvEYzPOjSCrcs", + "originalText": "ray worker node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "jDwEHqyyCIFxDq3p9qWt2", + "type": "rectangle", + "x": 890.2644480420386, + "y": 2608.3540352013147, + "width": 163.1357446724801, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3D", + "roundness": null, + "seed": 1932806765, + "version": 500, + "versionNonce": 1623810051, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "uQ36HzOihakgxzEL6U7lT" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "uQ36HzOihakgxzEL6U7lT", + "type": "text", + "x": 921.1823646287669, + "y": 2625.8540352013147, + "width": 101.29991149902344, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3E", + "roundness": null, + "seed": 2088400077, + "version": 566, + "versionNonce": 1631872931, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "ray worker\nnode", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "jDwEHqyyCIFxDq3p9qWt2", + "originalText": "ray worker node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "3UlVJoxmTIuxD5GDOcfHG", + "type": "rectangle", + "x": 370.8145031170506, + "y": 2589.6308718673913, + "width": 147.4799234681481, + "height": 55.000000000000014, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3F", + "roundness": null, + "seed": 608981805, + "version": 951, + "versionNonce": 406682435, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "HoBGie1pN_WgFNHpbYmd2" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "HoBGie1pN_WgFNHpbYmd2", + "type": "text", + "x": 413.32449201176917, + "y": 2604.6308718673913, + "width": 62.45994567871094, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3G", + "roundness": null, + "seed": 118711693, + "version": 1000, + "versionNonce": 571677411, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "WebUI", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "3UlVJoxmTIuxD5GDOcfHG", + "originalText": "WebUI", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "JeKebUMNJV9v7B5fePXsk", + "type": "rectangle", + "x": 614.4862848780233, + "y": 2864.7262618838045, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3H", + "roundness": null, + "seed": 2013866989, + "version": 733, + "versionNonce": 1890998755, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "d2yX6cU1iiTalDcCD-3Cd" + }, + { + "id": "Fbi6WgLMToDz_YcJHD1-4", + "type": "arrow" + } + ], + "updated": 1767579980740, + "link": null, + "locked": false + }, + { + "id": "d2yX6cU1iiTalDcCD-3Cd", + "type": "text", + "x": 625.3696848414022, + "y": 2882.2262618838045, + "width": 134.89988708496094, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3I", + "roundness": null, + "seed": 657925709, + "version": 785, + "versionNonce": 1190913859, + "isDeleted": false, + "boundElements": [], + "updated": 1767579987889, + "link": null, + "locked": false, + "text": "Advanced\nVerlTaskSpec ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "JeKebUMNJV9v7B5fePXsk", + "originalText": "Advanced VerlTaskSpec ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Fbi6WgLMToDz_YcJHD1-4", + "type": "arrow", + "x": 614.0224780811068, + "y": 2909.0912543980044, + "width": 80.66214281697523, + "height": 84.85848269127973, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3J", + "roundness": { + "type": 2 + }, + "seed": 125228205, + "version": 198, + "versionNonce": 1777925837, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868974, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -80.66214281697523, + -84.85848269127973 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "JeKebUMNJV9v7B5fePXsk", + "focus": -0.678588231473157, + "gap": 1 + }, + "endBinding": { + "elementId": "roCJxNYJCfJBTojl34uS9", + "focus": -0.4748599898404835, + "gap": 8.517998107308927 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "VaoTGDERjqOoJC3xlNTUv", + "type": "rectangle", + "x": 185.61045457092558, + "y": 2662.7487931693854, + "width": 154.6436510018092, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3K", + "roundness": null, + "seed": 1953609485, + "version": 1125, + "versionNonce": 1815538947, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "wT9Wed9lIgCjhhhjiCtO5" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "wT9Wed9lIgCjhhhjiCtO5", + "type": "text", + "x": 201.81232310161533, + "y": 2667.7487931693854, + "width": 122.23991394042969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3L", + "roundness": null, + "seed": 413224301, + "version": 1193, + "versionNonce": 561534115, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "user\nmanagement ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "VaoTGDERjqOoJC3xlNTUv", + "originalText": "user management ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "nnVAd0OkVxsNSu7naL8r2", + "type": "rectangle", + "x": 191.67185098984265, + "y": 2856.333624822138, + "width": 154.6436510018092, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3M", + "roundness": null, + "seed": 323950541, + "version": 1357, + "versionNonce": 1701562435, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "IKkiq8hE1Wfbrpe0ab19q" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "IKkiq8hE1Wfbrpe0ab19q", + "type": "text", + "x": 211.8737195205324, + "y": 2861.333624822138, + "width": 114.23991394042969, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3N", + "roundness": null, + "seed": 1586796077, + "version": 1439, + "versionNonce": 770688995, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "data\nmanagement\nSFTPGo ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "nnVAd0OkVxsNSu7naL8r2", + "originalText": "data management\nSFTPGo ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "OyMuyqupt4CsAwM8flms1", + "type": "rectangle", + "x": 1063.3889292276112, + "y": 2631.083373332381, + "width": 78.84661364258261, + "height": 188.34936741723558, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3Q", + "roundness": null, + "seed": 421677389, + "version": 1228, + "versionNonce": 476057283, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "5GgEcr0L_twORhDdDfAe7" + } + ], + "updated": 1767579868399, + "link": null, + "locked": false + }, + { + "id": "5GgEcr0L_twORhDdDfAe7", + "type": "text", + "x": 1075.2022583267346, + "y": 2712.758057040999, + "width": 55.21995544433594, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3R", + "roundness": null, + "seed": 1980077997, + "version": 1245, + "versionNonce": 999767651, + "isDeleted": false, + "boundElements": [], + "updated": 1767579868399, + "link": null, + "locked": false, + "text": "GPFS", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "OyMuyqupt4CsAwM8flms1", + "originalText": "GPFS", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "dzrZoVfIpG4vit_qCmGQh", + "type": "rectangle", + "x": 196.58168960453048, + "y": 2979.697507510093, + "width": 566.5404057062265, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3S", + "roundness": null, + "seed": 1552901389, + "version": 1800, + "versionNonce": 783584717, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "B9vVqUUh1aDRKtp98kVFT" + } + ], + "updated": 1767579996061, + "link": null, + "locked": false + }, + { + "id": "B9vVqUUh1aDRKtp98kVFT", + "type": "text", + "x": 414.00194738928434, + "y": 2997.197507510093, + "width": 131.69989013671875, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3T", + "roundness": null, + "seed": 1289304941, + "version": 1927, + "versionNonce": 433802285, + "isDeleted": false, + "boundElements": [], + "updated": 1767579996061, + "link": null, + "locked": false, + "text": "weight & bias", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dzrZoVfIpG4vit_qCmGQh", + "originalText": "weight & bias", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "qg8jFPFtp50OAJT2ab_YO", + "type": "rectangle", + "x": 187.2240309153068, + "y": 3838.839430576088, + "width": 647.3203086953981, + "height": 541.9127073088301, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3U", + "roundness": { + "type": 3 + }, + "seed": 253245411, + "version": 1077, + "versionNonce": 1966178979, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "ej_5uPkV3roLWgrNmRVQJ", + "type": "rectangle", + "x": 392.8775610462518, + "y": 4019.5060870702287, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3V", + "roundness": null, + "seed": 866993027, + "version": 826, + "versionNonce": 97465923, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "5J21ZHZTHpGu78H0oBGne" + }, + { + "id": "dCW4muSHBOjk2yt4VP-8M", + "type": "arrow" + }, + { + "id": "mfel3yZDZK2O4BoiB9bfp", + "type": "arrow" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "5J21ZHZTHpGu78H0oBGne", + "type": "text", + "x": 413.82092805064633, + "y": 4037.0060870702287, + "width": 114.77995300292969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3W", + "roundness": null, + "seed": 81779491, + "version": 817, + "versionNonce": 196441571, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "ray job tool\n(ray client)", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ej_5uPkV3roLWgrNmRVQJ", + "originalText": "ray job tool\n(ray client)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "UJvTvQ4khDdrdYsxDaKt0", + "type": "rectangle", + "x": 637.8775610462517, + "y": 4019.0060870702287, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3X", + "roundness": null, + "seed": 790527683, + "version": 963, + "versionNonce": 1352432835, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "0hvq7uN4KgQ1e0xT7DKVG" + }, + { + "id": "dCW4muSHBOjk2yt4VP-8M", + "type": "arrow" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "0hvq7uN4KgQ1e0xT7DKVG", + "type": "text", + "x": 648.7609610096306, + "y": 4036.5060870702287, + "width": 134.89988708496094, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3Y", + "roundness": null, + "seed": 1292349027, + "version": 967, + "versionNonce": 1242950755, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "VerlTaskSpec \nyaml", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UJvTvQ4khDdrdYsxDaKt0", + "originalText": "VerlTaskSpec \nyaml", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "dCW4muSHBOjk2yt4VP-8M", + "type": "arrow", + "x": 639.2109045521111, + "y": 4057.5060870702287, + "width": 90.33331298828125, + "height": 4, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3Z", + "roundness": { + "type": 2 + }, + "seed": 560589315, + "version": 2197, + "versionNonce": 803445773, + "isDeleted": false, + "boundElements": [], + "updated": 1767580422054, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -90.33331298828125, + 4 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "UJvTvQ4khDdrdYsxDaKt0", + "focus": 0.16118805172016018, + "gap": 1.333343505859375 + }, + "endBinding": { + "elementId": "ej_5uPkV3roLWgrNmRVQJ", + "focus": 0.06393742185082936, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "oSLr5BRtyqrLyySPfeO9j", + "type": "text", + "x": 225.14633005688438, + "y": 3790.8170038464273, + "width": 476.19970703125, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3a", + "roundness": null, + "seed": 289649059, + "version": 1167, + "versionNonce": 626310957, + "isDeleted": false, + "boundElements": [], + "updated": 1767580447801, + "link": null, + "locked": false, + "text": "v3.8 IB & Roce support for multi node training ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "v3.8 IB & Roce support for multi node training ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ia_9sG-dZPebY0HaRl5mG", + "type": "rectangle", + "x": 578.7015091194257, + "y": 3861.672682529213, + "width": 210.1763265850289, + "height": 55.000000000000014, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3b", + "roundness": null, + "seed": 1573094723, + "version": 1115, + "versionNonce": 1531388643, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "LsfI3OipT-Y5Jx7jpcccf" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "LsfI3OipT-Y5Jx7jpcccf", + "type": "text", + "x": 630.9397120847918, + "y": 3876.672682529213, + "width": 105.69992065429688, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3c", + "roundness": null, + "seed": 508028131, + "version": 1155, + "versionNonce": 281464451, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "API server", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ia_9sG-dZPebY0HaRl5mG", + "originalText": "API server", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "xrABjxBFNPVtYSJ6ZPiSh", + "type": "rectangle", + "x": 392.7109045521112, + "y": 3932.672682529213, + "width": 154.6436510018092, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3d", + "roundness": null, + "seed": 2053066883, + "version": 1345, + "versionNonce": 1016071715, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "-Kr5CotillYBlIuQsHPGW" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "-Kr5CotillYBlIuQsHPGW", + "type": "text", + "x": 408.9127730828009, + "y": 3937.672682529213, + "width": 122.23991394042969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3e", + "roundness": null, + "seed": 600328227, + "version": 1404, + "versionNonce": 1972455875, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "task\nmanagement ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "xrABjxBFNPVtYSJ6ZPiSh", + "originalText": "task management ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "Shkqjthowq7IUHYuPuzXr", + "type": "rectangle", + "x": 576.8184431377808, + "y": 3933.2469908419275, + "width": 207.59447102997498, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3f", + "roundness": null, + "seed": 275860419, + "version": 1436, + "versionNonce": 1894324579, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "NUOv1Sok4Fz4O-Jom4drR" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "NUOv1Sok4Fz4O-Jom4drR", + "type": "text", + "x": 593.0857485380222, + "y": 3938.2469908419275, + "width": 175.0598602294922, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3g", + "roundness": null, + "seed": 1954077539, + "version": 1526, + "versionNonce": 1004926211, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "node management\n(ssh, ray cluster) ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "Shkqjthowq7IUHYuPuzXr", + "originalText": "node management\n(ssh, ray cluster) ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "rdU6p3LbZpLNIPDuhEU6z", + "type": "rectangle", + "x": 913.0172482736575, + "y": 4014.620714934976, + "width": 163.1357446724801, + "height": 106.45708709635272, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3h", + "roundness": null, + "seed": 2083354371, + "version": 733, + "versionNonce": 906386595, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "fyUCxFxmJICAq48idEvPj" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "fyUCxFxmJICAq48idEvPj", + "type": "text", + "x": 943.9351648603858, + "y": 4042.8492584831524, + "width": 101.29991149902344, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3i", + "roundness": null, + "seed": 1175297699, + "version": 795, + "versionNonce": 1257820227, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "ray worker\nnode", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "rdU6p3LbZpLNIPDuhEU6z", + "originalText": "ray worker node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "cl1dzmeCwwz4SXuZrad2a", + "type": "rectangle", + "x": 914.9663589431866, + "y": 3878.182953197394, + "width": 163.1357446724801, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3j", + "roundness": null, + "seed": 482857539, + "version": 782, + "versionNonce": 1822150627, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "fPAvr_57exrKFQWCnW2mb" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "fPAvr_57exrKFQWCnW2mb", + "type": "text", + "x": 945.8842755299149, + "y": 3895.682953197394, + "width": 101.29991149902344, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3k", + "roundness": null, + "seed": 1899663843, + "version": 848, + "versionNonce": 1291705219, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "ray worker\nnode", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "cl1dzmeCwwz4SXuZrad2a", + "originalText": "ray worker node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ReF6vnI-dB3lXIMd4AvGs", + "type": "rectangle", + "x": 395.5164140181985, + "y": 3859.4597898634706, + "width": 147.4799234681481, + "height": 55.000000000000014, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3l", + "roundness": null, + "seed": 343170435, + "version": 1233, + "versionNonce": 1482519331, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "5tqRv0HXQ949u10Kw2eiU" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "5tqRv0HXQ949u10Kw2eiU", + "type": "text", + "x": 438.02640291291704, + "y": 3874.4597898634706, + "width": 62.45994567871094, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3m", + "roundness": null, + "seed": 833860899, + "version": 1282, + "versionNonce": 1864963779, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "WebUI", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ReF6vnI-dB3lXIMd4AvGs", + "originalText": "WebUI", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "QFS8OSVEFsg_hE5ngbZ3q", + "type": "rectangle", + "x": 639.1881957791711, + "y": 4134.555179879884, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3n", + "roundness": null, + "seed": 140974275, + "version": 1015, + "versionNonce": 1610565219, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "vd21IfNtOA5knKWzVf_sk" + }, + { + "id": "mfel3yZDZK2O4BoiB9bfp", + "type": "arrow" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "vd21IfNtOA5knKWzVf_sk", + "type": "text", + "x": 650.07159574255, + "y": 4152.055179879884, + "width": 134.89988708496094, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3o", + "roundness": null, + "seed": 1714227299, + "version": 1067, + "versionNonce": 565084675, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "Advanced\nVerlTaskSpec ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "QFS8OSVEFsg_hE5ngbZ3q", + "originalText": "Advanced VerlTaskSpec ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "mfel3yZDZK2O4BoiB9bfp", + "type": "arrow", + "x": 638.7243889822546, + "y": 4178.920172394084, + "width": 80.66214281697523, + "height": 84.85848269127973, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3p", + "roundness": { + "type": 2 + }, + "seed": 1624507395, + "version": 1044, + "versionNonce": 1833680077, + "isDeleted": false, + "boundElements": [], + "updated": 1767580422054, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -80.66214281697523, + -84.85848269127973 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "QFS8OSVEFsg_hE5ngbZ3q", + "focus": -0.6785882314731545, + "gap": 1 + }, + "endBinding": { + "elementId": "ej_5uPkV3roLWgrNmRVQJ", + "focus": -0.47485998984048416, + "gap": 8.517998107308813 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "aWTI7cnR7QatcqJUTZEkH", + "type": "rectangle", + "x": 210.31236547207345, + "y": 3932.5777111654647, + "width": 154.6436510018092, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3q", + "roundness": null, + "seed": 1348111267, + "version": 1407, + "versionNonce": 213089507, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "NDAe6kDHeZy_G-F24BbSA" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "NDAe6kDHeZy_G-F24BbSA", + "type": "text", + "x": 226.5142340027632, + "y": 3937.5777111654647, + "width": 122.23991394042969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3r", + "roundness": null, + "seed": 149888835, + "version": 1475, + "versionNonce": 1201137795, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "user\nmanagement ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aWTI7cnR7QatcqJUTZEkH", + "originalText": "user management ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "6DIoCPbBjmqXhdWQiF-Ds", + "type": "rectangle", + "x": 216.37376189099052, + "y": 4126.162542818218, + "width": 154.6436510018092, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3s", + "roundness": null, + "seed": 1425030883, + "version": 1639, + "versionNonce": 1848500259, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "ZMAp89RSyrH2uBqKYd6xX" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "ZMAp89RSyrH2uBqKYd6xX", + "type": "text", + "x": 236.57563042168027, + "y": 4131.162542818218, + "width": 114.23991394042969, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3t", + "roundness": null, + "seed": 787245699, + "version": 1721, + "versionNonce": 711189443, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "data\nmanagement\nSFTPGo ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6DIoCPbBjmqXhdWQiF-Ds", + "originalText": "data management\nSFTPGo ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "i9kRvyS1BKdIOzkDPwoS0", + "type": "rectangle", + "x": 1088.0908401287593, + "y": 3900.9122913284605, + "width": 78.84661364258261, + "height": 188.34936741723558, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3u", + "roundness": null, + "seed": 1000481315, + "version": 1510, + "versionNonce": 1683518307, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "j1I0PKrtvzLMy6fsxx_6a" + } + ], + "updated": 1767580421422, + "link": null, + "locked": false + }, + { + "id": "j1I0PKrtvzLMy6fsxx_6a", + "type": "text", + "x": 1099.9041692278827, + "y": 3982.5869750370784, + "width": 55.21995544433594, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3v", + "roundness": null, + "seed": 67102147, + "version": 1528, + "versionNonce": 2086276867, + "isDeleted": false, + "boundElements": [], + "updated": 1767580421422, + "link": null, + "locked": false, + "text": "GPFS", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "i9kRvyS1BKdIOzkDPwoS0", + "originalText": "GPFS", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "mSWRJ8RKPV7XfoaSxNjz8", + "type": "rectangle", + "x": 221.28360050567835, + "y": 4249.526425506172, + "width": 239.9955982022902, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3w", + "roundness": null, + "seed": 1864643939, + "version": 2128, + "versionNonce": 688984067, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "39BgAR4RVEZm9MjMi7RPJ" + } + ], + "updated": 1767580500208, + "link": null, + "locked": false + }, + { + "id": "39BgAR4RVEZm9MjMi7RPJ", + "type": "text", + "x": 275.4314545384641, + "y": 4267.026425506172, + "width": 131.69989013671875, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b3x", + "roundness": null, + "seed": 2086506755, + "version": 2253, + "versionNonce": 622204835, + "isDeleted": false, + "boundElements": [], + "updated": 1767580500209, + "link": null, + "locked": false, + "text": "weight & bias", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "mSWRJ8RKPV7XfoaSxNjz8", + "originalText": "weight & bias", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "_dawvRGHc8iIWvtZgQ8my", + "type": "rectangle", + "x": 187.1078063236842, + "y": 3211.040007039422, + "width": 647.3203086953981, + "height": 541.9127073088301, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b40", + "roundness": { + "type": 3 + }, + "seed": 258563459, + "version": 1139, + "versionNonce": 987772547, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "kmSdgT4RJDyvWvTuvvqWh", + "type": "rectangle", + "x": 392.76133645462926, + "y": 3391.7066635335627, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b41", + "roundness": null, + "seed": 101233955, + "version": 888, + "versionNonce": 792489507, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "9LC-U-4w0PKn4xd2rIb9l" + }, + { + "id": "EeKOp5xHmmSFZ6hZb9yAJ", + "type": "arrow" + }, + { + "id": "V3YTAKkdaz8uWYHzMw2Is", + "type": "arrow" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "9LC-U-4w0PKn4xd2rIb9l", + "type": "text", + "x": 413.7047034590238, + "y": 3409.2066635335623, + "width": 114.77995300292969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b42", + "roundness": null, + "seed": 169613507, + "version": 879, + "versionNonce": 644919747, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "ray job tool\n(ray client)", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kmSdgT4RJDyvWvTuvvqWh", + "originalText": "ray job tool\n(ray client)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "2Sw17fUgWD30F3Y9a0g9G", + "type": "rectangle", + "x": 637.761336454629, + "y": 3391.2066635335627, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b43", + "roundness": null, + "seed": 1765935203, + "version": 1025, + "versionNonce": 1530840227, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Zsm4APcDK69CbNQ3-ADwB" + }, + { + "id": "EeKOp5xHmmSFZ6hZb9yAJ", + "type": "arrow" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "Zsm4APcDK69CbNQ3-ADwB", + "type": "text", + "x": 648.6447364180079, + "y": 3408.7066635335623, + "width": 134.89988708496094, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b44", + "roundness": null, + "seed": 961086467, + "version": 1029, + "versionNonce": 2095331395, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "VerlTaskSpec \nyaml", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "2Sw17fUgWD30F3Y9a0g9G", + "originalText": "VerlTaskSpec \nyaml", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "EeKOp5xHmmSFZ6hZb9yAJ", + "type": "arrow", + "x": 639.0946799604884, + "y": 3429.7066635335623, + "width": 90.33331298828125, + "height": 4, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b45", + "roundness": { + "type": 2 + }, + "seed": 1378847651, + "version": 2379, + "versionNonce": 2060224077, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434877, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -90.33331298828125, + 4 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "2Sw17fUgWD30F3Y9a0g9G", + "focus": 0.16118805172019085, + "gap": 1.333343505859375 + }, + "endBinding": { + "elementId": "kmSdgT4RJDyvWvTuvvqWh", + "focus": 0.06393742185080617, + "gap": 1 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "w6vHd2GaQE1AUKsLdmkEj", + "type": "text", + "x": 193.03751973340792, + "y": 3146.4696998450304, + "width": 184.7998809814453, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b46", + "roundness": null, + "seed": 136585027, + "version": 1220, + "versionNonce": 22919299, + "isDeleted": false, + "boundElements": [], + "updated": 1767580456184, + "link": null, + "locked": false, + "text": "v3.7 Model serving ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "left", + "verticalAlign": "top", + "containerId": null, + "originalText": "v3.7 Model serving ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "kuAX3XgJjBpEQ_Wknjhho", + "type": "rectangle", + "x": 578.5852845278031, + "y": 3233.873258992547, + "width": 210.1763265850289, + "height": 55.000000000000014, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b47", + "roundness": null, + "seed": 629489379, + "version": 1177, + "versionNonce": 1008762563, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "rqqvX9jF-OsKMfh49qlrP" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "rqqvX9jF-OsKMfh49qlrP", + "type": "text", + "x": 630.8234874931691, + "y": 3248.873258992547, + "width": 105.69992065429688, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b48", + "roundness": null, + "seed": 618752643, + "version": 1218, + "versionNonce": 1774825059, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "API server", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "kuAX3XgJjBpEQ_Wknjhho", + "originalText": "API server", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "WDXLavd7W_KflvWaXFjOe", + "type": "rectangle", + "x": 392.59467996048863, + "y": 3304.873258992547, + "width": 154.6436510018092, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b49", + "roundness": null, + "seed": 720839203, + "version": 1407, + "versionNonce": 1441524227, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "_gZRspPoTlhFqDjthreky" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "_gZRspPoTlhFqDjthreky", + "type": "text", + "x": 408.7965484911784, + "y": 3309.873258992547, + "width": 122.23991394042969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4A", + "roundness": null, + "seed": 1085597123, + "version": 1467, + "versionNonce": 1703260579, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "task\nmanagement ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "WDXLavd7W_KflvWaXFjOe", + "originalText": "task management ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ykIjs9C9gh53emDs_NUZH", + "type": "rectangle", + "x": 576.7022185461582, + "y": 3305.4475673052616, + "width": 207.59447102997498, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4B", + "roundness": null, + "seed": 1295594851, + "version": 1498, + "versionNonce": 1506116931, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "VjawvB3ULBdJHaDf5SUHY" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "VjawvB3ULBdJHaDf5SUHY", + "type": "text", + "x": 592.9695239463996, + "y": 3310.4475673052616, + "width": 175.0598602294922, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4C", + "roundness": null, + "seed": 1339199747, + "version": 1588, + "versionNonce": 1242162403, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "node management\n(ssh, ray cluster) ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ykIjs9C9gh53emDs_NUZH", + "originalText": "node management\n(ssh, ray cluster) ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ztzSxxw4gFHtLebqPFwq_", + "type": "rectangle", + "x": 912.9010236820349, + "y": 3386.821291398311, + "width": 163.1357446724801, + "height": 106.45708709635272, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4D", + "roundness": null, + "seed": 1509914787, + "version": 795, + "versionNonce": 1575047299, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "idEPabW5S1LZD69d85ve1" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "idEPabW5S1LZD69d85ve1", + "type": "text", + "x": 943.8189402687632, + "y": 3415.049834946488, + "width": 101.29991149902344, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4E", + "roundness": null, + "seed": 1231973443, + "version": 858, + "versionNonce": 1191856163, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "ray worker\nnode", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ztzSxxw4gFHtLebqPFwq_", + "originalText": "ray worker node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "UhDaYiDTmw9wnsmkDqwgD", + "type": "rectangle", + "x": 914.850134351564, + "y": 3250.383529660728, + "width": 163.1357446724801, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4F", + "roundness": null, + "seed": 1107160035, + "version": 844, + "versionNonce": 2071153603, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "stn0JrExRn0tfK-9wqEh2" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "stn0JrExRn0tfK-9wqEh2", + "type": "text", + "x": 945.7680509382923, + "y": 3267.883529660728, + "width": 101.29991149902344, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4G", + "roundness": null, + "seed": 1740404611, + "version": 910, + "versionNonce": 792386403, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "ray worker\nnode", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "UhDaYiDTmw9wnsmkDqwgD", + "originalText": "ray worker node", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "rQ04gFE1LDliFuaK5keJ1", + "type": "rectangle", + "x": 395.4001894265758, + "y": 3231.6603663268047, + "width": 147.4799234681481, + "height": 55.000000000000014, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4H", + "roundness": null, + "seed": 1491636003, + "version": 1295, + "versionNonce": 246380291, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "I2Jxhc9ykHruyGsuYtMS7" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "I2Jxhc9ykHruyGsuYtMS7", + "type": "text", + "x": 437.9101783212944, + "y": 3246.6603663268047, + "width": 62.45994567871094, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4I", + "roundness": null, + "seed": 1743827651, + "version": 1345, + "versionNonce": 944742051, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "WebUI", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "rQ04gFE1LDliFuaK5keJ1", + "originalText": "WebUI", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "oX4bw99MJwmfVPdk1HTlT", + "type": "rectangle", + "x": 639.0719711875485, + "y": 3506.755756343219, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4J", + "roundness": null, + "seed": 359883363, + "version": 1077, + "versionNonce": 1992938051, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "szDN6jsgPmcwwvSmvoxTV" + }, + { + "id": "V3YTAKkdaz8uWYHzMw2Is", + "type": "arrow" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "szDN6jsgPmcwwvSmvoxTV", + "type": "text", + "x": 649.9553711509274, + "y": 3524.255756343219, + "width": 134.89988708496094, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4K", + "roundness": null, + "seed": 1245650435, + "version": 1129, + "versionNonce": 989630947, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "Advanced\nVerlTaskSpec ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "oX4bw99MJwmfVPdk1HTlT", + "originalText": "Advanced VerlTaskSpec ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "V3YTAKkdaz8uWYHzMw2Is", + "type": "arrow", + "x": 638.6081643906319, + "y": 3551.120748857419, + "width": 80.66214281697523, + "height": 84.85848269127973, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4L", + "roundness": { + "type": 2 + }, + "seed": 1364354467, + "version": 1226, + "versionNonce": 707729165, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434877, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -80.66214281697523, + -84.85848269127973 + ] + ], + "lastCommittedPoint": null, + "startBinding": { + "elementId": "oX4bw99MJwmfVPdk1HTlT", + "focus": -0.6785882314731589, + "gap": 1 + }, + "endBinding": { + "elementId": "kmSdgT4RJDyvWvTuvvqWh", + "focus": -0.4748599898404784, + "gap": 8.5179981073087 + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false + }, + { + "id": "PuOZm640dpTGF1OLq4i5m", + "type": "rectangle", + "x": 210.19614088045086, + "y": 3304.7782876287997, + "width": 154.6436510018092, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4M", + "roundness": null, + "seed": 299612483, + "version": 1469, + "versionNonce": 1365842115, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "m44OLDjbhsnwUR7zhLVdZ" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "m44OLDjbhsnwUR7zhLVdZ", + "type": "text", + "x": 226.3980094111406, + "y": 3309.7782876287997, + "width": 122.23991394042969, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4N", + "roundness": null, + "seed": 154769635, + "version": 1537, + "versionNonce": 1055852643, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "user\nmanagement ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PuOZm640dpTGF1OLq4i5m", + "originalText": "user management ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "gDplQ9gnXyCTd6esC3gII", + "type": "rectangle", + "x": 216.25753729936793, + "y": 3498.363119281553, + "width": 154.6436510018092, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4O", + "roundness": null, + "seed": 83526787, + "version": 1701, + "versionNonce": 1364591619, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "rX3wPPrl8npHWdRBFiXUD" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "rX3wPPrl8npHWdRBFiXUD", + "type": "text", + "x": 236.45940583005768, + "y": 3503.363119281553, + "width": 114.23991394042969, + "height": 75, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4P", + "roundness": null, + "seed": 484244515, + "version": 1783, + "versionNonce": 1620872099, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "data\nmanagement\nSFTPGo ", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "gDplQ9gnXyCTd6esC3gII", + "originalText": "data management\nSFTPGo ", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "uN1gn-apNvTwBEgExTqEC", + "type": "rectangle", + "x": 1087.9746155371367, + "y": 3273.1128677917945, + "width": 78.84661364258261, + "height": 188.34936741723558, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4Q", + "roundness": null, + "seed": 1825537987, + "version": 1572, + "versionNonce": 1089544003, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "M7vFjbFzjwepVauApsUxl" + } + ], + "updated": 1767580434170, + "link": null, + "locked": false + }, + { + "id": "M7vFjbFzjwepVauApsUxl", + "type": "text", + "x": 1099.7879446362601, + "y": 3354.7875515004125, + "width": 55.21995544433594, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4R", + "roundness": null, + "seed": 250646371, + "version": 1591, + "versionNonce": 1018314467, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "GPFS", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "uN1gn-apNvTwBEgExTqEC", + "originalText": "GPFS", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "aLz6JA4VKycwGE6wJ7aIt", + "type": "rectangle", + "x": 221.16737591405575, + "y": 3621.727001969507, + "width": 566.5404057062265, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4S", + "roundness": null, + "seed": 1435413251, + "version": 2145, + "versionNonce": 444077677, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "WnVdMvE92zydRawmASZ06" + } + ], + "updated": 1767580482033, + "link": null, + "locked": false + }, + { + "id": "WnVdMvE92zydRawmASZ06", + "type": "text", + "x": 438.5876336988096, + "y": 3639.227001969507, + "width": 131.69989013671875, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4T", + "roundness": null, + "seed": 1160270499, + "version": 2272, + "versionNonce": 451158563, + "isDeleted": false, + "boundElements": [], + "updated": 1767580434170, + "link": null, + "locked": false, + "text": "weight & bias", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "aLz6JA4VKycwGE6wJ7aIt", + "originalText": "weight & bias", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "GdoROXtlGBxCQBezTYH1E", + "type": "rectangle", + "x": 393.437456483182, + "y": 4786.201212157448, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4U", + "roundness": null, + "seed": 2087868493, + "version": 1083, + "versionNonce": 1630780301, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "SBSYmCOAYENhuZjDKG2bQ" + } + ], + "updated": 1767580466531, + "link": null, + "locked": false + }, + { + "id": "SBSYmCOAYENhuZjDKG2bQ", + "type": "text", + "x": 437.56083142214686, + "y": 4803.701212157448, + "width": 68.41993713378906, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4V", + "roundness": null, + "seed": 846972589, + "version": 1109, + "versionNonce": 397328877, + "isDeleted": false, + "boundElements": [], + "updated": 1767580466531, + "link": null, + "locked": false, + "text": "model\nServing", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "GdoROXtlGBxCQBezTYH1E", + "originalText": "model\nServing", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "NUlw66Q9OIa8TIBSh8shW", + "type": "rectangle", + "x": 397.85025827385925, + "y": 4127.595569661166, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4W", + "roundness": null, + "seed": 1603019427, + "version": 1113, + "versionNonce": 319397283, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "iI-RJL93xebiF9s9i5MaW" + } + ], + "updated": 1767580471663, + "link": null, + "locked": false + }, + { + "id": "iI-RJL93xebiF9s9i5MaW", + "type": "text", + "x": 441.9736332128241, + "y": 4145.095569661166, + "width": 68.41993713378906, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4X", + "roundness": null, + "seed": 198563395, + "version": 1139, + "versionNonce": 132903235, + "isDeleted": false, + "boundElements": [], + "updated": 1767580471663, + "link": null, + "locked": false, + "text": "model\nServing", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "NUlw66Q9OIa8TIBSh8shW", + "originalText": "model\nServing", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "yPC5J4VL6MFR8KYd3TIqn", + "type": "rectangle", + "x": 395.643907878644, + "y": 3497.672886303671, + "width": 156.66668701171875, + "height": 85, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4Y", + "roundness": null, + "seed": 1077772589, + "version": 1123, + "versionNonce": 1715199907, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "aX2wRhDW7R4kZ49ZHsB7X" + } + ], + "updated": 1767580479760, + "link": null, + "locked": false + }, + { + "id": "aX2wRhDW7R4kZ49ZHsB7X", + "type": "text", + "x": 439.7672828176088, + "y": 3515.172886303671, + "width": 68.41993713378906, + "height": 50, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4Z", + "roundness": null, + "seed": 682546061, + "version": 1148, + "versionNonce": 539578189, + "isDeleted": false, + "boundElements": [], + "updated": 1767580476679, + "link": null, + "locked": false, + "text": "model\nServing", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "yPC5J4VL6MFR8KYd3TIqn", + "originalText": "model\nServing", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "sfgomunt3ThMckJ6XKU_q", + "type": "rectangle", + "x": 488.5689978967893, + "y": 4249.311580728391, + "width": 299.56796787532073, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "#ffc9c9", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4a", + "roundness": null, + "seed": 512539075, + "version": 2215, + "versionNonce": 190733965, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "WeNR6EN-asrq8s5DPDrqu" + } + ], + "updated": 1767580516436, + "link": null, + "locked": false + }, + { + "id": "WeNR6EN-asrq8s5DPDrqu", + "type": "text", + "x": 541.7930376816176, + "y": 4266.811580728391, + "width": 193.11988830566406, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4b", + "roundness": null, + "seed": 1224116579, + "version": 2358, + "versionNonce": 2100077421, + "isDeleted": false, + "boundElements": [], + "updated": 1767580513277, + "link": null, + "locked": false, + "text": "IB & RoCE support", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "sfgomunt3ThMckJ6XKU_q", + "originalText": "IB & RoCE support", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "MFBSa2ubaUD9ofO7oFDAX", + "type": "rectangle", + "x": 219.23352789007174, + "y": 4915.747023497185, + "width": 239.9955982022902, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4c", + "roundness": null, + "seed": 1230068963, + "version": 2172, + "versionNonce": 1968459331, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "L9ANH6aHqTr18kVeHpkD7" + } + ], + "updated": 1767580528071, + "link": null, + "locked": false + }, + { + "id": "L9ANH6aHqTr18kVeHpkD7", + "type": "text", + "x": 273.3813819228575, + "y": 4933.247023497185, + "width": 131.69989013671875, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4d", + "roundness": null, + "seed": 1970915459, + "version": 2297, + "versionNonce": 2060946915, + "isDeleted": false, + "boundElements": [], + "updated": 1767580528071, + "link": null, + "locked": false, + "text": "weight & bias", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "MFBSa2ubaUD9ofO7oFDAX", + "originalText": "weight & bias", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "lfA_f7LdHHZOlVJcdyc3d", + "type": "rectangle", + "x": 486.51892528118265, + "y": 4915.532178719405, + "width": 299.56796787532073, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4e", + "roundness": null, + "seed": 1154727971, + "version": 2260, + "versionNonce": 1418667853, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "u1iQgDeEBo4G9WP1xSZyQ" + } + ], + "updated": 1767580531532, + "link": null, + "locked": false + }, + { + "id": "u1iQgDeEBo4G9WP1xSZyQ", + "type": "text", + "x": 539.742965066011, + "y": 4933.032178719405, + "width": 193.11988830566406, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4f", + "roundness": null, + "seed": 1900921795, + "version": 2402, + "versionNonce": 2101457187, + "isDeleted": false, + "boundElements": [], + "updated": 1767580528071, + "link": null, + "locked": false, + "text": "IB & RoCE support", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "lfA_f7LdHHZOlVJcdyc3d", + "originalText": "IB & RoCE support", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "d6HJM5niCdXm__tjACWnG", + "type": "rectangle", + "x": 202.68559692521774, + "y": 5681.362225665228, + "width": 239.9955982022902, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4g", + "roundness": null, + "seed": 2084012493, + "version": 2298, + "versionNonce": 29748483, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Qqo4oxKBaxNHFRuFrl-Mx" + } + ], + "updated": 1767580559782, + "link": null, + "locked": false + }, + { + "id": "Qqo4oxKBaxNHFRuFrl-Mx", + "type": "text", + "x": 256.8334509580035, + "y": 5698.862225665228, + "width": 131.69989013671875, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4h", + "roundness": null, + "seed": 585089069, + "version": 2423, + "versionNonce": 757552291, + "isDeleted": false, + "boundElements": [], + "updated": 1767580559782, + "link": null, + "locked": false, + "text": "weight & bias", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "d6HJM5niCdXm__tjACWnG", + "originalText": "weight & bias", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "WAAd0XFKUqYZrZJzHBml5", + "type": "rectangle", + "x": 469.97099431632864, + "y": 5681.147380887448, + "width": 299.56796787532073, + "height": 60, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4i", + "roundness": null, + "seed": 447820429, + "version": 2386, + "versionNonce": 591054915, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "JtiTV8-vNxkwt8Ymi12EQ" + } + ], + "updated": 1767580559782, + "link": null, + "locked": false + }, + { + "id": "JtiTV8-vNxkwt8Ymi12EQ", + "type": "text", + "x": 523.195034101157, + "y": 5698.647380887448, + "width": 193.11988830566406, + "height": 25, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b4j", + "roundness": null, + "seed": 1348819181, + "version": 2528, + "versionNonce": 1507772387, + "isDeleted": false, + "boundElements": [], + "updated": 1767580559782, + "link": null, + "locked": false, + "text": "IB & RoCE support", + "fontSize": 20, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "WAAd0XFKUqYZrZJzHBml5", + "originalText": "IB & RoCE support", + "autoResize": true, + "lineHeight": 1.25 } ], "appState": { diff --git a/specs/mvp/v3.5/v3.5_changes.md b/specs/mvp/v3.5/v3.5_changes.md new file mode 100644 index 0000000..b67f58c --- /dev/null +++ b/specs/mvp/v3.5/v3.5_changes.md @@ -0,0 +1,67 @@ +# MVP v3.5 功能变更总结(相对 v3.0) + +> v3.5 本轮按已确认的精简 scope:**只聚焦 Advanced TaskSpec + Custom Reward(方式 A:用户在 command 里写 overrides)**。Serving/IB/断点续训/多版本 verl 等能力本轮仍不做。 + +## 1. TaskSpec / 任务语义 + +### 1.1 新增 Advanced TaskSpec(自定义 command) + +- 新增 `kind: advanced` 的 TaskSpec 类型: + - 用户可提交任意 bash `command`,不再局限于平台内置 PPO/GRPO/SFT 模板。 + - `workload` 不再要求用户填写,也不做 infer;平台内部统一按 `"advanced"` 做任务分类与 task_id 命名(避免未来训练类型扩展带来的限制)。 +- 支持 `$HOME` 宏替换(服务端提交前展开): + - `$HOME` → `/private/users/` + - `$HOME/common/datasets` → `/private/datasets` + - `$HOME/common/hf` → `/private/hf` +- `command` 校验(best-effort,面向内部可信用户): + - 要求包含 `python3 -m verl.`(允许 `verl.trainer.*` / `verl.model_merger` 等)。 + - 不做强沙箱;主要防止明显误用导致的不可预期行为。 + +### 1.2 Custom Reward(方式 A) + +- 平台不新增 reward 专用字段、不扩展 TaskSpec schema。 +- 用户通过在 `command` 里写 VERL 原生 overrides 来注入 reward: + - `custom_reward_function.path=...` + - `custom_reward_function.name=...` + - `custom_reward_function.reward_kwargs=...`(如需) +- 平台侧仅做: + - 基础路径/宏展开($HOME) + - best-effort 的字符串校验(不做深度 AST 解析) + +## 2. WebUI(New Task 体验增强,仍兼容 YAML) + +- `New Task` 页面新增 **YAML 模式 / 表单模式**切换: + - 表单模式只覆盖 **5 个模板**:PPO / GRPO / SFT / Advanced / Model Merge。 + - 表单模式实时生成 YAML 预览;Submit 时提交生成 YAML;可一键切回 YAML 模式继续手工编辑。 +- `Advanced example`: + - 示例命令改为多行、可读性更好。 + - 补齐 PPO 常见 fail-fast 所需的关键 overrides(例如 actor micro batch),避免用户“照抄即失败”。 +- 新增 `Model merge example`(Advanced command 形式): + - 使用 `python3 -m verl.model_merger merge ...` + - 支持用 `$HOME/jobs//...` 访问训练产物目录。 + +## 3. SFTPGo / common 目录可读性(配合 v3.5 的 $HOME/common 语义) + +> 这些变更主要用于保证 v3.5 所定义的 `$HOME/common/{datasets,hf}` 语义在 SFTPGo WebClient/客户端下可用。 + +- `/common/datasets` 与 `/common/hf` 作为 SFTPGo virtual folders 暴露为只读共享目录: + - 允许 list + download(用于浏览与下载/查看内容;仍不允许 upload/rename/delete)。 + - 权限规则覆盖到子路径(避免“能进目录但文件不可读”的情况)。 +- API 调用 SFTPGo admin API 的连通性增强: + - dev 环境下避免依赖容器内 DNS(部分 head 容器环境存在临时解析失败),改为通过 docker bridge 网关 + 映射端口访问 admin API。 +- API 启动脚本确保注入 `SFTPGO_ADMIN_PASSWORD`(与 compose 默认值保持一致),避免 Reset Password 走到 401。 + +## 4. 兼容性与行为变化 + +- **完全兼容 v3.0 的 PPO/GRPO/SFT TaskSpec YAML**(原有字段与提交方式不变)。 +- 新增能力不会影响 ray/node management(仍按 v3.0:head 发布 discovery、worker watchdog join/self-heal)。 +- Advanced 任务不会进入 PPO/GRPO/SFT 的语义约束;平台仅负责: + - 资源字段(`nnodes` / `n_gpus_per_node`)用于队列调度与提交 gate + - 将 `command` 作为 Ray job entrypoint 执行 + +## 5. 已知限制(v3.5 不做) + +- 不提供“可视化” reward 配置面板(仅方式 A:用户自己写 command)。 +- 不支持 per-job 自定义 verl 代码快照/多版本共存(本轮不做 code_path 选择)。 +- 不支持断点续训一键 resubmit / IB(RDMA) / model serving(按 roadmap 后续版本推进)。 + diff --git a/specs/mvp/v3.6/README.md b/specs/mvp/v3.6/README.md new file mode 100644 index 0000000..fc220f4 --- /dev/null +++ b/specs/mvp/v3.6/README.md @@ -0,0 +1,9 @@ +# MVP v3.6 + +本目录包含 v3.6 的需求与设计: + +- `Snipaste_2026-01-05_10-56-34.png`:v3.6 架构草图(在 v3.5 基础上增加 Weights & Biases;其余模块保持不变) +- `requirements.md`:需求要点(W&B + Evaluation 模板) +- `wandb.md`:W&B local server 的前期调研与资料(license、部署方式、VERL 配置要点等) +- `v3.6_design.md`:v3.6 详细设计方案(基于 v3.5) +- `v3.6_progress.md`:v3.6 里程碑进度记录 diff --git a/specs/mvp/v3.6/Snipaste_2026-01-05_10-56-34.png b/specs/mvp/v3.6/Snipaste_2026-01-05_10-56-34.png new file mode 100644 index 0000000000000000000000000000000000000000..8e889e2396f451cf621d45dc0d17e716aaca3943 GIT binary patch literal 72243 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 设计基线:当前线上已具备 v3.5 能力(PPO/GRPO/SFT + Advanced TaskSpec + SFTPGo 数据管理 + WebUI)。 +> v3.6 的架构草图见:`specs/mvp/v3.6/Snipaste_2026-01-05_10-56-34.png` + +## 0. 目标与范围 + +### 0.1 v3.6 目标 + +1) **Weights & Biases(W&B)集成** +- 训练/任务运行时自动打点到 W&B local server。 +- 采用“共享 W&B 账号(license 只支持 1 user) + 为每个 MVP 用户创建独立 project”的方式隔离可视化与检索体验。 +- 平台提供 W&B 的跳转链接、以及每个 task 对应 run 的可定位信息(最小闭环)。 + +2) **New Task 增加 Evaluation 模板** +- 在 New Task 页面提供一个最小可用的 “Evaluation” 任务模板(用于离线评估/打分),并能把结果落到 job 输出与(可选)W&B。 + +### 0.2 非目标(v3.6 不做) + +- 不引入新的 node management 机制;保持 v3.5 的 head discovery + worker watchdog stateless pool。 +- 不做 Serving/IB/RDMA/断点续训/多版本 verl code_path 等(仍按 v3.5 的范围)。 +- 不做多租户安全隔离(W&B API Key 注入属于内部可信环境)。 + +--- + +## 1. W&B local server(部署与连通) + +> 资料参考:`specs/mvp/v3.6/wandb.md`。**文档中 license/token 属敏感信息,v3.6 设计不在代码/文档里写死。** + +### 1.1 部署方式(dev/h1) + +建议在 `src/mvp/docker-compose.yaml` 新增 `wandb` 服务(与现有 ray_head / sftpgo 同一 compose network): + +- 镜像:`wandb/local:latest` +- 容器端口:`8080` +- 宿主机端口:建议 `8090:8080`(避免和 MVP API `8080`、SFTPGo `8081` 冲突) +- 持久化:挂载到 NFS/共享目录(例如 `/private/common/wandb`)以持久化 runs/artifacts +- 首次启动后由管理员在 W&B System Admin 页面粘贴 license(见 `wandb.md`) + +### 1.1.1 持久化策略(必须) + +v3.6 约定 **W&B server 的元数据必须持久化**(否则会丢 license/账号/API key、历史 runs/project 索引等): + +- W&B server 数据目录(`/vol`)挂载到共享目录(NFS):例如 `/private/common/wandb:/vol` +- 这部分数据由平台/管理员长期保留(不跟随单个 job 清理) + +与之相对,**每个 Ray job 对应的本地 W&B run 文件**放在 job 目录下(见 §2.3 的 `WANDB_DIR`),并由现有 janitor 随 job 一起清理: + +- `WANDB_DIR=/private/users//jobs//wandb` +- janitor 对 job 的策略是“结束后 3 天移入回收站、7 天后删除”,因此该目录会被一并处理 + +> 说明:W&B 的最终“事实数据”在 server 侧持久化(runs/metrics/可视化);job 目录下的 `WANDB_DIR` 更像运行期缓存/临时文件与调试材料。 + +### 1.2 容器内访问 W&B 的 base_url + +在 v3.5 经验中,Ray head 容器对 docker 内部 DNS 名称解析不稳定(`Temporary failure in name resolution`)。 +为避免再次踩坑,v3.6 统一采用 **“docker bridge 网关 + host 映射端口”** 的方式让容器访问 W&B: + +- `WANDB_BASE_URL=http://172.22.0.1:8090`(其中 `172.22.0.1` 为 `mvp_argus-ray-net` 网关) + +> 注意:如果未来 dev 环境 network 网段变化,需要把网关地址做成配置项(见 §2)。 + +--- + +## 2. 平台侧 W&B 集成(API/Scheduler/Ray Job runtime_env) + +### 2.1 配置设计(YAML) + +在 `configs/dev.yaml`(以及生产配置)中新增一段 W&B 配置,建议结构: + +```yaml +tracking: + wandb: + enabled: true + base_url: "http://172.22.0.1:8090" + api_key_env: "WANDB_API_KEY" + entity: "" # 可选;verL 通过 env WANDB_ENTITY 读取 + project_suffix: "_project" # 例如 alice_project + # 可选:代理(verL 支持 trainer.wandb_proxy) + proxy: null +``` + +平台读取 `api_key_env` 对应的环境变量,并在 job 维度注入 `WANDB_API_KEY`。 +**不在配置文件中存明文 API KEY**(避免泄露)。 + +### 2.2 项目/命名规范(共享账号 + user project) + +由于 W&B local license 限制 “最多 1 user”,v3.6 采用: + +- **同一个 W&B 账号**(同一个 `WANDB_API_KEY`) +- 每个 MVP user 使用不同 project: + - `project_name = + project_suffix` + - 示例:`alice_project` +- 每个 Ray job attempt 对应一个 run: + - `experiment_name = `(保证 attempt 唯一;也便于从 dashboard 反查) + +verL 内部 `wandb.init(project=project_name, name=experiment_name, entity=$WANDB_ENTITY)`(见 `verl/utils/tracking.py`)。 + +### 2.3 Ray Job 注入(runtime_env env_vars) + +当 `tracking.wandb.enabled=true` 时,在 scheduler 提交 ray job 时,统一注入: + +- `WANDB_BASE_URL`(来自配置) +- `WANDB_API_KEY`(来自 env `tracking.wandb.api_key_env`) +- `WANDB_ENTITY`(可选) +- `WANDB_MODE=online`(默认在线;可在 dev/离线时切换) +- `WANDB_DIR=/private/users//jobs//wandb`(保证每个 job 的本地 run 文件落到对应 job 目录;便于随 job 一起被 janitor 清理) + +> 对 Advanced TaskSpec:平台无法替用户改 command,但仍可注入上述 env,让用户在 command 中自行启用 wandb。 + +### 2.4 verL 训练任务自动开启 wandb logger + +对平台内置 workload(PPO/GRPO/SFT),平台将 hydra overrides 改为: + +- `trainer.logger=[console,wandb]`(替代 v3.5 的 `trainer.logger=console`) +- `trainer.project_name=_project` +- `trainer.experiment_name=` +- 可选:如果配置了 `tracking.wandb.proxy`,注入 `trainer.wandb_proxy=...` + +兼容性说明: +- `trainer.logger` 在 verL 的 ppo/sft config 中本身是 list(示例为 `["console","wandb"]`),因此不会破坏 verL 配置解析。 +- 现有 v3.5 的 checkpoint/log dir 仍按 job_dir 注入,不依赖 `trainer.default_local_dir`。 + +### 2.5 数据库存储与 API 输出(最小闭环) + +v3.6 需要让用户能在 WebUI/Task 详情中 “点一下跳转到 W&B”: + +建议在 DB(sqlite)里新增 attempt 级字段(或 metadata JSON): + +- `wandb_project`:如 `alice_project` +- `wandb_run_name`:如 `` +- `wandb_base_url`:如 `http://:8090` +- `wandb_url`:拼出来的最终链接(若 W&B local 的 URL 结构不稳定,可只存前 3 项,由前端拼接) + +API 侧在: +- `GET /api/v2/tasks/` 的 `latest_attempt` 增加 `wandb` 字段(或顶层增加 `tracking` 字段) +- `GET /api/v2/me` 增加 `wandb` 信息(base_url、project_name),便于页面展示 “Open W&B” + +> W&B local 的 URL 路径结构需要在接入时确认;若不确定,先只提供 base_url + project/run 名称,用户可在 W&B UI 搜索。 + +--- + +## 3. WebUI 变更(v3.6) + +### 3.1 Data / Login 页 + +- 在 Data 或 Login 页新增: + - “Open W&B” 链接(跳转到 `tracking.wandb.base_url` 的 Web UI) + - 展示当前用户的 project 名称(例如 `alice_project`),并提供 Copy 按钮 + +### 3.2 Tasks / Task Detail + +- Task list 无需大改;Task detail 页面增加: + - W&B run 信息(project / run name / link) + - 若任务失败,W&B 仍可用于查看已上报的中间日志(对训练类任务有价值) + +### 3.3 New Task 增加 Evaluation 模板 + +New Task 页面新增一个 “Evaluation example”: + +#### 方案 A(推荐,最小改动):作为 Advanced TaskSpec 模板 + +- 直接提供 `kind: advanced` 的模板,command 运行 verL 自带评估入口: + - `python3 -m verl.trainer.main_eval data.path=... custom_reward_function.path=... +ray_kwargs.ray_init.address=auto` +- 优点:不需要扩展 TaskSpec schema / scheduler 逻辑 +- 缺点:Evaluation 在平台侧不可结构化(仍属于 advanced) + +#### 方案 B(更产品化):新增内置 workload: evaluation(后续可选) + +若希望在任务分类/队列中把 evaluation 作为一类“内置任务”,可新增: + +```yaml +workload: evaluation +code_path: /private/common/code/verl/verl_repo +nnodes: 1 +n_gpus_per_node: 0 +data_path: $HOME/common/datasets/<...>.parquet +response_key: responses +data_source_key: data_source +reward_model_key: reward_model +custom_reward_path: $HOME/code/reward.py # 可选 +custom_reward_name: compute_score # 可选 +``` + +平台把它编译成 `verl.trainer.main_eval` 的 hydra overrides + ray init address;并可选择把结果解析后上报 W&B。 + +v3.6 建议先落地 **方案 A**(模板即可),方案 B 作为 v3.7+ 的演进。 + +--- + +## 4. 验收标准(v3.6) + +### 4.1 W&B(训练任务) + +- 提交 PPO/GRPO/SFT 任一任务: + - W&B local server 能看到对应 project(如 `alice_project`) + - 能看到 run 名称为该任务 attempt 的 `ray_submission_id` + - run 内能持续刷新 metrics(至少包含 loss/step 等 verL 默认上报) +- WebUI: + - 能打开 W&B UI 链接 + - Task detail 能展示 project/run(或至少可检索信息) + +### 4.2 Evaluation 模板 + +- New Task 中出现 “Evaluation example” +- 复制模板提交后: + - 任务能在 Ray 上运行(CPU 即可,`+ray_kwargs.ray_init.address=auto` 连接集群) + - 输出 metrics(`main_eval.py` 默认 print dict) + - (可选)若用户在 command 里启用 wandb,则能在对应 project 下看到评估 run + +--- + +## 5. 兼容性影响评估 + +- 对现有 v3.5 功能: + - 默认行为改变:PPO/GRPO/SFT 会默认多一个 logger(wandb),并对外发起 HTTP 请求到 W&B server。 + - 若 W&B server 不可用/配置缺失: + - 建议行为:平台自动降级为 `trainer.logger=[console]` 并在 task 状态中给出 warning(避免训练直接失败)。 + - 初版也可选择 fail-fast:缺少 `WANDB_API_KEY` 时拒绝开启 wandb(由配置开关控制)。 +- 对资源/存储: + - W&B server 自身会写入一定量数据(license 限制 10GB);需配合 retention 策略做清理(v3.6 先手动,后续可自动)。 + - job 目录下的 `WANDB_DIR` 会随 jobs retention 被清理;不会无限增长。 + +--- + +## 6. 已确认的落地约定 + +- W&B server 对外端口:`8090` +- Project 命名:`_project`(不使用 `WANDB_ENTITY`) +- Evaluation:先只提供 **Advanced 模板**(New Task 页面提供示例即可) + +--- + +## 7. 运维与验收流程(dev/h1) + +### 7.1 启动服务(docker compose) + +1) 启动/重启 compose(Ray head/worker + SFTPGo + W&B): + +```bash +cd /home2/argus/infra/mvp/src/mvp +docker compose up -d +``` + +2) 访问 UI: +- MVP WebUI:`http://:8080/ui` +- Ray Dashboard:`http://:8265` +- SFTPGo Web:`http://:8081/web/` +- W&B Web:`http://:8090` + +> W&B 容器数据目录已挂载到共享盘:`/private/common/wandb`(容器内 `/vol`)。 + +### 7.2 初始化 W&B(管理员一次性操作) + +1) 打开 `http://:8090/system-admin` 粘贴 license(详见 `specs/mvp/v3.6/wandb.md`)。 +2) 进入 W&B UI 创建/登录到共享账号(license 只支持 1 user)。 +3) 在 W&B UI 的用户设置页面生成 API Key(通常位于 “User Settings / API Keys”)。 + +### 7.3 启动 API server(需要注入 WANDB_API_KEY) + +平台不会把 `WANDB_API_KEY` 写入配置文件;必须在启动 API server 时通过环境变量提供。 + +示例(在宿主机): + +```bash +export MVP_INTERNAL_TOKEN="my-dev-token" +export WANDB_API_KEY="...从 W&B UI 复制..." +cd /home2/argus/infra/mvp/src/mvp/scripts +./60_start_api.sh +``` + +> 说明:`./60_start_api.sh` 会把 `WANDB_API_KEY` 透传给 head 容器内运行的 API server。 + +### 7.4 验收(最小闭环) + +1) 在 WebUI Login 页面能看到 W&B 区块(Open W&B + project copy)。 +2) 提交一个 PPO/GRPO/SFT 任务(任意一个即可): + - W&B project 为 `_project`(如 `alice_project`) + - run name 为该 attempt 的 `ray_submission_id` +3) 提交 Evaluation 模板(Advanced)能在 Ray 上运行并输出评估结果(stdout / logs)。 diff --git a/specs/mvp/v3.6/v3.6_dev_plan.md b/specs/mvp/v3.6/v3.6_dev_plan.md new file mode 100644 index 0000000..d0fa1e3 --- /dev/null +++ b/specs/mvp/v3.6/v3.6_dev_plan.md @@ -0,0 +1,194 @@ +# MVP v3.6(基于 v3.5)开发计划(TDD) + +> 设计依据:`specs/mvp/v3.6/v3.6_design.md` +> 本计划默认已确认: +> 1) W&B host 端口:`8090`;2) project:`_project`;3) evaluation 先做 Advanced 模板;4) 不使用 `WANDB_ENTITY`。 + +## 总体原则 + +- **TDD**:每个里程碑先补齐单测(或契约测试)再实现功能;保证覆盖率门槛不回退。 +- **最小闭环**:先做到“可用+可验证”,再做体验优化;不做超出 v3.6 scope 的扩展。 +- **配置不落盘敏感信息**:`WANDB_API_KEY` 只能来自运行环境变量,不写入仓库配置文件。 + +--- + +## Milestones + +### M1:配置层(tracking.wandb)与解析 + +**目标** +- 在服务配置中新增 `tracking.wandb`,支持开关、base_url、api_key_env。 +- 不引入 `WANDB_ENTITY`(保持为空即可)。 + +**开发任务** +- 在 `src/mvp/py/argus/service/config.py`: + - 新增 `TrackingConfig/WandbConfig` dataclass(或等价结构)。 + - `V2Config.from_root_dict()` 解析 `tracking.wandb.*`(缺省为 disabled)。 + - 校验:`enabled=true` 时 `base_url` 不能为空;`api_key_env` 默认 `WANDB_API_KEY`。 + +**测试(先写)** +- `test_config_parses_wandb_defaults`:没有 tracking 字段时,默认 disabled。 +- `test_config_parses_wandb_enabled`:enabled=true 能读到 base_url/api_key_env。 +- `test_config_rejects_empty_base_url_when_enabled`:enabled=true 且 base_url 空时报错(或记录 warning,取决于实现选择)。 + +**验收** +- 仅通过 config 即能决定是否启用 W&B,且不会破坏 v3.5 现有配置解析。 + +--- + +### M2:Ray Job runtime_env 注入(WANDB_* env) + +**目标** +- 当 `tracking.wandb.enabled=true` 时:平台在 job 粒度注入 `WANDB_BASE_URL/WANDB_API_KEY/WANDB_MODE/WANDB_DIR` 等 env。 +- `WANDB_API_KEY` 从 API server 进程环境变量中读取:`os.environ[api_key_env]`。 + +**开发任务** +- 在 scheduler / ray job builder(`src/mvp/py/argus/service/scheduler.py` 或 `src/mvp/py/argus/ray/builders.py`): + - 构造 job runtime_env.env_vars 的 merge 逻辑: + - 现有 `ray.runtime_env.env_vars` 为基础; + - 追加 W&B env(不覆盖用户显式指定的同名变量,或按“平台优先”策略二选一并写清楚)。 + - 注入: + - `WANDB_BASE_URL=` + - `WANDB_API_KEY=` + - `WANDB_MODE=online` + - `WANDB_DIR=/private/users//jobs//wandb`(本地 run 文件随 job 一起由 janitor 清理) + +**测试(先写)** +- `test_scheduler_injects_wandb_env_when_enabled`: + - mock env 中存在 `WANDB_API_KEY`; + - 提交一个内置任务(ppo/sft 任意),断言构造出来的 runtime_env 含以上 env_vars。 +- `test_scheduler_sets_wandb_dir_under_job_dir`: + - 断言 `WANDB_DIR` 位于该 attempt 的 job 目录下(而不是 common 目录),避免无法跟随 job retention 清理。 +- `test_scheduler_does_not_inject_wandb_env_when_disabled`。 +- `test_scheduler_wandb_missing_api_key_behaviour`: + - enabled=true 但缺少 env 时的行为: + - 方案 A(推荐):**自动降级**为 console(不注入 wandb),并在 task/attempt message 记录 warning; + - 或方案 B:fail-fast(返回 500/400)。 + - 需在实现前确认采用哪种策略;建议 v3.6 选 A 提升可用性。 + +**验收** +- 任意内置任务提交后,在 Ray job runtime_env 中能看到 `WANDB_*`。 + +--- + +### M3:内置训练任务自动开启 wandb logger(PPO/GRPO/SFT) + +**目标** +- 当 W&B enabled 时,平台默认把内置训练任务改成 `trainer.logger=["console","wandb"]`,并设置 project/run 命名。 + +**开发任务** +- 在 job 构建(PPO/GRPO/SFT 的 overrides 生成处): + - 将 `trainer.logger` 从 `console` 改为 list:`[console,wandb]`(hydra 语法按现有实现方式拼接)。 + - `trainer.project_name=_project` + - `trainer.experiment_name=` + - 保持 v3.5 的 job_dir/checkpoint/log dir 注入方式不变。 + +**测试(先写)** +- `test_job_overrides_include_wandb_logger_when_enabled`: + - 断言 entrypoint/overrides 包含 `trainer.logger=[console,wandb]`(或等价写法)。 + - 断言包含 `trainer.project_name=_project`、`trainer.experiment_name=`。 +- `test_job_overrides_keep_console_only_when_wandb_disabled_or_missing_key`。 + +**验收** +- 训练任务 run 会自动出现在 W&B 对应 project 下(E2E 验证在 M6)。 + +--- + +### M4:API 输出与 WebUI 链接(最小闭环) + +**目标** +- 用户可以在 UI 里“知道去哪看 W&B”,以及知道本 task 对应哪个 project/run。 + +**开发任务** +- API: + - `GET /api/v2/me` 增加 `wandb` 信息(仅当 enabled 时返回): + - `base_url` + - `project_name`(`_project`) + - `GET /api/v2/tasks/{task_id}`(或 attempt 结构)增加 `wandb_project` / `wandb_run_name`(run_name=ray_submission_id)。 +- WebUI: + - Login/Data 页增加 “Open W&B” 链接(跳 `base_url`),展示 project_name + Copy。 + - Task detail 增加 wandb 字段展示(project/run/可点击链接或可复制文本)。 + +**测试(先写)** +- `test_me_includes_wandb_when_enabled`(mock config + env)。 +- `test_task_detail_contains_wandb_fields_when_enabled`(mock task/attempt)。 +- `test_ui_contains_wandb_link`(渲染 HTML 断言包含 base_url/project_name 字样)。 + +**验收** +- WebUI 能一键跳转 W&B;Task detail 能定位到 run。 + +--- + +### M5:New Task 增加 Evaluation 模板(Advanced) + +**目标** +- New Task 页面增加一个 Evaluation 模板按钮/示例(先按 Advanced TaskSpec 提供)。 + +**开发任务** +- 在 `src/mvp/py/argus/service/ui.py`: + - YAML 模式增加 “Evaluation example”。 + - 表单模式本轮可选(不要求): + - 如果要支持:把 evaluation 作为 advanced 模板的一种预填 command(仍 `kind: advanced`)。 +- 模板建议使用 verL 自带入口: + - `python3 -m verl.trainer.main_eval ... +ray_kwargs.ray_init.address=auto` + - `data.path=$HOME/common/datasets/...`(按 v3.5 的宏规则) + +**测试(先写)** +- `test_ui_new_task_contains_evaluation_example`:断言页面包含 `main_eval` 与关键字段。 + +**验收** +- 用户复制 evaluation 模板可提交成功并在 Ray 上运行(E2E 在 M6)。 + +--- + +### M6:端到端(dev/h1)部署与验收流程 + +> 里程碑 M6 以脚本/手工步骤为主;不强制写 e2e 自动化测试。 + +**部署任务** +- compose 增加 `wandb` 服务: + - `wandb/local:latest` + - host 端口 `8090:8080` + - 数据挂载到 `/private/common/wandb:/vol` 持久化(W&B 元数据/账号/API key/历史 runs) +- API 启动方式: + - 在宿主机 export:`WANDB_API_KEY=<从 W&B UI 生成的 key>` + - 启动 API(确保 env 透传到容器内) +- 首次初始化: + - 打开 `http://:8090/system-admin` 粘贴 license(管理员操作) + +**验收用例** +1) **训练类任务自动打点** + - 用 alice 提交一个 SFT(或 PPO)任务(内置 workload) + - 在 W&B UI 中看到 project:`alice_project` + - run name 为 `ray_submission_id`,metrics 可见 +2) **Advanced task 可手动打点** + - 提交一个 Advanced(用户自己写 command)并在 command 中启用 `trainer.logger=["console","wandb"]`(如需) + - 确认 env 注入生效(W&B 记录出现) +3) **Evaluation 模板** + - 用 New Task 的 Evaluation example 提交 + - 任务成功运行并输出 metrics(stdout/logs) + - (可选)如果 evaluation 也启用 wandb,则能出现在对应 project 下 + +**回归** +- v3.5 的三类任务(ppo/grpo/sft)在 W&B disabled 或缺少 key 时仍可跑通(至少 console 输出不受影响)。 + +**Retention 联动检查** +- 提交一个短任务生成 `WANDB_DIR`,结束后确认: + - `WANDB_DIR` 位于 `/private/users//jobs//wandb` + - janitor 运行后该目录会随 job 一起进入 trash / 被 purge(与 jobs retention 一致) + +--- + +## 交付物清单(v3.6) + +- 文档: + - `specs/mvp/v3.6/v3.6_design.md`(已存在,必要时补充操作流程) + - `specs/mvp/v3.6/v3.6_dev_plan.md`(本文) +- 代码(预期变更点): + - `src/mvp/py/argus/service/config.py` + - `src/mvp/py/argus/service/scheduler.py` / `src/mvp/py/argus/ray/builders.py` / `src/mvp/py/argus/ray/ray_job_tool.py` + - `src/mvp/py/argus/service/app.py`(/me 与 task detail 输出) + - `src/mvp/py/argus/service/ui.py`(Open W&B + Evaluation template) + - `src/mvp/docker-compose.yaml`(wandb service) + - `src/mvp/configs/dev.yaml`(tracking.wandb 配置) + - `src/mvp/scripts/*`(API 启动时 env 透传,必要时补充) diff --git a/specs/mvp/v3.6/v3.6_progress.md b/specs/mvp/v3.6/v3.6_progress.md new file mode 100644 index 0000000..7f681c8 --- /dev/null +++ b/specs/mvp/v3.6/v3.6_progress.md @@ -0,0 +1,42 @@ +# MVP v3.6 进度记录 + +> 基线:v3.5 已完成(Advanced TaskSpec + Custom reward(方式A)+ WebUI + SFTPGo + stateless ray node pool)。 +> 本文件用于记录 v3.6 每个 milestone 的完成情况与关键改动点。 + +## M1(完成) + +- 新增 `tracking.wandb` 配置解析与校验(enabled/base_url/api_key_env)。 + +## M2(完成) + +- Ray job 维度注入 `WANDB_*` env(含 `WANDB_BASE_URL/WANDB_API_KEY/WANDB_MODE/WANDB_DIR`),缺少 key 时降级并记录 warning。 + +## M3(完成) + +- PPO/GRPO/SFT 内置训练任务在 wandb 可用时自动追加 overrides: + - `trainer.logger=[console,wandb]` + - `trainer.project_name=_project` + - `trainer.experiment_name=` + +## M4(完成) + +- API 输出增加 W&B 定位信息: + - `/api/v2/me` 返回 `wandb.{enabled,base_url,project_name}` + - `/api/v2/tasks/{task_id}` 在 `latest_attempt.wandb` 返回 `{base_url,project_name,run_name}` +- WebUI: + - Login 页面增加 W&B 区块(跳转 8090、copy project) + - Task detail 页面增加 W&B 区块(copy run) + +## M5(完成) + +- WebUI New Task 增加 Evaluation 模板(Advanced): + - 使用 `python3 -m verl.trainer.main_eval ... +ray_kwargs.ray_init.address=auto` + - 以占位符路径示例(用户替换 `/`) + +## M6(完成) + +- `docker-compose.yaml` 集成 W&B local server: + - host 端口 `8090` + - 持久化目录 `/private/common/wandb`(容器内 `/vol`) +- dev 配置新增 `tracking.wandb` 默认开启(缺 key 自动降级并记录 warning)。 +- API 启动脚本支持把 `WANDB_API_KEY` 从宿主机透传到 head 容器中的 API server。 diff --git a/specs/mvp/v3.6/v3.6_summary.md b/specs/mvp/v3.6/v3.6_summary.md new file mode 100644 index 0000000..818bb4d --- /dev/null +++ b/specs/mvp/v3.6/v3.6_summary.md @@ -0,0 +1,126 @@ +# MVP v3.6 迭代研发总结(基于 v3.5) + +> 时间基线:2026-01(H20 dev 环境:`argus@h1:/home2/argus/infra/mvp`) +> v3.6 架构草图:`specs/mvp/v3.6/Snipaste_2026-01-05_10-56-34.png` + +## 1. 迭代目标回顾 + +v3.6 在 v3.5(WebUI + API server + Ray stateless node pool + SFTPGo + Advanced TaskSpec)基础上,新增两块能力: + +1) **Weights & Biases(W&B)local server 集成** +- 训练任务(PPO/GRPO/SFT)默认可写入 W&B。 +- 采用“共享 W&B 账号 + 按用户拆分 project(`_project`)”的隔离策略。 + +2) **New Task 增加 Evaluation 示例** +- New Task 页面新增一个最小可用的 evaluation 模板(以 Advanced command 方式运行 `verl.trainer.main_eval`)。 + +## 2. 交付内容(代码/配置/脚本) + +### 2.1 部署形态(docker compose) + +v3.6 在 `src/mvp/docker-compose.yaml` 新增 W&B 服务: + +- 服务名:`wandb`(容器名:`argus-wandb`) +- 宿主机端口:`8090:8080` +- 持久化:`../../shared/common/wandb:/vol` +- 同 network:`argus-ray-net`(便于 Ray 容器内访问) + +### 2.2 平台配置(YAML) + +在 `src/mvp/configs/dev.yaml` 增加/启用 W&B 配置: + +```yaml +tracking: + wandb: + enabled: true + base_url: "http://172.22.0.1:8090" + api_key_env: "WANDB_API_KEY" + project_suffix: "_project" +``` + +说明: +- `base_url` 采用 docker bridge 网关 + 宿主机映射端口的方式,规避容器内 DNS 偶发解析失败问题。 +- 不在 config 中写明文 key,统一通过启动 API server 时注入 `WANDB_API_KEY`。 + +### 2.3 Ray Job runtime_env 注入(核心) + +v3.6 在**每个 Ray job attempt**提交时注入两类环境变量: + +1) **始终注入(无论是否启用 W&B)**:便于 Advanced command 在不改模板的情况下能运行 +- `MVP_TRAINER_LOGGER`:`console` 或 `[console,wandb]` +- `MVP_WANDB_PROJECT`:`_project`(例如 `alice_project`) +- `MVP_WANDB_RUN`:``(每次 attempt 唯一) + +2) **当 W&B 有效启用时注入** +- `WANDB_BASE_URL` +- `WANDB_API_KEY` +- `WANDB_MODE=online` +- `WANDB_DIR=/wandb`(例如 `/private/users/alice/jobs//wandb`) + +降级策略: +- 当 `tracking.wandb.enabled=true` 但缺少 `WANDB_API_KEY` 时,平台会**降级为 console**(并在 attempt.message 中记录 warning),避免训练失败。 + +### 2.4 WebUI 变更 + +1) **Login 页面** +- 增加 “Open W&B” 跳转(指向 `tracking.wandb.base_url`) + +2) **New Task 页面** +- 新增 **Evaluation example** +- Advanced example 更新为 v3.6: + - `command: |` 内不再包含任何注释(避免 YAML/命令解析报错) + - W&B 参数不再让用户手填,改为引用平台注入的 `${MVP_*}` env: + - `trainer.logger=${MVP_TRAINER_LOGGER}` + - `trainer.project_name=${MVP_WANDB_PROJECT}` + - `trainer.experiment_name=${MVP_WANDB_RUN}` + +> 备注:driver 日志里会打印 `MVP_DRIVER_EXEC: bash -lc '...'`,此处看到 `${MVP_*}` 仍是“未替换”的字符串是正常现象;变量替换发生在 `bash` 执行阶段,而不是打印 argv 阶段。 + +### 2.5 启动脚本 + +`src/mvp/scripts/60_start_api.sh` 支持将宿主机的 `WANDB_API_KEY` 透传进 head 容器内启动的 API server: + +- 宿主机设置:`export WANDB_API_KEY=...` +- 启动 API:脚本会 `docker exec -e WANDB_API_KEY=...` 进入 head 容器启动 `python3 /workspace/mvp/py/server.py` + +## 3. 用户侧操作流程(v3.6) + +### 3.1 一次性初始化(只在首次启用/清空 /vol 时需要) + +1) 打开 W&B UI:`http://:8090` +2) 在 System Admin 页面粘贴 license 完成初始化 +3) 生成并记录 `WANDB_API_KEY`(local key) +4) 以后启动 API server 时注入该 key(`WANDB_API_KEY=...`) + +只要保留 `shared/common/wandb`(即 `/vol` 持久化目录),重建容器无需再次进入 8090 配置。 + +### 3.2 日常使用 + +1) WebUI 登录:`http://:8080/ui/login`(输入 user token) +2) New Task 提交任务:`http://:8080/ui/tasks/new` +3) 到 Tasks 查看状态/日志:`/ui/tasks` 与 task detail +4) 打开 W&B:`http://:8090`,在 `_project` 下查看 runs/metrics + +## 4. 验收结果(本迭代应达成) + +1) PPO/GRPO/SFT 任一任务运行后: +- W&B local server 可见对应 project(如 `alice_project`) +- run name 与 `ray_submission_id` 对齐(便于追踪每次 attempt) + +2) Evaluation 示例: +- 可作为 Advanced 任务提交并在 Ray 上执行 `verl.trainer.main_eval` +- 支持用户在 command 内自行加入 reward overrides(平台不做封装) + +## 5. 已知限制与后续建议 + +1) **W&B 初始化自动化** +- 当前:首次仍需在 8090 页面粘贴 license、生成 key(更稳、侵入最小)。 +- 若需要“从零部署也完全免页面操作”,可进一步调研 W&B local 的可用管理 API/启动参数(自动注入 license + 自动创建 key)。 + +2) **Advanced command 的自由度** +- 平台只负责: + - `$HOME` 宏替换 + - runtime_env env_vars 注入 + - 任务队列与 Ray job 提交 +- command 的语义正确性仍由用户负责(例如 PPO 必需的 micro batch 等参数)。 + diff --git a/specs/mvp/v3.6/wandb.md b/specs/mvp/v3.6/wandb.md new file mode 100644 index 0000000..c7ddeda --- /dev/null +++ b/specs/mvp/v3.6/wandb.md @@ -0,0 +1,70 @@ + +# License + +License: +eyJhbGciOiJSUzI1NiIsImtpZCI6InUzaHgyQjQyQWhEUXM1M0xQY09yNnZhaTdoSlduYnF1bTRZTlZWd1VwSWM9In0.eyJjb25jdXJyZW50QWdlbnRzIjoxLCJ0cmlhbCI6ZmFsc2UsIm1heFN0b3JhZ2VHYiI6MTAsIm1heFRlYW1zIjowLCJtYXhVc2VycyI6MSwibWF4Vmlld09ubHlVc2VycyI6MCwibWF4UmVnaXN0ZXJlZE1vZGVscyI6MiwiZXhwaXJlc0F0IjoiMjAyNy0wMS0wNVQwMjoxMjo1MC4zMjRaIiwiZGVwbG95bWVudElkIjoiYzNmN2Y5N2ItMzAxOS00Nzk2LTkxYTgtZDUyMjc1NDBiMTI1IiwiZmxhZ3MiOltdLCJjb250cmFjdFN0YXJ0RGF0ZSI6IjIwMjYtMDEtMDVUMDI6MTI6NTAuMzI0WiIsImFjY2Vzc0tleSI6IjYxMGM5NjliLTk4ZWEtNGRhNS1iYzU1LWM2MzVlZWNhNzc0OCIsInNlYXRzIjoxLCJ2aWV3T25seVNlYXRzIjowLCJ0ZWFtcyI6MCwicmVnaXN0ZXJlZE1vZGVscyI6Miwic3RvcmFnZUdpZ3MiOjEwLCJleHAiOjE3OTkxMTUxNzAsIndlYXZlTGltaXRzIjp7IndlYXZlTGltaXRCeXRlcyI6bnVsbCwid2VhdmVPdmVyYWdlQ29zdENlbnRzIjowLCJ3ZWF2ZU92ZXJhZ2VVbml0IjoiTUIifX0.VADnc0PExWhGDAxMIbu0vlmPN423B398of4HFl6BMJ1vqGA9H1ESElOZfk0VQ0YnYgwZc_CZF9k0HRyfCBgRhtRKyB1PpGnaKT_kKNVQryykWRpNhnpDqhmTa-wfTUBXNxhu1ktNPKBFNaEbaYuPsLN_aXPGW0dDwp6coGnGEXEqdRmuvekE6ytu7t6IA6flYs35WqCojvvjAmfBdovo2zPTfmlqKeaz7GPrApMo9JBpmb1a6bZEjCoRhhUx_k-v2rbvE3hd9ix9_UMZ6siJ5IKtNuXy_cprcCXXIFVUMcfTnt78RRXY0jCRMQqWkNq9ZGF0Mgcjsh3ts9xSxPgWnw + +# License 限制 +Add License to your Local Instance +Create up to 0 teams +Create up to 1 users +Store up to 10 GB of data +Create up to 2 Registered Models + +Quickstart +On a machine with Docker and Python installed, run: +1 pip install wandb --upgrade +2 wandb server start +Generate a free license from the Deployer. +Add it to your W&B Server's localhost's settings. +Paste the license in the /system-admin page on your localhost + +# docker 部署 + +deployment: +version: "3.8" + +services: + wandb: + image: wandb/local:latest + container_name: wandb-local + ports: + - "8080:8080" + volumes: + - wandb_data:/vol + restart: unless-stopped + +volumes: + wandb_data: + + + +# 连接方式: +方式 B:环境变量(适合容器/批处理/CI) + +通过 ray job的runtime_env来设置环境变量 + +export WANDB_BASE_URL=http://<服务器IP或域名>:8080 +export WANDB_API_KEY=<你的API_KEY> + + +官方文档说明可以用 WANDB_BASE_URL + WANDB_API_KEY 代替 wandb login --host .. + + +# verl配置: +在 verl 里打开 wandb(你只需要配 trainer) + +verl 的配置里,最关键是这三个字段:trainer.logger、trainer.project_name、trainer.experiment_name。文档里也写了 logger 用于 console + tracking(tracking 会初始化 wandb)。 +veRL Documentation ++1 + +推荐写法(新版本): + +trainer: + logger: ["console", "wandb"] + project_name: my_project # 用argus的用户名_project ,譬如 alice_project + experiment_name: exp_001 # 用 task id 作为实验名 + + + + diff --git a/src/mvp/configs/dev.yaml b/src/mvp/configs/dev.yaml index 4f79fe6..de11c65 100644 --- a/src/mvp/configs/dev.yaml +++ b/src/mvp/configs/dev.yaml @@ -32,6 +32,16 @@ service: retry_interval_s: 60 max_running_tasks: 1 +tracking: + wandb: + # v3.6: enable Weights & Biases integration (best-effort). + # - If WANDB_API_KEY is missing, the platform degrades to console logging and records a warning. + enabled: true + # For dev compose, use docker bridge gateway + published port for stability. + base_url: "http://172.22.0.1:8090" + api_key_env: "WANDB_API_KEY" + project_suffix: "_project" + # v3.0: user data management (filesystem + SFTPGo) data: # All user writable data is placed under this root: diff --git a/src/mvp/configs/dev_v30.yaml b/src/mvp/configs/dev_v30.yaml index 3c2649d..131dd7c 100644 --- a/src/mvp/configs/dev_v30.yaml +++ b/src/mvp/configs/dev_v30.yaml @@ -32,6 +32,14 @@ service: retry_interval_s: 60 max_running_tasks: 1 +tracking: + wandb: + enabled: true + # For dev compose, recommend docker bridge gateway + host published port for stability. + base_url: "http://172.22.0.1:8090" + api_key_env: "WANDB_API_KEY" + project_suffix: "_project" + data: user_root: "/private/users" sftpgo: @@ -48,4 +56,3 @@ data: jobs_trash_after_days: 3 jobs_purge_after_days: 7 janitor_interval_s: 3600 - diff --git a/src/mvp/docker-compose.yaml b/src/mvp/docker-compose.yaml index f7d1991..1b8c86a 100644 --- a/src/mvp/docker-compose.yaml +++ b/src/mvp/docker-compose.yaml @@ -74,6 +74,27 @@ services: SFTPGO_HTTPD__BINDINGS__0__PORT: "8080" SFTPGO_SFTPD__BINDINGS__0__PORT: "2022" + # v3.6: Weights & Biases local server (shared single account; per-user projects). + # + # - UI: 8080 in container, mapped to host 8090 + # - Data: persist under /private/common/wandb (NFS/shared) + # + # NOTE: + # - The license/initial setup is done via the W&B UI. + # - Ray jobs use WANDB_BASE_URL from config (recommended: docker bridge gateway + host published port). + wandb: + image: wandb/local:latest + container_name: argus-wandb + ports: + - "8090:8080" + volumes: + - ../../shared/common/wandb:/vol + networks: + argus-ray-net: + aliases: + - wandb + - argus-wandb + ray_worker_0: image: argus/argus-ray-node:v2.5 container_name: argus-ray-worker-0 diff --git a/src/mvp/py/argus/ray/ray_job_tool.py b/src/mvp/py/argus/ray/ray_job_tool.py index a0077a2..52ce223 100644 --- a/src/mvp/py/argus/ray/ray_job_tool.py +++ b/src/mvp/py/argus/ray/ray_job_tool.py @@ -10,7 +10,7 @@ from typing import Any import ray from ray.job_submission import JobSubmissionClient -from .builders import build_advanced_argv, build_training_argv +from .builders import BuiltCommand, build_advanced_argv, build_training_argv from .models import AdvancedTaskSpec, JobSpec, RayConfig from .yaml_io import dump_yaml @@ -44,10 +44,17 @@ class RayJobTool: def _job_dir(self, submission_id: str) -> str: return f"{self.cfg.shared_root}/jobs/{submission_id}" - def _runtime_env(self, spec: JobSpec) -> dict[str, Any]: - return self._runtime_env_for(code_path=spec.code_path, workload=spec.workload) + def _runtime_env(self, spec: JobSpec, *, extra_env_vars: dict[str, str] | None = None) -> dict[str, Any]: + return self._runtime_env_for(code_path=spec.code_path, workload=spec.workload, extra_env_vars=extra_env_vars) - def _runtime_env_for(self, *, code_path: str, workload: str, extra_pythonpath: list[str] | None = None) -> dict[str, Any]: + def _runtime_env_for( + self, + *, + code_path: str, + workload: str, + extra_pythonpath: list[str] | None = None, + extra_env_vars: dict[str, str] | None = None, + ) -> dict[str, Any]: env_vars = dict(self.cfg.runtime_env_env_vars) # Default HF cache @@ -74,13 +81,29 @@ class RayJobTool: if workload == "sft": env_vars.setdefault("RAY_ADDRESS", "auto") + # Per-job extra env vars (e.g. W&B). Do not override existing keys. + if extra_env_vars: + for k, v in dict(extra_env_vars).items(): + if k not in env_vars: + env_vars[k] = str(v) + return {"env_vars": env_vars} - def submit(self, spec: JobSpec, no_wait: bool, job_dir: str | None = None) -> str: + def submit( + self, + spec: JobSpec, + no_wait: bool, + job_dir: str | None = None, + *, + extra_env_vars: dict[str, str] | None = None, + extra_overrides: list[str] | None = None, + ) -> str: submission_id = spec.submission_id or f"mvp11_{spec.workload}_{_ts()}_{os.getpid()}" job_dir = job_dir or self._job_dir(submission_id) built = build_training_argv(spec, submission_id=submission_id, job_dir=job_dir) + if extra_overrides: + built = BuiltCommand(argv=[*built.argv, *list(extra_overrides)]) entrypoint_argv = [ "python3", "-m", @@ -91,7 +114,7 @@ class RayJobTool: ] entrypoint = " ".join(shlex.quote(x) for x in entrypoint_argv) - runtime_env = self._runtime_env(spec) + runtime_env = self._runtime_env(spec, extra_env_vars=extra_env_vars) # Prepare job artifacts directory job_root = Path(job_dir) @@ -155,6 +178,8 @@ class RayJobTool: no_wait: bool, job_dir: str | None = None, user_id: str | None = None, + *, + extra_env_vars: dict[str, str] | None = None, ) -> str: submission_id = spec.submission_id or f"mvp11_{spec.workload}_{_ts()}_{os.getpid()}" job_dir = job_dir or self._job_dir(submission_id) @@ -174,7 +199,12 @@ class RayJobTool: if user_id: extra_pythonpath.append(f"{self.cfg.shared_root}/users/{user_id}/code") - runtime_env = self._runtime_env_for(code_path=self.cfg.verl_code_path, workload=spec.workload, extra_pythonpath=extra_pythonpath) + runtime_env = self._runtime_env_for( + code_path=self.cfg.verl_code_path, + workload=spec.workload, + extra_pythonpath=extra_pythonpath, + extra_env_vars=extra_env_vars, + ) # Prepare job artifacts directory job_root = Path(job_dir) diff --git a/src/mvp/py/argus/service/app.py b/src/mvp/py/argus/service/app.py index 7bcb2f0..faa7942 100644 --- a/src/mvp/py/argus/service/app.py +++ b/src/mvp/py/argus/service/app.py @@ -276,6 +276,14 @@ def create_app(config_path: str) -> FastAPI: "jobs_purge_after_days": int(v2_cfg.data.retention.jobs_purge_after_days), }, } + if v2_cfg.tracking.wandb.enabled: + suffix = v2_cfg.tracking.wandb.project_suffix or "_project" + out["wandb"] = { + "enabled": True, + # Training uses base_url; UI can also open the host port directly. + "base_url": v2_cfg.tracking.wandb.base_url, + "project_name": f"{user_id}{suffix}", + } if _sftpgo_enabled(): out["sftp"] = { "host": v2_cfg.data.sftpgo.host, @@ -429,7 +437,7 @@ def create_app(config_path: str) -> FastAPI: "error_summary": row.get("error_summary"), } if latest_attempt: - out["latest_attempt"] = { + latest_out: dict[str, Any] = { "attempt_no": latest_attempt["attempt_no"], "ray_submission_id": latest_attempt["ray_submission_id"], "ray_status": latest_attempt.get("ray_status"), @@ -438,6 +446,16 @@ def create_app(config_path: str) -> FastAPI: "failure_kind": latest_attempt.get("failure_kind"), "message": latest_attempt.get("message"), } + if v2_cfg.tracking.wandb.enabled: + suffix = v2_cfg.tracking.wandb.project_suffix or "_project" + # For v3.6: shared W&B account, user-level projects. + # Use attempt's ray_submission_id as run name for stable mapping. + latest_out["wandb"] = { + "base_url": v2_cfg.tracking.wandb.base_url, + "project_name": f"{str(row.get('user_id') or subject.get('user_id'))}{suffix}", + "run_name": str(latest_attempt.get("ray_submission_id") or ""), + } + out["latest_attempt"] = latest_out return out @app.get("/api/v2/tasks/{task_id}/spec") diff --git a/src/mvp/py/argus/service/config.py b/src/mvp/py/argus/service/config.py index 3ece213..193aef2 100644 --- a/src/mvp/py/argus/service/config.py +++ b/src/mvp/py/argus/service/config.py @@ -34,6 +34,19 @@ class V2RetentionConfig: janitor_interval_s: int = 3600 +@dataclass(frozen=True) +class V2WandbConfig: + enabled: bool = False + base_url: str = "" + api_key_env: str = "WANDB_API_KEY" + project_suffix: str = "_project" + + +@dataclass(frozen=True) +class V2TrackingConfig: + wandb: V2WandbConfig + + @dataclass(frozen=True) class V2SFTPGoConfig: enabled: bool = False @@ -57,6 +70,7 @@ class V2Config: auth: V2AuthConfig sqlite: V2SqliteConfig scheduler: V2SchedulerConfig + tracking: V2TrackingConfig data: V2DataConfig @staticmethod @@ -83,6 +97,13 @@ class V2Config: else: shared_root = str(root.get("shared_root") or "/private") + tracking = root.get("tracking") or {} + if not isinstance(tracking, dict): + raise ValueError("config.tracking must be a mapping") + wandb = tracking.get("wandb") or {} + if not isinstance(wandb, dict): + raise ValueError("config.tracking.wandb must be a mapping") + data = root.get("data") or {} if not isinstance(data, dict): raise ValueError("config.data must be a mapping") @@ -96,6 +117,11 @@ class V2Config: user_root = str(data.get("user_root") or f"{shared_root}/users") + wandb_enabled = bool(wandb.get("enabled") or False) + wandb_base_url = str(wandb.get("base_url") or "") + if wandb_enabled and not wandb_base_url: + raise ValueError("tracking.wandb.base_url is required when tracking.wandb.enabled=true") + return V2Config( api=V2ApiConfig( host=str(api.get("host") or "0.0.0.0"), @@ -108,6 +134,14 @@ class V2Config: retry_interval_s=int(scheduler.get("retry_interval_s") or 60), max_running_tasks=int(scheduler.get("max_running_tasks") or 1), ), + tracking=V2TrackingConfig( + wandb=V2WandbConfig( + enabled=wandb_enabled, + base_url=wandb_base_url, + api_key_env=str(wandb.get("api_key_env") or "WANDB_API_KEY"), + project_suffix=str(wandb.get("project_suffix") or "_project"), + ) + ), data=V2DataConfig( user_root=user_root, sftpgo=V2SFTPGoConfig( diff --git a/src/mvp/py/argus/service/scheduler.py b/src/mvp/py/argus/service/scheduler.py index c9b975e..4fb3fc1 100644 --- a/src/mvp/py/argus/service/scheduler.py +++ b/src/mvp/py/argus/service/scheduler.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os import re import time from dataclasses import dataclass @@ -48,6 +49,74 @@ class Scheduler: required = float(nnodes * n_gpus_per_node) return avail.total_available_gpus >= required + def _wandb_env_vars(self, *, ray_submission_id: str, job_dir: str) -> tuple[dict[str, str], str | None]: + """ + Returns (env_vars, warn_message). + + env_vars is always non-empty so that AdvancedTaskSpec examples can safely + reference injected env vars without requiring the user to edit their command: + - MVP_TRAINER_LOGGER: "console" or "[console,wandb]" + - MVP_WANDB_PROJECT: "_project" + - MVP_WANDB_RUN: "" + + When W&B is effectively enabled (config enabled + API key present), + WANDB_* env vars are included as well. + """ + env, warn, _enabled = self._tracking_env_vars(user_id=None, ray_submission_id=ray_submission_id, job_dir=job_dir) + return env, warn + + def _tracking_env_vars( + self, *, user_id: str | None, ray_submission_id: str, job_dir: str + ) -> tuple[dict[str, str], str | None, bool]: + """ + Returns (env_vars, warn_message, wandb_enabled_effective). + + - env_vars always includes MVP_* vars so advanced commands can use them. + - wandb_enabled_effective is True only when tracking.wandb.enabled AND api key exists. + """ + cfg = self.v2_cfg.tracking.wandb + project_suffix = cfg.project_suffix or "_project" + project_name = f"{user_id}{project_suffix}" if user_id else f"mvp{project_suffix}" + + api_key = os.environ.get(cfg.api_key_env, "").strip() + wandb_enabled_effective = bool(cfg.enabled and api_key) + warn: str | None = None + if cfg.enabled and not api_key: + warn = f"WARN_WANDB_DISABLED_MISSING_ENV:{cfg.api_key_env}" + + env: dict[str, str] = { + "MVP_TRAINER_LOGGER": ("[console,wandb]" if wandb_enabled_effective else "console"), + "MVP_WANDB_PROJECT": project_name, + "MVP_WANDB_RUN": ray_submission_id, + } + + if wandb_enabled_effective: + env.update( + { + "WANDB_BASE_URL": cfg.base_url, + "WANDB_API_KEY": api_key, + "WANDB_MODE": "online", + "WANDB_DIR": f"{job_dir.rstrip('/')}/wandb", + } + ) + + return env, warn, wandb_enabled_effective + + def _wandb_training_overrides(self, *, user_id: str | None, ray_submission_id: str) -> list[str] | None: + """ + Returns hydra overrides to enable W&B logging for built-in training workloads. + + Only call this when W&B is effectively enabled for the job (i.e. API key exists). + """ + cfg = self.v2_cfg.tracking.wandb + project_suffix = cfg.project_suffix or "_project" + project_name = f"{user_id}{project_suffix}" if user_id else f"mvp{project_suffix}" + return [ + "trainer.logger=[console,wandb]", + f"trainer.project_name={project_name}", + f"trainer.experiment_name={ray_submission_id}", + ] + def _parse_taskspec(self, jobspec_yaml: str) -> JobSpec | AdvancedTaskSpec: obj = yaml.safe_load(jobspec_yaml) or {} if not isinstance(obj, dict): @@ -70,21 +139,38 @@ class Scheduler: self.db.set_task_state(task_id=task_id, state="SUBMITTING", latest_attempt_no=attempt_no) # Override submission_id in taskspec (v1.1 compatible) + tracking_env, wandb_warn, wandb_enabled = self._tracking_env_vars( + user_id=user_id_s, ray_submission_id=ray_sid, job_dir=job_dir + ) if isinstance(spec, JobSpec): d = spec.to_public_dict() d["submission_id"] = ray_sid spec2 = JobSpec.from_dict(d) - submit = lambda: self.tool.submit(spec2, no_wait=True, job_dir=job_dir) + wandb_overrides = self._wandb_training_overrides(user_id=user_id_s, ray_submission_id=ray_sid) if wandb_enabled else None + submit = lambda: self.tool.submit( + spec2, + no_wait=True, + job_dir=job_dir, + extra_env_vars=tracking_env, + extra_overrides=wandb_overrides, + ) else: d = spec.to_public_dict() d["submission_id"] = ray_sid spec2 = AdvancedTaskSpec.from_dict(d) - submit = lambda: self.tool.submit_advanced(spec2, no_wait=True, job_dir=job_dir, user_id=user_id_s) + submit = lambda: self.tool.submit_advanced( + spec2, no_wait=True, job_dir=job_dir, user_id=user_id_s, extra_env_vars=tracking_env + ) try: submitted = submit() # submitted should equal ray_sid; keep as source of truth. - self.db.update_attempt(task_id=task_id, attempt_no=attempt_no, ray_status="SUBMITTED") + self.db.update_attempt( + task_id=task_id, + attempt_no=attempt_no, + ray_status="SUBMITTED", + message=wandb_warn, + ) self.db.set_task_state(task_id=task_id, state="SUBMITTED") if submitted != ray_sid: self.db.set_task_state(task_id=task_id, state="SUBMITTED", event_type="WARN_SUBMISSION_ID_MISMATCH") diff --git a/src/mvp/py/argus/service/ui.py b/src/mvp/py/argus/service/ui.py index 8a41ef4..5da48d7 100644 --- a/src/mvp/py/argus/service/ui.py +++ b/src/mvp/py/argus/service/ui.py @@ -188,11 +188,29 @@ def register_ui_routes(app: FastAPI) -> None:
(not loaded)
+
+
+

W&B

+
Weights & Biases local server (v3.6). Metrics are written by training jobs; this UI is for viewing.
+
+
+ Open W&B (:8090) + +
+
+
project: (unknown)
+
base_url (job runtime): (unknown)
+
""".strip() script = """ const tokEl = document.getElementById("tok"); const msg = document.getElementById("msg"); const me = document.getElementById("me"); +const wandbOpen = document.getElementById("wandb-open"); +const wandbProject = document.getElementById("wandb-project"); +const wandbBaseUrl = document.getElementById("wandb-base-url"); +document.getElementById("wandb-copy-project").onclick = async () => { await copyText(wandbProject.textContent || ""); }; +wandbOpen.href = curOriginWithPort(8090); tokEl.value = mvpTokenGet(); async function refreshMe() { @@ -200,8 +218,17 @@ async function refreshMe() { try { const obj = await apiJson("/api/v2/me"); me.textContent = fmtJson(obj); + if (obj.wandb && obj.wandb.enabled) { + wandbProject.textContent = obj.wandb.project_name || "(unknown)"; + wandbBaseUrl.textContent = obj.wandb.base_url || "(unknown)"; + } else { + wandbProject.textContent = "(disabled)"; + wandbBaseUrl.textContent = "(disabled)"; + } } catch (e) { me.textContent = "Error: " + (e.status || "") + "\\n" + (e.body || String(e)); + wandbProject.textContent = "(error)"; + wandbBaseUrl.textContent = "(error)"; } } @@ -358,19 +385,26 @@ val_file: /private/common/datasets/gsm8k_sft/test.parquet # 验证数据(必 # trainer_device: cpu # 仅 SFT 生效:driver 侧 device(可选,默认:cpu) # submission_id: "" # Ray submission_id(可选,默认空;通常由服务自动生成,无需填写) """.strip() - adv = """# Advanced TaskSpec (YAML) - v3.5 + adv = """# Advanced TaskSpec (YAML) - v3.6 kind: advanced # 任务类型(必填):advanced(自定义 command) -# 说明:v3.5 中 Advanced 任务不会按 ppo/grpo/sft 分类;平台统一按 "advanced" 做任务分类与 task_id 命名。 - -nnodes: 2 # 训练节点数(必填):用于平台队列调度与资源预检查 -n_gpus_per_node: 4 # 每节点 GPU 数(必填):用于平台队列调度与资源预检查 - -# 自定义训练命令(必填):平台会做 $HOME 宏替换: +# 说明:平台统一按 "advanced" 做任务分类与 task_id 命名(不按 ppo/grpo/sft 细分)。 +# +# 自定义训练命令:平台会做 $HOME 宏替换: # - $HOME -> /private/users/ # - $HOME/common/datasets -> /private/datasets(共享只读数据) # - $HOME/common/hf -> /private/hf(共享只读 HF cache) +# +# W&B(v3.6): +# - 平台会在 runtime_env 注入 WANDB_BASE_URL/WANDB_API_KEY/WANDB_DIR +# - 以及注入以下 env,供 Advanced command 使用(无需用户手动修改 project): +# - MVP_TRAINER_LOGGER: "console" 或 "[console,wandb]" +# - MVP_WANDB_PROJECT: "_project" +# - MVP_WANDB_RUN: "" +# +nnodes: 2 # 训练节点数(必填):用于平台队列调度与资源预检查 +n_gpus_per_node: 4 # 每节点 GPU 数(必填):用于平台队列调度与资源预检查 + command: | - # 注意:PPO 需要一些关键参数,否则 VERL 会在启动前 fail-fast(例如 actor 的 micro batch)。 PYTHONUNBUFFERED=1 \ python3 -m verl.trainer.main_ppo \ data.train_files=$HOME/common/datasets/gsm8k/train.parquet \ @@ -391,7 +425,9 @@ command: | critic.model.path=Qwen/Qwen2.5-0.5B-Instruct \ critic.ppo_micro_batch_size_per_gpu=4 \ algorithm.kl_ctrl.kl_coef=0.001 \ - trainer.logger=console \ + trainer.logger=${MVP_TRAINER_LOGGER} \ + trainer.project_name=${MVP_WANDB_PROJECT} \ + trainer.experiment_name=${MVP_WANDB_RUN} \ trainer.val_before_train=False \ trainer.nnodes=2 \ trainer.n_gpus_per_node=4 \ @@ -426,6 +462,32 @@ command: | --backend fsdp \ --local_dir $HOME/jobs//checkpoints//actor \ --target_dir $HOME/jobs//checkpoints//actor/huggingface +""".strip() + evaluation = """# Evaluation (YAML) - v3.6 (Advanced command) +# 用途:对“已生成的结果 parquet”做离线评估(基于 custom reward function),示例使用 VERL 内置 main_eval。 +# +# 说明: +# - 你需要准备一个包含生成 responses 的 parquet,并替换 (示例放在某个 job 目录下)。 +# - main_eval 会在 Ray 上并发计算,因此这里也用 +ray_kwargs 连接已有 Ray 集群。 +# +kind: advanced +nnodes: 1 +n_gpus_per_node: 0 + +command: | + PYTHONUNBUFFERED=1 \ + python3 -m verl.trainer.main_eval \ + data.path=$HOME/jobs//outputs/.parquet \ + +ray_kwargs.ray_init.address=auto + +# 可选:指定 parquet schema(按你的 parquet 列名调整) +# data.response_key=responses +# data.data_source_key=data_source +# data.reward_model_key=reward_model +# +# 可选:自定义 reward(方式 A:用户自行提供 reward.py) +# custom_reward_function.path=$HOME/code/reward.py +# custom_reward_function.name=compute_score """.strip() body = f"""

New Task

@@ -447,6 +509,7 @@ command: | +
@@ -603,21 +666,24 @@ command: | tpl_grpo = json.dumps(grpo) tpl_sft = json.dumps(sft) tpl_adv = json.dumps(adv) + tpl_eval = json.dumps(evaluation) tpl_merge = json.dumps(merge) script = ( """ -const msg = document.getElementById("msg"); -const yamlEl = document.getElementById("yaml"); -const TPL_PPO = __TPL_PPO__; -const TPL_GRPO = __TPL_GRPO__; -const TPL_SFT = __TPL_SFT__; -const TPL_ADV = __TPL_ADV__; -const TPL_MERGE = __TPL_MERGE__; -document.getElementById("tpl-ppo").onclick = () => { yamlEl.value = TPL_PPO; msg.textContent = ""; }; -document.getElementById("tpl-grpo").onclick = () => { yamlEl.value = TPL_GRPO; msg.textContent = ""; }; -document.getElementById("tpl-sft").onclick = () => { yamlEl.value = TPL_SFT; msg.textContent = ""; }; -document.getElementById("tpl-adv").onclick = () => { yamlEl.value = TPL_ADV; msg.textContent = ""; }; -document.getElementById("tpl-merge").onclick = () => { yamlEl.value = TPL_MERGE; msg.textContent = ""; }; + const msg = document.getElementById("msg"); + const yamlEl = document.getElementById("yaml"); + const TPL_PPO = __TPL_PPO__; + const TPL_GRPO = __TPL_GRPO__; + const TPL_SFT = __TPL_SFT__; + const TPL_ADV = __TPL_ADV__; + const TPL_EVAL = __TPL_EVAL__; + const TPL_MERGE = __TPL_MERGE__; + document.getElementById("tpl-ppo").onclick = () => { yamlEl.value = TPL_PPO; msg.textContent = ""; }; + document.getElementById("tpl-grpo").onclick = () => { yamlEl.value = TPL_GRPO; msg.textContent = ""; }; + document.getElementById("tpl-sft").onclick = () => { yamlEl.value = TPL_SFT; msg.textContent = ""; }; + document.getElementById("tpl-adv").onclick = () => { yamlEl.value = TPL_ADV; msg.textContent = ""; }; + document.getElementById("tpl-eval").onclick = () => { yamlEl.value = TPL_EVAL; msg.textContent = ""; }; + document.getElementById("tpl-merge").onclick = () => { yamlEl.value = TPL_MERGE; msg.textContent = ""; }; function yamlQuote(s) { s = String(s ?? ""); @@ -802,11 +868,12 @@ document.getElementById("submit").onclick = async () => { msg.textContent = "OK: " + fmtJson(obj); if (obj.task_id) window.location.href = "/ui/tasks/" + obj.task_id; }; -""".strip() + """.strip() .replace("__TPL_PPO__", tpl_ppo) .replace("__TPL_GRPO__", tpl_grpo) .replace("__TPL_SFT__", tpl_sft) .replace("__TPL_ADV__", tpl_adv) + .replace("__TPL_EVAL__", tpl_eval) .replace("__TPL_MERGE__", tpl_merge) ) return HTMLResponse(content=_page("New Task", "new", body, script)) @@ -827,6 +894,19 @@ document.getElementById("submit").onclick = async () => {
Loading...
+
+

W&B

+
This is a best-effort hint. Run name maps to ray_submission_id (attempt).
+
+
+ Open W&B (:8090) + +
+
+
project: (unknown)
+
run: (unknown)
+
+

TaskSpec (YAML)

Resolved TaskSpec (includes default values; submission_id reflects latest attempt when available).
@@ -836,6 +916,8 @@ document.getElementById("submit").onclick = async () => { """.strip() script = f""" document.getElementById("nav-ray-dashboard").href = curOriginWithPort(8265); +document.getElementById("wandb-open").href = curOriginWithPort(8090); +document.getElementById("wandb-copy-run").onclick = async () => {{ await copyText((document.getElementById("wandb-run").textContent || \"\").trim()); }}; const out = document.getElementById("out"); const spec = document.getElementById("spec"); async function refresh() {{ @@ -844,7 +926,18 @@ async function refresh() {{ const resp = await apiFetch("/api/v2/tasks/{task_id}"); const text = await resp.text(); if (!resp.ok) {{ out.textContent = "Error: " + resp.status + "\\n" + text; return; }} - out.textContent = fmtJson(JSON.parse(text)); + const obj = JSON.parse(text); + out.textContent = fmtJson(obj); + const p = document.getElementById("wandb-project"); + const r = document.getElementById("wandb-run"); + const w = (obj.latest_attempt && obj.latest_attempt.wandb) ? obj.latest_attempt.wandb : null; + if (w) {{ + p.textContent = w.project_name || "(unknown)"; + r.textContent = w.run_name || "(unknown)"; + }} else {{ + p.textContent = "(not available)"; + r.textContent = "(not available)"; + }} const resp2 = await apiFetch("/api/v2/tasks/{task_id}/spec"); const text2 = await resp2.text(); diff --git a/src/mvp/py/tests/test_app.py b/src/mvp/py/tests/test_app.py index fd97d19..9b4b566 100644 --- a/src/mvp/py/tests/test_app.py +++ b/src/mvp/py/tests/test_app.py @@ -164,6 +164,37 @@ def test_task_submit_get_cancel_logs_queue(tmp_path: Path, monkeypatch): assert r5.text.strip() == "c" +def test_me_includes_wandb_when_enabled(tmp_path: Path, monkeypatch): + from argus.service import app as app_mod + + cfg_path = _write_config(tmp_path) + # Enable tracking.wandb + cfg = yaml.safe_load(cfg_path.read_text(encoding="utf-8")) + cfg["tracking"] = {"wandb": {"enabled": True, "base_url": "http://172.22.0.1:8090"}} + cfg_path.write_text(yaml.safe_dump(cfg), encoding="utf-8") + + monkeypatch.setenv("MVP_INTERNAL_TOKEN", "token1") + + class _Scheduler: + def __init__(self, **kwargs): + self.tool = object() + + def run_forever(self, stop_flag): + return None + + monkeypatch.setattr(app_mod, "Scheduler", _Scheduler) + app = app_mod.create_app(str(cfg_path)) + + with TestClient(app) as c: + r = c.get("/api/v2/me", headers={"authorization": "Bearer token1"}) + assert r.status_code == 200 + obj = r.json() + assert obj["user_id"] == "admin" + assert obj.get("wandb", {}).get("enabled") is True + assert obj["wandb"]["base_url"] == "http://172.22.0.1:8090" + assert obj["wandb"]["project_name"] == "admin_project" + + def test_submit_rejects_non_mapping_jobspec(tmp_path: Path, monkeypatch): from argus.service import app as app_mod diff --git a/src/mvp/py/tests/test_scheduler.py b/src/mvp/py/tests/test_scheduler.py index b6c929a..1502f02 100644 --- a/src/mvp/py/tests/test_scheduler.py +++ b/src/mvp/py/tests/test_scheduler.py @@ -60,10 +60,21 @@ def test_tick_submits_one_task(monkeypatch, tmp_path: Path): def __init__(self, cfg): self.submitted = [] self.job_dirs = [] + self.extra_env = [] + self.extra_overrides = [] - def submit(self, spec, no_wait: bool, job_dir: str | None = None): + def submit( + self, + spec, + no_wait: bool, + job_dir: str | None = None, + extra_env_vars: dict | None = None, + extra_overrides: list[str] | None = None, + ): self.submitted.append(spec.submission_id) self.job_dirs.append(job_dir) + self.extra_env.append(extra_env_vars) + self.extra_overrides.append(extra_overrides) return str(spec.submission_id) def status(self, submission_id: str): @@ -83,6 +94,11 @@ def test_tick_submits_one_task(monkeypatch, tmp_path: Path): assert len(attempts) == 1 assert attempts[0]["ray_submission_id"] == "t1--a01" assert s.tool.job_dirs[-1] == "/private/users/alice/jobs/t1--a01" + env = s.tool.extra_env[-1] + assert env and env["MVP_WANDB_PROJECT"] == "alice_project" + assert env["MVP_WANDB_RUN"] == "t1--a01" + assert env["MVP_TRAINER_LOGGER"] == "console" + assert s.tool.extra_overrides[-1] is None def test_tick_submits_one_task_advanced(monkeypatch, tmp_path: Path): @@ -118,13 +134,23 @@ def test_tick_submits_one_task_advanced(monkeypatch, tmp_path: Path): def __init__(self, cfg): self.submitted = [] self.job_dirs = [] + self.extra_env = [] def submit(self, spec, no_wait: bool, job_dir: str | None = None): raise AssertionError("should not call submit() for advanced") - def submit_advanced(self, spec, no_wait: bool, job_dir: str | None = None, user_id: str | None = None): + def submit_advanced( + self, + spec, + no_wait: bool, + job_dir: str | None = None, + user_id: str | None = None, + extra_env_vars: dict | None = None, + extra_overrides: list[str] | None = None, + ): self.submitted.append(spec.submission_id) self.job_dirs.append(job_dir) + self.extra_env.append(extra_env_vars) return str(spec.submission_id) def status(self, submission_id: str): @@ -144,6 +170,252 @@ def test_tick_submits_one_task_advanced(monkeypatch, tmp_path: Path): assert len(attempts) == 1 assert attempts[0]["ray_submission_id"] == "t1--a01" assert s.tool.job_dirs[-1] == "/private/users/alice/jobs/t1--a01" + env = s.tool.extra_env[-1] + assert env and env["MVP_WANDB_PROJECT"] == "alice_project" + assert env["MVP_WANDB_RUN"] == "t1--a01" + assert env["MVP_TRAINER_LOGGER"] == "console" + + +def test_tick_injects_wandb_env_when_enabled(monkeypatch, tmp_path: Path): + from argus.service import scheduler as sched_mod + + root = { + "ray": { + "address": "http://127.0.0.1:8265", + "shared_root": "/private", + "entrypoint_resources": {"worker_node": 1}, + "runtime_env": {"env_vars": {}}, + }, + "service": { + "sqlite": {"db_path": str(tmp_path / "mvp.sqlite3")}, + "scheduler": {"tick_s": 1, "retry_interval_s": 1, "max_running_tasks": 1}, + }, + "tracking": {"wandb": {"enabled": True, "base_url": "http://172.22.0.1:8090"}}, + } + ray_cfg, v2_cfg = RayConfig.from_dict(root), V2Config.from_root_dict(root) + db = Db(v2_cfg.sqlite.db_path) + db.init() + db.create_task_v25( + task_id="t1", + user_id="alice", + workload="ppo", + jobspec_yaml=yaml.safe_dump( + { + "workload": "ppo", + "code_path": "/private/common/code/verl", + "model_id": "m", + "train_file": "/private/common/datasets/t", + "val_file": "/private/common/datasets/v", + } + ), + nnodes=2, + n_gpus_per_node=4, + ) + + monkeypatch.setenv("WANDB_API_KEY", "k") + monkeypatch.setattr(sched_mod, "ensure_ray_connected", lambda: None) + monkeypatch.setattr( + sched_mod, + "get_cluster_available", + lambda: SimpleNamespace(total_available_gpus=999.0, total_available_npus=0.0), + ) + + class _Tool: + def __init__(self, cfg): + self.extra_env = [] + self.job_dirs = [] + + def submit( + self, + spec, + no_wait: bool, + job_dir: str | None = None, + extra_env_vars: dict | None = None, + extra_overrides: list[str] | None = None, + ): + self.job_dirs.append(job_dir) + self.extra_env.append(extra_env_vars) + return str(spec.submission_id) + + def status(self, submission_id: str): + return "RUNNING" + + def logs(self, submission_id: str): + return "" + + monkeypatch.setattr(sched_mod, "RayJobTool", _Tool) + + s = sched_mod.Scheduler(db=db, ray_cfg=ray_cfg, v2_cfg=v2_cfg) + s.tick() + + assert s.tool.job_dirs[-1] == "/private/users/alice/jobs/t1--a01" + env = s.tool.extra_env[-1] + assert env and env["WANDB_API_KEY"] == "k" + assert env["WANDB_BASE_URL"] == "http://172.22.0.1:8090" + assert env["WANDB_MODE"] == "online" + assert env["WANDB_DIR"] == "/private/users/alice/jobs/t1--a01/wandb" + assert env["MVP_WANDB_PROJECT"] == "alice_project" + assert env["MVP_WANDB_RUN"] == "t1--a01" + assert env["MVP_TRAINER_LOGGER"] == "[console,wandb]" + + +def test_tick_wandb_enabled_missing_key_degrades(monkeypatch, tmp_path: Path): + from argus.service import scheduler as sched_mod + + root = { + "ray": { + "address": "http://127.0.0.1:8265", + "shared_root": "/private", + "entrypoint_resources": {"worker_node": 1}, + "runtime_env": {"env_vars": {}}, + }, + "service": { + "sqlite": {"db_path": str(tmp_path / "mvp.sqlite3")}, + "scheduler": {"tick_s": 1, "retry_interval_s": 1, "max_running_tasks": 1}, + }, + "tracking": {"wandb": {"enabled": True, "base_url": "http://172.22.0.1:8090"}}, + } + ray_cfg, v2_cfg = RayConfig.from_dict(root), V2Config.from_root_dict(root) + db = Db(v2_cfg.sqlite.db_path) + db.init() + db.create_task_v25( + task_id="t1", + user_id="alice", + workload="ppo", + jobspec_yaml=yaml.safe_dump( + { + "workload": "ppo", + "code_path": "/private/common/code/verl", + "model_id": "m", + "train_file": "/private/common/datasets/t", + "val_file": "/private/common/datasets/v", + } + ), + nnodes=2, + n_gpus_per_node=4, + ) + + monkeypatch.delenv("WANDB_API_KEY", raising=False) + monkeypatch.setattr(sched_mod, "ensure_ray_connected", lambda: None) + monkeypatch.setattr( + sched_mod, + "get_cluster_available", + lambda: SimpleNamespace(total_available_gpus=999.0, total_available_npus=0.0), + ) + + class _Tool: + def __init__(self, cfg): + self.extra_env = [] + + def submit( + self, + spec, + no_wait: bool, + job_dir: str | None = None, + extra_env_vars: dict | None = None, + extra_overrides: list[str] | None = None, + ): + self.extra_env.append(extra_env_vars) + return str(spec.submission_id) + + def status(self, submission_id: str): + return "RUNNING" + + def logs(self, submission_id: str): + return "" + + monkeypatch.setattr(sched_mod, "RayJobTool", _Tool) + + s = sched_mod.Scheduler(db=db, ray_cfg=ray_cfg, v2_cfg=v2_cfg) + s.tick() + + env = s.tool.extra_env[-1] + assert env and env["MVP_WANDB_PROJECT"] == "alice_project" + assert env["MVP_WANDB_RUN"] == "t1--a01" + assert env["MVP_TRAINER_LOGGER"] == "console" + attempts = db.list_attempts("t1") + assert attempts and "WANDB_API_KEY" in str(attempts[0].get("message") or "") + + +def test_tick_wandb_enabled_updates_jobspec_logger(monkeypatch, tmp_path: Path): + from argus.service import scheduler as sched_mod + + root = { + "ray": { + "address": "http://127.0.0.1:8265", + "shared_root": "/private", + "entrypoint_resources": {"worker_node": 1}, + "runtime_env": {"env_vars": {}}, + }, + "service": { + "sqlite": {"db_path": str(tmp_path / "mvp.sqlite3")}, + "scheduler": {"tick_s": 1, "retry_interval_s": 1, "max_running_tasks": 1}, + }, + "tracking": {"wandb": {"enabled": True, "base_url": "http://172.22.0.1:8090"}}, + } + ray_cfg, v2_cfg = RayConfig.from_dict(root), V2Config.from_root_dict(root) + db = Db(v2_cfg.sqlite.db_path) + db.init() + db.create_task_v25( + task_id="t1", + user_id="alice", + workload="ppo", + jobspec_yaml=yaml.safe_dump( + { + "workload": "ppo", + "code_path": "/private/common/code/verl", + "model_id": "m", + "train_file": "/private/common/datasets/t", + "val_file": "/private/common/datasets/v", + } + ), + nnodes=2, + n_gpus_per_node=4, + ) + + monkeypatch.setenv("WANDB_API_KEY", "k") + monkeypatch.setattr(sched_mod, "ensure_ray_connected", lambda: None) + monkeypatch.setattr( + sched_mod, + "get_cluster_available", + lambda: SimpleNamespace(total_available_gpus=999.0, total_available_npus=0.0), + ) + + class _Tool: + def __init__(self, cfg): + self.specs = [] + self.extra_overrides = [] + + def submit( + self, + spec, + no_wait: bool, + job_dir: str | None = None, + extra_env_vars: dict | None = None, + extra_overrides: list[str] | None = None, + ): + self.specs.append(spec) + self.extra_overrides.append(extra_overrides) + return str(spec.submission_id) + + def status(self, submission_id: str): + return "RUNNING" + + def logs(self, submission_id: str): + return "" + + monkeypatch.setattr(sched_mod, "RayJobTool", _Tool) + + s = sched_mod.Scheduler(db=db, ray_cfg=ray_cfg, v2_cfg=v2_cfg) + s.tick() + + spec = s.tool.specs[-1] + # v3.6: built-in training should be configured to use wandb when enabled and API key exists. + assert spec.workload == "ppo" + ov = s.tool.extra_overrides[-1] + assert ov and "trainer.logger=[console,wandb]" in ov + assert ov and "trainer.project_name=alice_project" in ov + assert ov and "trainer.experiment_name=t1--a01" in ov def test_tick_marks_pending_resources(monkeypatch, tmp_path: Path): diff --git a/src/mvp/py/tests/test_service_config.py b/src/mvp/py/tests/test_service_config.py index 9fe7226..d75b93f 100644 --- a/src/mvp/py/tests/test_service_config.py +++ b/src/mvp/py/tests/test_service_config.py @@ -22,6 +22,7 @@ def test_v2_config_from_root_dict_new_format_defaults(): assert cfg.auth.token_env == "X" assert cfg.sqlite.db_path.endswith(".sqlite3") assert cfg.scheduler.max_running_tasks == 3 + assert cfg.tracking.wandb.enabled is False def test_v2_config_backward_compat_v2_section_and_default_db_path(): @@ -29,6 +30,7 @@ def test_v2_config_backward_compat_v2_section_and_default_db_path(): cfg = V2Config.from_root_dict({"shared_root": "/private", "v2": {"sqlite": {}}}) assert cfg.sqlite.db_path == "/private/common/db/mvp.sqlite3" + assert cfg.tracking.wandb.enabled is False def test_v2_config_requires_mappings(): @@ -53,3 +55,49 @@ def test_v2_config_requires_data_mappings(): with pytest.raises(ValueError, match="config\\.data\\.\\{sftpgo,retention\\} must be mappings"): V2Config.from_root_dict({**base, "data": {"sftpgo": ["x"], "retention": {}}}) + + +def test_tracking_wandb_defaults_disabled(): + from argus.service.config import V2Config + + cfg = V2Config.from_root_dict( + { + "ray": {"shared_root": "/private"}, + "service": {"api": {}, "auth": {}, "sqlite": {}, "scheduler": {}}, + "data": {"sftpgo": {}, "retention": {}}, + } + ) + assert cfg.tracking.wandb.enabled is False + assert cfg.tracking.wandb.base_url == "" + assert cfg.tracking.wandb.api_key_env == "WANDB_API_KEY" + assert cfg.tracking.wandb.project_suffix == "_project" + + +def test_tracking_wandb_enabled_parses(): + from argus.service.config import V2Config + + cfg = V2Config.from_root_dict( + { + "ray": {"shared_root": "/private"}, + "service": {"api": {}, "auth": {}, "sqlite": {}, "scheduler": {}}, + "tracking": {"wandb": {"enabled": True, "base_url": "http://127.0.0.1:8090", "api_key_env": "X"}}, + "data": {"sftpgo": {}, "retention": {}}, + } + ) + assert cfg.tracking.wandb.enabled is True + assert cfg.tracking.wandb.base_url == "http://127.0.0.1:8090" + assert cfg.tracking.wandb.api_key_env == "X" + + +def test_tracking_wandb_enabled_requires_base_url(): + from argus.service.config import V2Config + + with pytest.raises(ValueError, match="tracking\\.wandb\\.base_url is required"): + V2Config.from_root_dict( + { + "ray": {"shared_root": "/private"}, + "service": {"api": {}, "auth": {}, "sqlite": {}, "scheduler": {}}, + "tracking": {"wandb": {"enabled": True, "base_url": ""}}, + "data": {"sftpgo": {}, "retention": {}}, + } + ) diff --git a/src/mvp/py/tests/test_ui.py b/src/mvp/py/tests/test_ui.py index 8255fbf..d110c9e 100644 --- a/src/mvp/py/tests/test_ui.py +++ b/src/mvp/py/tests/test_ui.py @@ -93,5 +93,35 @@ def test_ui_new_task_contains_advanced_example_snippet(tmp_path, monkeypatch): # workload is not needed for advanced in v3.5. assert "# workload:" not in r.text assert "actor_rollout_ref.actor.ppo_micro_batch_size_per_gpu" in r.text + # v3.6: Advanced example uses platform-injected env vars so users don't need to edit W&B project/run. + assert "trainer.logger=${MVP_TRAINER_LOGGER}" in r.text + assert "trainer.project_name=${MVP_WANDB_PROJECT}" in r.text + assert "trainer.experiment_name=${MVP_WANDB_RUN}" in r.text assert "Model merge example" in r.text assert "verl.model_merger" in r.text + + +def test_ui_new_task_contains_evaluation_example(tmp_path, monkeypatch): + cfg = _write_config(tmp_path) + monkeypatch.setenv("MVP_INTERNAL_TOKEN", "admin-token") + app = create_app(str(cfg)) + c = TestClient(app) + + r = c.get("/ui/tasks/new") + assert r.status_code == 200 + assert "Evaluation example" in r.text + assert "verl.trainer.main_eval" in r.text + assert "data.path=$HOME/jobs//outputs/.parquet" in r.text + assert "+ray_kwargs.ray_init.address=auto" in r.text + + +def test_ui_login_contains_wandb_section(tmp_path, monkeypatch): + cfg = _write_config(tmp_path) + monkeypatch.setenv("MVP_INTERNAL_TOKEN", "admin-token") + app = create_app(str(cfg)) + c = TestClient(app) + + r = c.get("/ui/login") + assert r.status_code == 200 + assert "W&B" in r.text + assert "Open W&B" in r.text diff --git a/src/mvp/scripts/00_prereq_check.sh b/src/mvp/scripts/00_prereq_check.sh index c6335df..af85e02 100755 --- a/src/mvp/scripts/00_prereq_check.sh +++ b/src/mvp/scripts/00_prereq_check.sh @@ -31,6 +31,10 @@ echo "[host] ensure shared dirs exist under ../../shared" mkdir -p "${ROOT_DIR}/../../shared"/{datasets,hf,jobs,outputs,ray,common,user} mkdir -p "${ROOT_DIR}/../../shared/common"/{code,datasets,models} mkdir -p "${ROOT_DIR}/../../shared/user"/{code} +# v3.6: W&B local server persists metadata under /private/common/wandb (mounted as /vol). +# The wandb container runs as a non-root user; ensure the bind mount is writable. +mkdir -p "${ROOT_DIR}/../../shared/common/wandb" +chmod 777 "${ROOT_DIR}/../../shared/common/wandb" 2>/dev/null || true echo "[host] ensure verl repo exists under ../../verl (required by prepare scripts)" if [[ ! -d "${ROOT_DIR}/../../verl" ]]; then diff --git a/src/mvp/scripts/60_start_api.sh b/src/mvp/scripts/60_start_api.sh index 3c18ae6..ab04e12 100755 --- a/src/mvp/scripts/60_start_api.sh +++ b/src/mvp/scripts/60_start_api.sh @@ -25,6 +25,9 @@ fi env_args=(-e "MVP_INTERNAL_TOKEN=${MVP_INTERNAL_TOKEN}") # If host does not provide it, fall back to the dev default used by docker-compose (kept in sync). env_args+=(-e "SFTPGO_ADMIN_PASSWORD=${SFTPGO_ADMIN_PASSWORD:-my-dev-sftpgo-admin}") +if [[ -n "${WANDB_API_KEY:-}" ]]; then + env_args+=(-e "WANDB_API_KEY=${WANDB_API_KEY}") +fi docker exec -d "${env_args[@]}" "${HEAD_CONTAINER}" bash -lc "nohup python3 /workspace/mvp/py/server.py --config '${CONFIG_IN_CONTAINER}' >>'${LOG_PATH}' 2>&1 & echo \$! >'${PID_PATH}'"