diff --git a/conf/db/upgrade/V5.5.22__schema.sql b/conf/db/upgrade/V5.5.22__schema.sql index e69de29bb2d..94b14f06535 100644 --- a/conf/db/upgrade/V5.5.22__schema.sql +++ b/conf/db/upgrade/V5.5.22__schema.sql @@ -0,0 +1,31 @@ +-- ZSTAC-84025: Add pipelineTag to ModelVO for inference template auto-matching +CALL ADD_COLUMN('ModelVO', 'pipelineTag', 'VARCHAR(64)', 1, NULL); + +-- ZSTAC-84025: Add isDefault to ModelServiceRefVO to mark the default inference template per model +ALTER TABLE `zstack`.`ModelServiceRefVO` ADD COLUMN `isDefault` TINYINT(1) NOT NULL DEFAULT 0; + +-- ZSTAC-84025-F2: Add manifestJson to ModelVO so Step 1 (file format) of the auto-match Matcher can +-- parse file_types/file_extensions from the manifest returned by the aios agent. +CALL ADD_COLUMN('ModelVO', 'manifestJson', 'TEXT', 1, NULL); + +-- ZSTAC-84025: Add createDate/lastOpDate to ModelServiceRefVO so the auto-match Matcher can +-- pick the earliest isDefault=true row when DB has the rare 2+ defaults anomaly (Q5). +ALTER TABLE `zstack`.`ModelServiceRefVO` ADD COLUMN `lastOpDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP; +ALTER TABLE `zstack`.`ModelServiceRefVO` ADD COLUMN `createDate` TIMESTAMP NULL DEFAULT NULL; + +DROP PROCEDURE IF EXISTS backfill_model_service_ref_create_date; +DELIMITER $$ +CREATE PROCEDURE backfill_model_service_ref_create_date() +BEGIN + UPDATE `zstack`.`ModelServiceRefVO` + SET `createDate` = CURRENT_TIMESTAMP + WHERE `createDate` IS NULL OR `createDate` = '0000-00-00 00:00:00'; +END $$ +DELIMITER ; +CALL backfill_model_service_ref_create_date(); +DROP PROCEDURE IF EXISTS backfill_model_service_ref_create_date; + +-- Older MySQL/MariaDB versions allow only one TIMESTAMP column with CURRENT_TIMESTAMP +-- in DEFAULT or ON UPDATE. lastOpDate already uses it, so keep createDate non-zero +-- and let ModelServiceRefVO.@PrePersist populate the real creation time for new rows. +ALTER TABLE `zstack`.`ModelServiceRefVO` MODIFY COLUMN `createDate` TIMESTAMP NOT NULL DEFAULT '2000-01-01 00:00:00'; diff --git a/sdk/src/main/java/SourceClassMap.java b/sdk/src/main/java/SourceClassMap.java index 5104ebb6a6f..72fbac9b302 100644 --- a/sdk/src/main/java/SourceClassMap.java +++ b/sdk/src/main/java/SourceClassMap.java @@ -29,6 +29,8 @@ public class SourceClassMap { put("org.zstack.ai.entity.VmModelMountStatus", "org.zstack.sdk.VmModelMountStatus"); put("org.zstack.ai.message.ArchitectureImageMapping", "org.zstack.sdk.ArchitectureImageMapping"); put("org.zstack.ai.message.MaaSUsage", "org.zstack.sdk.MaaSUsage"); + put("org.zstack.ai.message.MatchEvidence", "org.zstack.sdk.MatchEvidence"); + put("org.zstack.ai.message.MatchedStep", "org.zstack.sdk.MatchedStep"); put("org.zstack.ai.message.ModelCenterServiceInventory", "org.zstack.sdk.ModelCenterServiceInventory"); put("org.zstack.ai.message.ModelCenterServiceInventory$MetaServerService", "org.zstack.sdk.MetaServerService"); put("org.zstack.ai.message.ModelCenterServiceInventory$ServiceStatus", "org.zstack.sdk.ServiceStatus"); @@ -1241,6 +1243,8 @@ public class SourceClassMap { put("org.zstack.sdk.LunInventory", "org.zstack.header.storageDevice.LunInventory"); put("org.zstack.sdk.MaaSUsage", "org.zstack.ai.message.MaaSUsage"); put("org.zstack.sdk.ManagementNodeInventory", "org.zstack.header.managementnode.ManagementNodeInventory"); + put("org.zstack.sdk.MatchEvidence", "org.zstack.ai.message.MatchEvidence"); + put("org.zstack.sdk.MatchedStep", "org.zstack.ai.message.MatchedStep"); put("org.zstack.sdk.MdevDeviceChooser", "org.zstack.pciDevice.virtual.vfio_mdev.MdevDeviceChooser"); put("org.zstack.sdk.MdevDeviceInventory", "org.zstack.pciDevice.virtual.vfio_mdev.MdevDeviceInventory"); put("org.zstack.sdk.MdevDeviceSpecInventory", "org.zstack.pciDevice.specification.mdev.MdevDeviceSpecInventory"); diff --git a/sdk/src/main/java/org/zstack/sdk/AddModelAction.java b/sdk/src/main/java/org/zstack/sdk/AddModelAction.java index 6e3d3d27bc2..eb9a4a4cbf7 100644 --- a/sdk/src/main/java/org/zstack/sdk/AddModelAction.java +++ b/sdk/src/main/java/org/zstack/sdk/AddModelAction.java @@ -73,6 +73,9 @@ public Result throwExceptionIfError() { @Param(required = false, validValues = {"Public"}, nonempty = false, nullElements = false, emptyString = true, noTrim = false) public java.lang.String shareMode; + @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String defaultModelServiceUuid; + @Param(required = false) public java.lang.String resourceUuid; diff --git a/sdk/src/main/java/org/zstack/sdk/AutoMatchModelServiceByModelAction.java b/sdk/src/main/java/org/zstack/sdk/AutoMatchModelServiceByModelAction.java new file mode 100644 index 00000000000..51765809a69 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/AutoMatchModelServiceByModelAction.java @@ -0,0 +1,95 @@ +package org.zstack.sdk; + +import java.util.HashMap; +import java.util.Map; +import org.zstack.sdk.*; + +public class AutoMatchModelServiceByModelAction extends AbstractAction { + + private static final HashMap parameterMap = new HashMap<>(); + + private static final HashMap nonAPIParameterMap = new HashMap<>(); + + public static class Result { + public ErrorCode error; + public org.zstack.sdk.AutoMatchModelServiceByModelResult value; + + public Result throwExceptionIfError() { + if (error != null) { + throw new ApiException( + String.format("error[code: %s, description: %s, details: %s, globalErrorCode: %s]", error.code, error.description, error.details, error.globalErrorCode) + ); + } + + return this; + } + } + + @Param(required = true, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String modelUuid; + + @Param(required = false) + public java.util.List systemTags; + + @Param(required = false) + public java.util.List userTags; + + @Param(required = false) + public String sessionId; + + @Param(required = false) + public String accessKeyId; + + @Param(required = false) + public String accessKeySecret; + + @Param(required = false) + public String requestIp; + + + private Result makeResult(ApiResult res) { + Result ret = new Result(); + if (res.error != null) { + ret.error = res.error; + return ret; + } + + org.zstack.sdk.AutoMatchModelServiceByModelResult value = res.getResult(org.zstack.sdk.AutoMatchModelServiceByModelResult.class); + ret.value = value == null ? new org.zstack.sdk.AutoMatchModelServiceByModelResult() : value; + + return ret; + } + + public Result call() { + ApiResult res = ZSClient.call(this); + return makeResult(res); + } + + public void call(final Completion completion) { + ZSClient.call(this, new InternalCompletion() { + @Override + public void complete(ApiResult res) { + completion.complete(makeResult(res)); + } + }); + } + + protected Map getParameterMap() { + return parameterMap; + } + + protected Map getNonAPIParameterMap() { + return nonAPIParameterMap; + } + + protected RestInfo getRestInfo() { + RestInfo info = new RestInfo(); + info.httpMethod = "GET"; + info.path = "/ai/models/{modelUuid}/auto-match-service"; + info.needSession = true; + info.needPoll = false; + info.parameterName = ""; + return info; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/AutoMatchModelServiceByModelResult.java b/sdk/src/main/java/org/zstack/sdk/AutoMatchModelServiceByModelResult.java new file mode 100644 index 00000000000..9d53b9056fb --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/AutoMatchModelServiceByModelResult.java @@ -0,0 +1,31 @@ +package org.zstack.sdk; + +import org.zstack.sdk.MatchedStep; +import org.zstack.sdk.MatchEvidence; + +public class AutoMatchModelServiceByModelResult { + public java.lang.String recommendedServiceUuid; + public void setRecommendedServiceUuid(java.lang.String recommendedServiceUuid) { + this.recommendedServiceUuid = recommendedServiceUuid; + } + public java.lang.String getRecommendedServiceUuid() { + return this.recommendedServiceUuid; + } + + public MatchedStep matchedByStep; + public void setMatchedByStep(MatchedStep matchedByStep) { + this.matchedByStep = matchedByStep; + } + public MatchedStep getMatchedByStep() { + return this.matchedByStep; + } + + public MatchEvidence evidence; + public void setEvidence(MatchEvidence evidence) { + this.evidence = evidence; + } + public MatchEvidence getEvidence() { + return this.evidence; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/MatchEvidence.java b/sdk/src/main/java/org/zstack/sdk/MatchEvidence.java new file mode 100644 index 00000000000..3d297660fc0 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/MatchEvidence.java @@ -0,0 +1,47 @@ +package org.zstack.sdk; + + + +public class MatchEvidence { + + public java.lang.String matchedRefUuid; + public void setMatchedRefUuid(java.lang.String matchedRefUuid) { + this.matchedRefUuid = matchedRefUuid; + } + public java.lang.String getMatchedRefUuid() { + return this.matchedRefUuid; + } + + public java.lang.String matchedFileExtension; + public void setMatchedFileExtension(java.lang.String matchedFileExtension) { + this.matchedFileExtension = matchedFileExtension; + } + public java.lang.String getMatchedFileExtension() { + return this.matchedFileExtension; + } + + public java.lang.String matchedPipelineTag; + public void setMatchedPipelineTag(java.lang.String matchedPipelineTag) { + this.matchedPipelineTag = matchedPipelineTag; + } + public java.lang.String getMatchedPipelineTag() { + return this.matchedPipelineTag; + } + + public java.util.List detectedPipelineTags; + public void setDetectedPipelineTags(java.util.List detectedPipelineTags) { + this.detectedPipelineTags = detectedPipelineTags; + } + public java.util.List getDetectedPipelineTags() { + return this.detectedPipelineTags; + } + + public java.lang.String warning; + public void setWarning(java.lang.String warning) { + this.warning = warning; + } + public java.lang.String getWarning() { + return this.warning; + } + +} diff --git a/sdk/src/main/java/org/zstack/sdk/MatchedStep.java b/sdk/src/main/java/org/zstack/sdk/MatchedStep.java new file mode 100644 index 00000000000..65b0607c986 --- /dev/null +++ b/sdk/src/main/java/org/zstack/sdk/MatchedStep.java @@ -0,0 +1,8 @@ +package org.zstack.sdk; + +public enum MatchedStep { + USER_PRESET, + FILE_FORMAT, + PIPELINE_TAG, + FALLBACK, +} diff --git a/sdk/src/main/java/org/zstack/sdk/ModelServiceRefInventory.java b/sdk/src/main/java/org/zstack/sdk/ModelServiceRefInventory.java index 93f3f18ea78..e658e48914c 100644 --- a/sdk/src/main/java/org/zstack/sdk/ModelServiceRefInventory.java +++ b/sdk/src/main/java/org/zstack/sdk/ModelServiceRefInventory.java @@ -28,4 +28,12 @@ public java.lang.String getModelServiceUuid() { return this.modelServiceUuid; } + public java.lang.Boolean isDefault; + public void setIsDefault(java.lang.Boolean isDefault) { + this.isDefault = isDefault; + } + public java.lang.Boolean getIsDefault() { + return this.isDefault; + } + } diff --git a/sdk/src/main/java/org/zstack/sdk/UpdateModelAction.java b/sdk/src/main/java/org/zstack/sdk/UpdateModelAction.java index 3b27ed271d8..f7936631adf 100644 --- a/sdk/src/main/java/org/zstack/sdk/UpdateModelAction.java +++ b/sdk/src/main/java/org/zstack/sdk/UpdateModelAction.java @@ -58,6 +58,9 @@ public Result throwExceptionIfError() { @Param(required = false, maxLength = 255, nonempty = false, nullElements = false, emptyString = true, noTrim = false) public java.lang.String modelId; + @Param(required = false, nonempty = false, nullElements = false, emptyString = true, noTrim = false) + public java.lang.String defaultModelServiceUuid; + @Param(required = false) public java.util.List systemTags; diff --git a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy index ab4531b5ade..b505a267862 100644 --- a/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy +++ b/testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy @@ -4769,6 +4769,33 @@ abstract class ApiHelper { } + def autoMatchModelServiceByModel(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.AutoMatchModelServiceByModelAction.class) Closure c) { + def a = new org.zstack.sdk.AutoMatchModelServiceByModelAction() + a.sessionId = Test.currentEnvSpec?.session?.uuid + c.resolveStrategy = Closure.OWNER_FIRST + c.delegate = a + c() + + + if (System.getProperty("apipath") != null) { + if (a.apiId == null) { + a.apiId = Platform.uuid + } + + def tracker = new ApiPathTracker(a.apiId) + def out = errorOut(a.call()) + def path = tracker.getApiPath() + if (!path.isEmpty()) { + Test.apiPaths[a.class.name] = path.join(" --->\n") + } + + return out + } else { + return errorOut(a.call()) + } + } + + def backupDatabaseToPublicCloud(@DelegatesTo(strategy = Closure.OWNER_FIRST, value = org.zstack.sdk.BackupDatabaseToPublicCloudAction.class) Closure c) { def a = new org.zstack.sdk.BackupDatabaseToPublicCloudAction() a.sessionId = Test.currentEnvSpec?.session?.uuid diff --git a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java index c38599da944..0c66ce52f55 100644 --- a/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java +++ b/utils/src/main/java/org/zstack/utils/clouderrorcode/CloudOperationsErrorCode.java @@ -14944,6 +14944,7 @@ public class CloudOperationsErrorCode { public static final String ORG_ZSTACK_AI_10163 = "ORG_ZSTACK_AI_10163"; public static final String ORG_ZSTACK_AI_10164 = "ORG_ZSTACK_AI_10164"; public static final String ORG_ZSTACK_AI_10165 = "ORG_ZSTACK_AI_10165"; + public static final String ORG_ZSTACK_AI_10166 = "ORG_ZSTACK_AI_10166"; public static final String ORG_ZSTACK_CORE_CLOUDBUS_10000 = "ORG_ZSTACK_CORE_CLOUDBUS_10000";