package com.zy.ai.utils;
|
|
import com.alibaba.fastjson.JSON;
|
import com.alibaba.fastjson.JSONObject;
|
import com.zy.ai.entity.AiAutoTuneChange;
|
import com.zy.ai.entity.AiAutoTuneJob;
|
import com.zy.ai.entity.AiAutoTuneMcpCall;
|
|
import java.util.List;
|
import java.util.Locale;
|
import java.util.Map;
|
|
public final class AutoTuneWriteBehaviorUtils {
|
|
public static final String WRITE_ANALYSIS_ONLY = "analysis_only";
|
public static final String WRITE_DRY_RUN = "dry_run";
|
public static final String WRITE_APPLY = "apply";
|
public static final String WRITE_ROLLBACK = "rollback";
|
public static final String WRITE_NO_CHANGE = "no_change";
|
public static final String WRITE_READ_ONLY = "read_only";
|
public static final String WRITE_FAILED = "failed";
|
public static final String WRITE_REJECTED = "rejected";
|
public static final String WRITE_UNKNOWN = "unknown";
|
|
private AutoTuneWriteBehaviorUtils() {
|
}
|
|
public static void addWriteBehavior(Map<String, Object> item, String writeBehavior) {
|
String safeWriteBehavior = isBlank(writeBehavior) ? WRITE_UNKNOWN : writeBehavior;
|
item.put("writeBehavior", safeWriteBehavior);
|
item.put("writeBehaviorLabel", writeBehaviorLabel(safeWriteBehavior));
|
}
|
|
public static String resolveJobWriteBehavior(AiAutoTuneJob job,
|
List<Map<String, Object>> mcpCalls,
|
List<Map<String, Object>> changes) {
|
if (containsWriteBehavior(mcpCalls, WRITE_ANALYSIS_ONLY)
|
|| containsWriteBehavior(changes, WRITE_ANALYSIS_ONLY)
|
|| jobSummaryIndicatesAnalysisOnly(job)) {
|
return WRITE_ANALYSIS_ONLY;
|
}
|
if (jobIndicatesSuccessfulRollback(job)
|
|| containsWriteBehavior(changes, WRITE_ROLLBACK)
|
|| hasSuccessfulRollbackCall(mcpCalls, changes)) {
|
return WRITE_ROLLBACK;
|
}
|
if (containsWriteBehavior(mcpCalls, WRITE_APPLY) || containsWriteBehavior(changes, WRITE_APPLY)) {
|
return WRITE_APPLY;
|
}
|
if (jobIndicatesStatus(job, WRITE_FAILED)
|
|| containsWriteBehavior(mcpCalls, WRITE_FAILED)
|
|| containsWriteBehavior(changes, WRITE_FAILED)) {
|
return WRITE_FAILED;
|
}
|
if (jobIndicatesStatus(job, WRITE_REJECTED)
|
|| containsWriteBehavior(mcpCalls, WRITE_REJECTED)
|
|| containsWriteBehavior(changes, WRITE_REJECTED)) {
|
return WRITE_REJECTED;
|
}
|
if (onlyReadOnlyCalls(mcpCalls, changes)) {
|
return WRITE_READ_ONLY;
|
}
|
if (jobIndicatesNoChange(job, mcpCalls, changes)) {
|
return WRITE_NO_CHANGE;
|
}
|
if (containsWriteBehavior(mcpCalls, WRITE_DRY_RUN) || containsWriteBehavior(changes, WRITE_DRY_RUN)) {
|
return WRITE_DRY_RUN;
|
}
|
return WRITE_UNKNOWN;
|
}
|
|
public static String resolveMcpWriteBehavior(AiAutoTuneMcpCall mcpCall) {
|
if (mcpCall == null) {
|
return WRITE_UNKNOWN;
|
}
|
String toolName = normalizeLower(mcpCall.getToolName());
|
if (isRollbackTool(toolName)) {
|
return resolveRollbackMcpWriteBehavior(mcpCall);
|
}
|
String failedBehavior = failedOrRejectedBehavior(mcpCall.getStatus());
|
if (isApplyTool(toolName)) {
|
Boolean dryRun = toBoolean(mcpCall.getDryRun());
|
if (Boolean.TRUE.equals(dryRun)) {
|
JSONObject response = parseJsonObject(mcpCall.getResponseJson());
|
if (responseIndicatesAnalysisOnly(response) || textIndicatesAnalysisOnly(mcpCall.getErrorMessage())) {
|
return WRITE_ANALYSIS_ONLY;
|
}
|
return failedBehavior == null ? WRITE_DRY_RUN : failedBehavior;
|
}
|
if (!Boolean.FALSE.equals(dryRun)) {
|
return failedBehavior == null ? WRITE_UNKNOWN : failedBehavior;
|
}
|
return resolveRealApplyMcpWriteBehavior(mcpCall);
|
}
|
if (isReadOnlyTool(toolName)) {
|
return failedBehavior == null ? WRITE_READ_ONLY : failedBehavior;
|
}
|
return WRITE_UNKNOWN;
|
}
|
|
public static String resolveChangeWriteBehavior(AiAutoTuneChange change) {
|
return resolveChangeWriteBehavior(change, null);
|
}
|
|
public static String resolveChangeWriteBehavior(AiAutoTuneChange change, String ownerTriggerType) {
|
if (change == null) {
|
return WRITE_UNKNOWN;
|
}
|
String resultStatus = normalizeLower(change.getResultStatus());
|
if (WRITE_DRY_RUN.equals(resultStatus)) {
|
return WRITE_DRY_RUN;
|
}
|
if ("success".equals(resultStatus)) {
|
return isRollbackOwnerTriggerType(ownerTriggerType) ? WRITE_ROLLBACK : WRITE_APPLY;
|
}
|
if (WRITE_NO_CHANGE.equals(resultStatus)) {
|
return WRITE_NO_CHANGE;
|
}
|
if ("rejected".equals(resultStatus) && textIndicatesAnalysisOnly(change.getRejectReason())) {
|
return WRITE_ANALYSIS_ONLY;
|
}
|
if (WRITE_REJECTED.equals(resultStatus)) {
|
return WRITE_REJECTED;
|
}
|
if (WRITE_FAILED.equals(resultStatus)) {
|
return WRITE_FAILED;
|
}
|
return WRITE_UNKNOWN;
|
}
|
|
public static String writeBehaviorLabel(String writeBehavior) {
|
if (WRITE_ANALYSIS_ONLY.equals(writeBehavior)) {
|
return "仅分析";
|
}
|
if (WRITE_DRY_RUN.equals(writeBehavior)) {
|
return "试算";
|
}
|
if (WRITE_APPLY.equals(writeBehavior)) {
|
return "正式写入";
|
}
|
if (WRITE_ROLLBACK.equals(writeBehavior)) {
|
return "回滚";
|
}
|
if (WRITE_NO_CHANGE.equals(writeBehavior)) {
|
return "无变更";
|
}
|
if (WRITE_READ_ONLY.equals(writeBehavior)) {
|
return "只读";
|
}
|
if (WRITE_FAILED.equals(writeBehavior)) {
|
return "失败";
|
}
|
if (WRITE_REJECTED.equals(writeBehavior)) {
|
return "已拒绝";
|
}
|
return "未知";
|
}
|
|
private static boolean hasSuccessfulRollbackCall(List<Map<String, Object>> mcpCalls,
|
List<Map<String, Object>> changes) {
|
if (mcpCalls == null || mcpCalls.isEmpty()) {
|
return false;
|
}
|
for (Map<String, Object> mcpCall : mcpCalls) {
|
if (!WRITE_ROLLBACK.equals(textValue(mcpCall.get("writeBehavior")))) {
|
continue;
|
}
|
if (isSuccessStatus(mcpCall.get("status")) && rollbackHasAppliedChange(mcpCall, changes)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private static boolean rollbackHasAppliedChange(Map<String, Object> mcpCall,
|
List<Map<String, Object>> changes) {
|
JSONObject response = parseJsonObject(textValue(mcpCall.get("responseJson")));
|
if (isSuccessStatus(mcpCall.get("status")) && safeCount(mcpCall.get("successCount")) > 0) {
|
return true;
|
}
|
return responseIndicatesSuccessfulChange(response) || changesContainSuccessfulWrite(changes);
|
}
|
|
private static boolean containsWriteBehavior(List<Map<String, Object>> items, String writeBehavior) {
|
if (items == null || items.isEmpty()) {
|
return false;
|
}
|
for (Map<String, Object> item : items) {
|
if (item != null && writeBehavior.equals(textValue(item.get("writeBehavior")))) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private static boolean onlyReadOnlyCalls(List<Map<String, Object>> mcpCalls,
|
List<Map<String, Object>> changes) {
|
if (mcpCalls == null || mcpCalls.isEmpty() || (changes != null && !changes.isEmpty())) {
|
return false;
|
}
|
for (Map<String, Object> mcpCall : mcpCalls) {
|
if (!WRITE_READ_ONLY.equals(textValue(mcpCall.get("writeBehavior")))) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
private static boolean jobIndicatesNoChange(AiAutoTuneJob job,
|
List<Map<String, Object>> mcpCalls,
|
List<Map<String, Object>> changes) {
|
if (job != null && WRITE_NO_CHANGE.equals(normalizeLower(job.getStatus()))) {
|
return true;
|
}
|
if (allChangesAreNoChange(changes)) {
|
return true;
|
}
|
return containsWriteBehavior(mcpCalls, WRITE_NO_CHANGE);
|
}
|
|
private static boolean jobIndicatesStatus(AiAutoTuneJob job, String status) {
|
return job != null && status.equals(normalizeLower(job.getStatus()));
|
}
|
|
private static boolean jobIndicatesSuccessfulRollback(AiAutoTuneJob job) {
|
if (job == null || !isRollbackOwnerTriggerType(job.getTriggerType())) {
|
return false;
|
}
|
return isSuccessStatus(job.getStatus()) && safeCount(job.getSuccessCount()) > 0;
|
}
|
|
private static boolean allChangesAreNoChange(List<Map<String, Object>> changes) {
|
if (changes == null || changes.isEmpty()) {
|
return false;
|
}
|
for (Map<String, Object> change : changes) {
|
if (!WRITE_NO_CHANGE.equals(textValue(change.get("writeBehavior")))) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
private static boolean jobSummaryIndicatesAnalysisOnly(AiAutoTuneJob job) {
|
if (job == null) {
|
return false;
|
}
|
return textIndicatesAnalysisOnly(job.getSummary()) || textIndicatesAnalysisOnly(job.getErrorMessage());
|
}
|
|
private static String resolveRealApplyMcpWriteBehavior(AiAutoTuneMcpCall mcpCall) {
|
JSONObject response = parseJsonObject(mcpCall.getResponseJson());
|
if (responseIndicatesAnalysisOnly(response) || textIndicatesAnalysisOnly(mcpCall.getErrorMessage())) {
|
return WRITE_ANALYSIS_ONLY;
|
}
|
if (hasSuccessfulApplyChange(mcpCall, response)) {
|
return WRITE_APPLY;
|
}
|
String failedBehavior = failedOrRejectedBehavior(mcpCall.getStatus());
|
if (failedBehavior != null) {
|
return failedBehavior;
|
}
|
if (responseIndicatesNoChange(response)) {
|
return WRITE_NO_CHANGE;
|
}
|
if (!isSuccessStatus(mcpCall.getStatus())) {
|
return WRITE_UNKNOWN;
|
}
|
return WRITE_UNKNOWN;
|
}
|
|
private static String resolveRollbackMcpWriteBehavior(AiAutoTuneMcpCall mcpCall) {
|
JSONObject response = parseJsonObject(mcpCall.getResponseJson());
|
if (responseIndicatesAnalysisOnly(response) || textIndicatesAnalysisOnly(mcpCall.getErrorMessage())) {
|
return WRITE_ANALYSIS_ONLY;
|
}
|
if (hasSuccessfulRollbackChange(mcpCall, response)) {
|
return WRITE_ROLLBACK;
|
}
|
String failedBehavior = failedOrRejectedBehavior(mcpCall.getStatus());
|
if (failedBehavior != null) {
|
return failedBehavior;
|
}
|
if (responseIndicatesNoChange(response)) {
|
return WRITE_NO_CHANGE;
|
}
|
return WRITE_UNKNOWN;
|
}
|
|
private static boolean hasSuccessfulApplyChange(AiAutoTuneMcpCall mcpCall, JSONObject response) {
|
if (responseIndicatesSuccessfulChange(response)) {
|
return true;
|
}
|
if (responseHasChangeDetails(response)) {
|
return false;
|
}
|
return isSuccessStatus(mcpCall.getStatus()) && safeCount(mcpCall.getSuccessCount()) > 0;
|
}
|
|
private static boolean hasSuccessfulRollbackChange(AiAutoTuneMcpCall mcpCall, JSONObject response) {
|
if (responseIndicatesSuccessfulChange(response)) {
|
return true;
|
}
|
if (responseHasChangeDetails(response)) {
|
return false;
|
}
|
return isSuccessStatus(mcpCall.getStatus()) && safeCount(mcpCall.getSuccessCount()) > 0;
|
}
|
|
private static String failedOrRejectedBehavior(Object status) {
|
String normalizedStatus = normalizeLower(textValue(status));
|
if (WRITE_FAILED.equals(normalizedStatus)) {
|
return WRITE_FAILED;
|
}
|
if (WRITE_REJECTED.equals(normalizedStatus)) {
|
return WRITE_REJECTED;
|
}
|
return null;
|
}
|
|
private static boolean isReadOnlyTool(String toolName) {
|
return toolName.contains("get_auto_tune_snapshot") || toolName.contains("get_recent_auto_tune_jobs");
|
}
|
|
private static boolean isApplyTool(String toolName) {
|
return toolName.contains("apply_auto_tune_changes");
|
}
|
|
private static boolean isRollbackTool(String toolName) {
|
return toolName.contains("revert_last_auto_tune_job") || toolName.contains("rollback");
|
}
|
|
private static boolean responseIndicatesAnalysisOnly(JSONObject response) {
|
if (response == null) {
|
return false;
|
}
|
if (isTrue(response.get("analysisOnly")) || textIndicatesAnalysisOnly(textValue(response.get("summary")))) {
|
return true;
|
}
|
return changesContainAnalysisOnly(response.get("changes"));
|
}
|
|
private static boolean responseIndicatesNoChange(JSONObject response) {
|
if (response == null) {
|
return false;
|
}
|
Object changes = response.get("changes");
|
if (hasChangeDetails(changes)) {
|
return allResponseChangesHaveStatus(changes, WRITE_NO_CHANGE);
|
}
|
if (isTrue(response.get("noApply"))) {
|
return true;
|
}
|
return isTrue(response.get("success"))
|
&& safeCount(response.get("successCount")) == 0
|
&& safeCount(response.get("rejectCount")) == 0;
|
}
|
|
private static boolean responseIndicatesSuccessfulChange(JSONObject response) {
|
if (response == null) {
|
return false;
|
}
|
Object changes = response.get("changes");
|
if (hasChangeDetails(changes)) {
|
return responseChangesContainStatus(changes, "success");
|
}
|
return safeCount(response.get("successCount")) > 0;
|
}
|
|
private static boolean responseHasChangeDetails(JSONObject response) {
|
return response != null && hasChangeDetails(response.get("changes"));
|
}
|
|
private static boolean hasChangeDetails(Object changes) {
|
return changes instanceof List<?> && !((List<?>) changes).isEmpty();
|
}
|
|
private static boolean changesContainSuccessfulWrite(List<Map<String, Object>> changes) {
|
if (changes == null || changes.isEmpty()) {
|
return false;
|
}
|
for (Map<String, Object> change : changes) {
|
if (change == null) {
|
continue;
|
}
|
String writeBehavior = textValue(change.get("writeBehavior"));
|
if (WRITE_APPLY.equals(writeBehavior) || WRITE_ROLLBACK.equals(writeBehavior)) {
|
return true;
|
}
|
if ("success".equals(normalizeLower(textValue(change.get("resultStatus"))))) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private static boolean changesContainAnalysisOnly(Object changes) {
|
if (!(changes instanceof List<?>)) {
|
return false;
|
}
|
for (Object change : (List<?>) changes) {
|
if (!(change instanceof Map<?, ?>)) {
|
continue;
|
}
|
Map<?, ?> changeMap = (Map<?, ?>) change;
|
if (textIndicatesAnalysisOnly(textValue(changeMap.get("rejectReason")))) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private static boolean allResponseChangesHaveStatus(Object changes, String status) {
|
if (!(changes instanceof List<?>)) {
|
return false;
|
}
|
List<?> changeList = (List<?>) changes;
|
if (changeList.isEmpty()) {
|
return false;
|
}
|
for (Object change : changeList) {
|
if (!(change instanceof Map<?, ?>)) {
|
return false;
|
}
|
Map<?, ?> changeMap = (Map<?, ?>) change;
|
if (!status.equals(normalizeLower(textValue(changeMap.get("resultStatus"))))) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
private static boolean responseChangesContainStatus(Object changes, String status) {
|
if (!(changes instanceof List<?>)) {
|
return false;
|
}
|
for (Object change : (List<?>) changes) {
|
if (!(change instanceof Map<?, ?>)) {
|
continue;
|
}
|
Map<?, ?> changeMap = (Map<?, ?>) change;
|
if (status.equals(normalizeLower(textValue(changeMap.get("resultStatus"))))) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private static JSONObject parseJsonObject(String json) {
|
if (isBlank(json)) {
|
return null;
|
}
|
try {
|
return JSON.parseObject(json);
|
} catch (RuntimeException ignore) {
|
return null;
|
}
|
}
|
|
private static boolean textIndicatesAnalysisOnly(String text) {
|
if (isBlank(text)) {
|
return false;
|
}
|
String normalizedText = text.toLowerCase(Locale.ROOT);
|
return normalizedText.contains("仅分析")
|
|| textContainsPositiveFlag(normalizedText, "analysisonly")
|
|| textContainsPositiveFlag(normalizedText, "analysis_only")
|
|| textContainsPositiveFlag(normalizedText, "noapply")
|
|| textContainsPositiveFlag(normalizedText, "no_apply")
|
|| normalizedText.contains("禁止实际应用");
|
}
|
|
private static boolean textContainsPositiveFlag(String text, String flag) {
|
int matchIndex = text.indexOf(flag);
|
while (matchIndex >= 0) {
|
int valueStart = skipFlagSeparators(text, matchIndex + flag.length());
|
if (startsWith(text, valueStart, "false") || startsWith(text, valueStart, "0")) {
|
matchIndex = text.indexOf(flag, matchIndex + flag.length());
|
continue;
|
}
|
return true;
|
}
|
return false;
|
}
|
|
private static int skipFlagSeparators(String text, int startIndex) {
|
int index = startIndex;
|
while (index < text.length()) {
|
char character = text.charAt(index);
|
if (!Character.isWhitespace(character)
|
&& character != '='
|
&& character != ':'
|
&& character != '"'
|
&& character != '\'') {
|
break;
|
}
|
index++;
|
}
|
return index;
|
}
|
|
private static boolean startsWith(String text, int startIndex, String prefix) {
|
return startIndex <= text.length() && text.startsWith(prefix, startIndex);
|
}
|
|
private static boolean isRollbackOwnerTriggerType(String ownerTriggerType) {
|
return WRITE_ROLLBACK.equals(normalizeLower(ownerTriggerType));
|
}
|
|
private static Boolean toBoolean(Integer value) {
|
if (value == null) {
|
return null;
|
}
|
return value == 1;
|
}
|
|
private static boolean isSuccessStatus(Object status) {
|
return "success".equals(normalizeLower(textValue(status)));
|
}
|
|
private static boolean isTrue(Object value) {
|
if (value instanceof Boolean) {
|
return Boolean.TRUE.equals(value);
|
}
|
return "true".equals(normalizeLower(textValue(value))) || "1".equals(textValue(value));
|
}
|
|
private static int safeCount(Object value) {
|
if (value instanceof Number) {
|
return ((Number) value).intValue();
|
}
|
if (value == null) {
|
return 0;
|
}
|
try {
|
return Integer.parseInt(String.valueOf(value));
|
} catch (NumberFormatException ignore) {
|
return 0;
|
}
|
}
|
|
private static String normalizeLower(String value) {
|
return isBlank(value) ? "" : value.trim().toLowerCase(Locale.ROOT);
|
}
|
|
private static String textValue(Object value) {
|
return value == null ? "" : String.valueOf(value);
|
}
|
|
private static boolean isBlank(String value) {
|
return value == null || value.trim().isEmpty();
|
}
|
}
|