From c7b9309e634e230817d6c61a3af18266a949a560 Mon Sep 17 00:00:00 2001 From: bjkim Date: Tue, 23 Sep 2025 18:31:39 +0900 Subject: [PATCH] =?UTF-8?q?[UPDATE]=20Experiments=20API=EC=97=90=20MLflow?= =?UTF-8?q?=20=EC=97=B0=EB=8F=99=20=EB=A1=9C=EC=A7=81=20=EB=B0=8F=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=EC=97=94=ED=8B=B0=ED=8B=B0=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8=20=EB=B0=8F=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=EC=B2=98=EB=A6=AC=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?(ExperimentsController,=20ExperimentsEntity)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/ExperimentsController.java | 62 +++++++++++++++---- src/main/resources/application.properties | 7 +++ 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/src/main/java/kr/re/etri/autoflow/controllers/ExperimentsController.java b/src/main/java/kr/re/etri/autoflow/controllers/ExperimentsController.java index 7654091..e27f751 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/ExperimentsController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/ExperimentsController.java @@ -128,17 +128,15 @@ public class ExperimentsController { @PostMapping public Mono> createExperiment(@RequestBody ExperimentsEntity experiment) { - // 1️⃣ DB 저장 ExperimentsEntity saved = experimentsService.save(experiment); - // 2️⃣ Kubeflow payload Map kubeflowPayload = new HashMap<>(); kubeflowPayload.put("display_name", saved.getDisplayName()); kubeflowPayload.put("description", saved.getDescription()); kubeflowPayload.put("namespace", "default"); return webClientBuilder.build() - // Kubeflow 등록 + // 1️⃣ Kubeflow 등록 .post() .uri(kubeflowBaseUrl + "/apis/v2beta1/experiments") .contentType(MediaType.APPLICATION_JSON) @@ -168,15 +166,13 @@ public class ExperimentsController { saved.setKubeflowStorageState((String) kubeflowResp.get("storage_state")); } - // DB 업데이트 experimentsService.save(saved); log.info("Kubeflow experiment 등록 완료: {}", kubeflowResp); - // 3️⃣ MLflow 등록 + // 2️⃣ MLflow 등록 Map mlflowPayload = new HashMap<>(); mlflowPayload.put("name", saved.getDisplayName()); - mlflowPayload.put("artifact_location", "/default/artifacts"); // 필요 시 변경 - // mlflowPayload.put("tags", ...); // 태그 필요 시 설정 + mlflowPayload.put("artifact_location", "/default/artifacts"); return webClientBuilder.build() .post() @@ -186,16 +182,60 @@ public class ExperimentsController { .bodyValue(mlflowPayload) .retrieve() .bodyToMono(Map.class) - .flatMap(mlflowResp -> { - log.info("MLflow experiment 등록 완료: {}", mlflowResp); - return Mono.just(ResponseEntity.ok(saved)); + .flatMap(createResp -> { + log.info("MLflow experiment 등록 완료: {}", createResp); + String mlflowExpId = (String) createResp.get("experiment_id"); + + // 3️⃣ MLflow 조회 + return webClientBuilder + .build() + .get() + .uri(mlflowBaseUrl + "/ajax-api/2.0/mlflow/experiments/get?experiment_id=" + mlflowExpId) + .headers(headers -> headers.setBasicAuth(mlflowUser, mlflowPassword)) + .retrieve() + .bodyToMono(Map.class) + .flatMap(getResp -> { + log.info("MLflow experiment 상세 조회 완료: {}", getResp); + // 필요한 필드를 entity에 반영 + if (getResp.containsKey("experiment")) { + Map exp = (Map) getResp.get("experiment"); + + // MLflow 응답 필드 반영 + if (exp.containsKey("experiment_id")) { + saved.setMlFlowId((String) exp.get("experiment_id")); + } + + if (exp.containsKey("artifact_location")) { + saved.setMlflow_artifactLocation((String) exp.get("artifact_location")); + } + if (exp.containsKey("lifecycle_stage")) { + saved.setMlflowLifecycleStage((String) exp.get("lifecycle_stage")); + } + if (exp.containsKey("created_at")) { + // created_at은 timestamp(ms)로 올 수도 있으므로 Instant 변환 + Object createdAtObj = exp.get("created_at"); + Instant createdAtInstant = null; + if (createdAtObj instanceof Number) { + createdAtInstant = Instant.ofEpochMilli(((Number) createdAtObj).longValue()); + } else if (createdAtObj instanceof String) { + createdAtInstant = Instant.parse((String) createdAtObj); + } + if (createdAtInstant != null) { + saved.setMlflowCreatedAt( + createdAtInstant.atZone(ZoneId.of("Asia/Seoul")).toLocalDateTime() + ); + } + } + } + experimentsService.save(saved); + return Mono.just(ResponseEntity.ok(saved)); + }); }); }) .doOnError(e -> log.error("Experiment 등록 실패", e)); } - @Operation(summary = "Experiment 수정") @PutMapping("/{id}") public ResponseEntity updateExperiment( diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 044087e..7ed17df 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -57,3 +57,10 @@ minio.bucket=mlpipeline # Kubeflow kubeflow.url=http://192.168.10.135:32473/ + +# MLflow +mlflow.url=http://192.168.10.135:30128/ +mlflow.user=user +mlflow.password=LImQa2Me37nu + +