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 + +