Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions conf/db/upgrade/V5.5.22__schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- 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;

Comment on lines +5 to +6
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

升级脚本加列方式不一致,存在重复执行失败风险。

Line 5、Line 16、Line 17 直接使用 ALTER TABLE ... ADD COLUMN,而同文件 Line 2/12 使用了 ADD_COLUMN 封装。若目标环境已存在这些列,直接 ALTER 会中断升级。建议统一改为带存在性检查的方式(如 ADD_COLUMNinformation_schema 判存)。

As per coding guidelines: "Upgrading scene has been carefully handled".

Also applies to: 16-17

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@conf/db/upgrade/V5.5.22__schema.sql` around lines 5 - 6, The upgrade script
uses raw ALTER TABLE ... ADD COLUMN for ModelServiceRefVO.`isDefault` (and the
other two ADD COLUMNs at the same file) which can fail if the column already
exists; change these raw ALTERs to the same safe pattern used elsewhere in this
file (use the ADD_COLUMN wrapper function or an existence check via
information_schema) so the script is idempotent — update the statements that add
isDefault (and the other two direct ADD COLUMN lines) to call the ADD_COLUMN
helper or surround them with a conditional existence check before ALTER TABLE.

-- Backfill isDefault: all existing refs default to 0 (no default template designated)
UPDATE `zstack`.`ModelServiceRefVO` SET `isDefault` = 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 NOT NULL DEFAULT '0000-00-00 00:00:00';
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
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<String, Parameter> parameterMap = new HashMap<>();

private static final HashMap<String, Parameter> 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;

@NonAPIParam
public long timeout = -1;

@NonAPIParam
public long pollingInterval = -1;


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<Result> completion) {
ZSClient.call(this, new InternalCompletion() {
@Override
public void complete(ApiResult res) {
completion.complete(makeResult(res));
}
});
}

protected Map<String, Parameter> getParameterMap() {
return parameterMap;
}

protected Map<String, Parameter> 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;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package org.zstack.sdk;

public class AutoMatchModelServiceByModelResult {
/**
* UUID of the recommended ModelServiceVO. May be null if FALLBACK step
* found no Transformers candidate.
*/
public java.lang.String recommendedServiceUuid;
public void setRecommendedServiceUuid(java.lang.String recommendedServiceUuid) {
this.recommendedServiceUuid = recommendedServiceUuid;
}
public java.lang.String getRecommendedServiceUuid() {
return this.recommendedServiceUuid;
}

/**
* Which matching step produced this recommendation:
* USER_PRESET | FILE_FORMAT | PIPELINE_TAG | FALLBACK
*/
public java.lang.String matchedByStep;
public void setMatchedByStep(java.lang.String matchedByStep) {
this.matchedByStep = matchedByStep;
}
public java.lang.String getMatchedByStep() {
return this.matchedByStep;
}

/**
* Diagnostic evidence for the match decision.
*/
public java.util.LinkedHashMap evidence;
public void setEvidence(java.util.LinkedHashMap evidence) {
this.evidence = evidence;
}
public java.util.LinkedHashMap getEvidence() {
return this.evidence;
}

}
3 changes: 3 additions & 0 deletions sdk/src/main/java/org/zstack/sdk/UpdateModelAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
27 changes: 27 additions & 0 deletions testlib/src/main/java/org/zstack/testlib/ApiHelper.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down