mirror of
https://github.com/xinnan-tech/xiaozhi-esp32-server.git
synced 2026-07-21 22:53:56 +08:00
Add agent configuration history
This commit is contained in:
+6
-3
@@ -127,7 +127,7 @@ public class AgentController {
|
||||
}
|
||||
AgentUpdateDTO agentUpdateDTO = new AgentUpdateDTO();
|
||||
agentUpdateDTO.setSummaryMemory(dto.getSummaryMemory());
|
||||
agentService.updateAgentById(device.getAgentId(), agentUpdateDTO);
|
||||
agentService.updateAgentById(device.getAgentId(), agentUpdateDTO, false);
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
@@ -332,8 +332,11 @@ public class AgentController {
|
||||
public Result<Void> saveAgentTags(@PathVariable String id, @RequestBody Map<String, Object> params) {
|
||||
List<String> tagIds = (List<String>) params.get("tagIds");
|
||||
List<String> tagNames = (List<String>) params.get("tagNames");
|
||||
agentTagService.saveAgentTags(id, tagIds, tagNames);
|
||||
AgentUpdateDTO dto = new AgentUpdateDTO();
|
||||
dto.setTagIds(tagIds);
|
||||
dto.setTagNames(tagNames);
|
||||
agentService.updateAgentById(id, dto);
|
||||
return new Result<Void>().ok(null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package xiaozhi.modules.agent.controller;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.AllArgsConstructor;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.user.UserDetail;
|
||||
import xiaozhi.common.utils.Result;
|
||||
import xiaozhi.modules.agent.service.AgentService;
|
||||
import xiaozhi.modules.agent.service.AgentSnapshotService;
|
||||
import xiaozhi.modules.agent.vo.AgentSnapshotVO;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
|
||||
@Tag(name = "智能体快照")
|
||||
@AllArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/agent/{agentId}/snapshots")
|
||||
public class AgentSnapshotController {
|
||||
private final AgentSnapshotService agentSnapshotService;
|
||||
private final AgentService agentService;
|
||||
|
||||
@GetMapping
|
||||
@Operation(summary = "获取智能体快照列表")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<PageData<AgentSnapshotVO>> page(
|
||||
@PathVariable String agentId,
|
||||
@RequestParam Map<String, Object> params) {
|
||||
checkPermission(agentId);
|
||||
return new Result<PageData<AgentSnapshotVO>>().ok(agentSnapshotService.page(agentId, params));
|
||||
}
|
||||
|
||||
@GetMapping("/{snapshotId}")
|
||||
@Operation(summary = "获取智能体快照详情")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<AgentSnapshotVO> getSnapshot(@PathVariable String agentId, @PathVariable String snapshotId) {
|
||||
checkPermission(agentId);
|
||||
return new Result<AgentSnapshotVO>().ok(agentSnapshotService.getSnapshot(agentId, snapshotId));
|
||||
}
|
||||
|
||||
@PostMapping("/{snapshotId}/restore")
|
||||
@Operation(summary = "恢复智能体快照")
|
||||
@RequiresPermissions("sys:role:normal")
|
||||
public Result<Void> restore(@PathVariable String agentId, @PathVariable String snapshotId) {
|
||||
checkPermission(agentId);
|
||||
agentSnapshotService.restoreSnapshot(agentId, snapshotId);
|
||||
return new Result<>();
|
||||
}
|
||||
|
||||
private void checkPermission(String agentId) {
|
||||
UserDetail user = SecurityUser.getUser();
|
||||
if (user == null || !agentService.checkAgentPermission(agentId, user.getId())) {
|
||||
throw new RenException("没有权限访问该智能体快照");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package xiaozhi.modules.agent.dao;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import xiaozhi.common.dao.BaseDao;
|
||||
import xiaozhi.modules.agent.entity.AgentSnapshotEntity;
|
||||
|
||||
@Mapper
|
||||
public interface AgentSnapshotDao extends BaseDao<AgentSnapshotEntity> {
|
||||
Integer selectMaxVersionNo(@Param("agentId") String agentId);
|
||||
|
||||
AgentSnapshotEntity selectNextSnapshot(@Param("agentId") String agentId, @Param("versionNo") Integer versionNo);
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package xiaozhi.modules.agent.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "智能体快照数据")
|
||||
public class AgentSnapshotDataDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String agentCode;
|
||||
private String agentName;
|
||||
private String asrModelId;
|
||||
private String vadModelId;
|
||||
private String llmModelId;
|
||||
private String slmModelId;
|
||||
private String vllmModelId;
|
||||
private String ttsModelId;
|
||||
private String ttsVoiceId;
|
||||
private String ttsLanguage;
|
||||
private Integer ttsVolume;
|
||||
private Integer ttsRate;
|
||||
private Integer ttsPitch;
|
||||
private String memModelId;
|
||||
private String intentModelId;
|
||||
private Integer chatHistoryConf;
|
||||
private String systemPrompt;
|
||||
private String summaryMemory;
|
||||
private String langCode;
|
||||
private String language;
|
||||
private Integer sort;
|
||||
private List<AgentUpdateDTO.FunctionInfo> functions;
|
||||
private List<ContextProviderDTO> contextProviders;
|
||||
private List<String> correctWordFileIds;
|
||||
private List<String> tagNames;
|
||||
private List<AgentSnapshotTagDTO> tags;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package xiaozhi.modules.agent.dto;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@Schema(description = "智能体快照标签")
|
||||
public class AgentSnapshotTagDTO implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private String id;
|
||||
private String tagName;
|
||||
private Integer sort;
|
||||
}
|
||||
@@ -4,9 +4,12 @@ import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import xiaozhi.common.utils.JsonUtils;
|
||||
|
||||
/**
|
||||
* 智能体更新DTO
|
||||
@@ -91,15 +94,50 @@ public class AgentUpdateDTO implements Serializable {
|
||||
@Schema(description = "替换词文件ID列表", nullable = true)
|
||||
private List<String> correctWordFileIds;
|
||||
|
||||
@Schema(description = "标签名称列表", nullable = true)
|
||||
private List<String> tagNames;
|
||||
|
||||
@Schema(description = "标签ID列表", nullable = true)
|
||||
private List<String> tagIds;
|
||||
|
||||
@Data
|
||||
@Schema(description = "插件函数信息")
|
||||
public static class FunctionInfo implements Serializable {
|
||||
private static final TypeReference<HashMap<String, Object>> PARAM_INFO_TYPE = new TypeReference<>() {
|
||||
};
|
||||
|
||||
@Schema(description = "插件ID", example = "plugin_01")
|
||||
private String pluginId;
|
||||
|
||||
@Schema(description = "函数参数信息", nullable = true)
|
||||
private HashMap<String, Object> paramInfo;
|
||||
private HashMap<String, Object> paramInfo = new HashMap<>();
|
||||
|
||||
public void setParamInfo(Object paramInfo) {
|
||||
this.paramInfo = normalizeParamInfo(paramInfo);
|
||||
}
|
||||
|
||||
private static HashMap<String, Object> normalizeParamInfo(Object paramInfo) {
|
||||
if (paramInfo == null) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
if (paramInfo instanceof String value) {
|
||||
if (value.trim().isEmpty()) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return JsonUtils.parseObject(value, PARAM_INFO_TYPE);
|
||||
}
|
||||
if (paramInfo instanceof Map<?, ?> value) {
|
||||
HashMap<String, Object> normalized = new HashMap<>();
|
||||
value.forEach((key, val) -> {
|
||||
if (key != null) {
|
||||
normalized.put(String.valueOf(key), val);
|
||||
}
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
return JsonUtils.parseObject(JsonUtils.toJsonString(paramInfo), PARAM_INFO_TYPE);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
package xiaozhi.modules.agent.entity;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
@TableName("ai_agent_snapshot")
|
||||
@Schema(description = "智能体配置快照")
|
||||
public class AgentSnapshotEntity {
|
||||
|
||||
@TableId(type = IdType.ASSIGN_UUID)
|
||||
@Schema(description = "快照ID")
|
||||
private String id;
|
||||
|
||||
@Schema(description = "智能体ID")
|
||||
private String agentId;
|
||||
|
||||
@Schema(description = "所属用户ID")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "版本号")
|
||||
private Integer versionNo;
|
||||
|
||||
@Schema(description = "快照数据JSON")
|
||||
private String snapshotData;
|
||||
|
||||
@Schema(description = "变更字段JSON")
|
||||
private String changedFields;
|
||||
|
||||
@Schema(description = "快照来源")
|
||||
private String source;
|
||||
|
||||
@Schema(description = "创建者")
|
||||
private Long creator;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private Date createdAt;
|
||||
}
|
||||
@@ -93,6 +93,15 @@ public interface AgentService extends BaseService<AgentEntity> {
|
||||
*/
|
||||
void updateAgentById(String agentId, AgentUpdateDTO dto);
|
||||
|
||||
/**
|
||||
* 更新智能体
|
||||
*
|
||||
* @param agentId 智能体ID
|
||||
* @param dto 更新智能体所需的信息
|
||||
* @param createSnapshot 是否创建配置快照
|
||||
*/
|
||||
void updateAgentById(String agentId, AgentUpdateDTO dto, boolean createSnapshot);
|
||||
|
||||
/**
|
||||
* 创建智能体
|
||||
*
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package xiaozhi.modules.agent.service;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.BaseService;
|
||||
import xiaozhi.modules.agent.dto.AgentUpdateDTO;
|
||||
import xiaozhi.modules.agent.entity.AgentSnapshotEntity;
|
||||
import xiaozhi.modules.agent.vo.AgentSnapshotVO;
|
||||
|
||||
public interface AgentSnapshotService extends BaseService<AgentSnapshotEntity> {
|
||||
void createSnapshot(String agentId, String source, AgentUpdateDTO pendingUpdate);
|
||||
|
||||
PageData<AgentSnapshotVO> page(String agentId, Map<String, Object> params);
|
||||
|
||||
AgentSnapshotVO getSnapshot(String agentId, String snapshotId);
|
||||
|
||||
void restoreSnapshot(String agentId, String snapshotId);
|
||||
}
|
||||
+2
-2
@@ -118,7 +118,7 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
|
||||
{
|
||||
setSummaryMemory(summaryDTO.getSummary());
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
log.info("成功保存会话 {} 的聊天记录总结到智能体 {}", sessionId, agentId);
|
||||
} else {
|
||||
log.info("生成总结失败: {}", summaryDTO.getErrorMessage());
|
||||
@@ -518,4 +518,4 @@ public class AgentChatSummaryServiceImpl implements AgentChatSummaryService {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
-23
@@ -43,9 +43,10 @@ import xiaozhi.modules.agent.entity.AgentTagEntity;
|
||||
import xiaozhi.modules.agent.entity.AgentTemplateEntity;
|
||||
import xiaozhi.modules.agent.service.AgentChatHistoryService;
|
||||
import xiaozhi.modules.agent.service.AgentContextProviderService;
|
||||
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
||||
import xiaozhi.modules.agent.service.AgentService;
|
||||
import xiaozhi.modules.agent.service.AgentTagService;
|
||||
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
||||
import xiaozhi.modules.agent.service.AgentService;
|
||||
import xiaozhi.modules.agent.service.AgentSnapshotService;
|
||||
import xiaozhi.modules.agent.service.AgentTagService;
|
||||
import xiaozhi.modules.agent.service.AgentTemplateService;
|
||||
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
||||
import xiaozhi.modules.correctword.service.CorrectWordFileService;
|
||||
@@ -73,9 +74,10 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
||||
private final AgentChatHistoryService agentChatHistoryService;
|
||||
private final AgentTemplateService agentTemplateService;
|
||||
private final ModelProviderService modelProviderService;
|
||||
private final AgentContextProviderService agentContextProviderService;
|
||||
private final AgentTagService agentTagService;
|
||||
private final CorrectWordFileService correctWordFileService;
|
||||
private final AgentContextProviderService agentContextProviderService;
|
||||
private final AgentTagService agentTagService;
|
||||
private final CorrectWordFileService correctWordFileService;
|
||||
private final AgentSnapshotService agentSnapshotService;
|
||||
|
||||
@Override
|
||||
public PageData<AgentEntity> adminAgentList(Map<String, Object> params) {
|
||||
@@ -272,17 +274,28 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
||||
return userId.equals(agent.getUserId());
|
||||
}
|
||||
|
||||
// 根据id更新智能体信息
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateAgentById(String agentId, AgentUpdateDTO dto) {
|
||||
// 先查询现有实体
|
||||
AgentEntity existingEntity = this.getAgentById(agentId);
|
||||
if (existingEntity == null) {
|
||||
throw new RenException(ErrorCode.AGENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
// 只更新提供的非空字段
|
||||
// 根据id更新智能体信息
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateAgentById(String agentId, AgentUpdateDTO dto) {
|
||||
updateAgentById(agentId, dto, true);
|
||||
}
|
||||
|
||||
// 根据id更新智能体信息
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateAgentById(String agentId, AgentUpdateDTO dto, boolean createSnapshot) {
|
||||
// 先查询现有实体
|
||||
AgentEntity existingEntity = this.getAgentById(agentId);
|
||||
if (existingEntity == null) {
|
||||
throw new RenException(ErrorCode.AGENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (createSnapshot) {
|
||||
agentSnapshotService.createSnapshot(agentId, "config", dto);
|
||||
}
|
||||
|
||||
// 只更新提供的非空字段
|
||||
if (dto.getAgentName() != null) {
|
||||
existingEntity.setAgentName(dto.getAgentName());
|
||||
}
|
||||
@@ -426,11 +439,16 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
||||
}
|
||||
|
||||
// 更新替换词文件关联
|
||||
if (dto.getCorrectWordFileIds() != null) {
|
||||
correctWordFileService.saveAgentCorrectWords(agentId, dto.getCorrectWordFileIds());
|
||||
}
|
||||
|
||||
boolean b = validateLLMIntentParams(dto.getLlmModelId(), dto.getIntentModelId());
|
||||
if (dto.getCorrectWordFileIds() != null) {
|
||||
correctWordFileService.saveAgentCorrectWords(agentId, dto.getCorrectWordFileIds());
|
||||
}
|
||||
|
||||
// 更新智能体标签
|
||||
if (dto.getTagNames() != null || dto.getTagIds() != null) {
|
||||
agentTagService.saveAgentTags(agentId, dto.getTagIds(), dto.getTagNames());
|
||||
}
|
||||
|
||||
boolean b = validateLLMIntentParams(dto.getLlmModelId(), dto.getIntentModelId());
|
||||
if (!b) {
|
||||
throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
|
||||
}
|
||||
@@ -576,4 +594,4 @@ public class AgentServiceImpl extends BaseServiceImpl<AgentDao, AgentEntity> imp
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+475
@@ -0,0 +1,475 @@
|
||||
package xiaozhi.modules.agent.service.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import xiaozhi.common.constant.Constant;
|
||||
import xiaozhi.common.exception.ErrorCode;
|
||||
import xiaozhi.common.exception.RenException;
|
||||
import xiaozhi.common.page.PageData;
|
||||
import xiaozhi.common.service.impl.BaseServiceImpl;
|
||||
import xiaozhi.common.utils.JsonUtils;
|
||||
import xiaozhi.modules.agent.dao.AgentDao;
|
||||
import xiaozhi.modules.agent.dao.AgentSnapshotDao;
|
||||
import xiaozhi.modules.agent.dao.AgentTagDao;
|
||||
import xiaozhi.modules.agent.dao.AgentTagRelationDao;
|
||||
import xiaozhi.modules.agent.dto.AgentSnapshotDataDTO;
|
||||
import xiaozhi.modules.agent.dto.AgentSnapshotTagDTO;
|
||||
import xiaozhi.modules.agent.dto.AgentUpdateDTO;
|
||||
import xiaozhi.modules.agent.entity.AgentContextProviderEntity;
|
||||
import xiaozhi.modules.agent.entity.AgentEntity;
|
||||
import xiaozhi.modules.agent.entity.AgentPluginMapping;
|
||||
import xiaozhi.modules.agent.entity.AgentSnapshotEntity;
|
||||
import xiaozhi.modules.agent.entity.AgentTagEntity;
|
||||
import xiaozhi.modules.agent.entity.AgentTagRelationEntity;
|
||||
import xiaozhi.modules.agent.service.AgentChatHistoryService;
|
||||
import xiaozhi.modules.agent.service.AgentContextProviderService;
|
||||
import xiaozhi.modules.agent.service.AgentPluginMappingService;
|
||||
import xiaozhi.modules.agent.service.AgentSnapshotService;
|
||||
import xiaozhi.modules.agent.service.AgentTagService;
|
||||
import xiaozhi.modules.agent.vo.AgentInfoVO;
|
||||
import xiaozhi.modules.agent.vo.AgentSnapshotVO;
|
||||
import xiaozhi.modules.correctword.service.CorrectWordFileService;
|
||||
import xiaozhi.modules.model.entity.ModelConfigEntity;
|
||||
import xiaozhi.modules.model.service.ModelConfigService;
|
||||
import xiaozhi.modules.security.user.SecurityUser;
|
||||
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class AgentSnapshotServiceImpl extends BaseServiceImpl<AgentSnapshotDao, AgentSnapshotEntity>
|
||||
implements AgentSnapshotService {
|
||||
private static final TypeReference<List<String>> STRING_LIST_TYPE = new TypeReference<>() {
|
||||
};
|
||||
private static final TypeReference<HashMap<String, Object>> PARAM_INFO_TYPE = new TypeReference<>() {
|
||||
};
|
||||
|
||||
private final AgentSnapshotDao agentSnapshotDao;
|
||||
private final AgentDao agentDao;
|
||||
private final AgentTagDao agentTagDao;
|
||||
private final AgentTagRelationDao agentTagRelationDao;
|
||||
private final AgentPluginMappingService agentPluginMappingService;
|
||||
private final AgentContextProviderService agentContextProviderService;
|
||||
private final AgentTagService agentTagService;
|
||||
private final AgentChatHistoryService agentChatHistoryService;
|
||||
private final ModelConfigService modelConfigService;
|
||||
private final CorrectWordFileService correctWordFileService;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void createSnapshot(String agentId, String source, AgentUpdateDTO pendingUpdate) {
|
||||
AgentSnapshotDataDTO snapshotData = buildSnapshotData(agentId);
|
||||
List<String> changedFields = getChangedFields(snapshotData, pendingUpdate);
|
||||
if (pendingUpdate != null && changedFields.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
AgentInfoVO agent = agentDao.selectAgentInfoById(agentId);
|
||||
if (agent == null) {
|
||||
throw new RenException(ErrorCode.AGENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
AgentSnapshotEntity entity = new AgentSnapshotEntity();
|
||||
entity.setAgentId(agentId);
|
||||
entity.setUserId(agent.getUserId());
|
||||
entity.setSnapshotData(JsonUtils.toJsonString(snapshotData));
|
||||
entity.setChangedFields(JsonUtils.toJsonString(changedFields));
|
||||
entity.setSource(StringUtils.defaultIfBlank(source, "config"));
|
||||
entity.setCreator(SecurityUser.getUserId());
|
||||
entity.setCreatedAt(new Date());
|
||||
insertSnapshotWithRetry(entity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageData<AgentSnapshotVO> page(String agentId, Map<String, Object> params) {
|
||||
IPage<AgentSnapshotEntity> page = getPage(params, "created_at", false);
|
||||
IPage<AgentSnapshotEntity> result = agentSnapshotDao.selectPage(page,
|
||||
new QueryWrapper<AgentSnapshotEntity>().eq("agent_id", agentId));
|
||||
List<AgentSnapshotVO> list = result.getRecords().stream()
|
||||
.map(entity -> toVO(entity, false))
|
||||
.toList();
|
||||
return new PageData<>(list, result.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgentSnapshotVO getSnapshot(String agentId, String snapshotId) {
|
||||
AgentSnapshotEntity entity = getSnapshotEntity(agentId, snapshotId);
|
||||
return toVO(entity, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void restoreSnapshot(String agentId, String snapshotId) {
|
||||
AgentSnapshotEntity snapshot = getSnapshotEntity(agentId, snapshotId);
|
||||
AgentSnapshotDataDTO data = JsonUtils.parseObject(snapshot.getSnapshotData(), AgentSnapshotDataDTO.class);
|
||||
if (data == null) {
|
||||
throw new RenException("快照数据为空,无法恢复");
|
||||
}
|
||||
|
||||
createSnapshot(agentId, "restore", null);
|
||||
|
||||
AgentEntity agent = agentDao.selectById(agentId);
|
||||
if (agent == null) {
|
||||
throw new RenException(ErrorCode.AGENT_NOT_FOUND);
|
||||
}
|
||||
applyAgentFields(agent, data);
|
||||
validateRestoreParams(agent);
|
||||
applyMemoryPolicy(agent);
|
||||
agent.setUpdater(SecurityUser.getUserId());
|
||||
agent.setUpdatedAt(new Date());
|
||||
agentDao.updateById(agent);
|
||||
|
||||
restoreFunctions(agentId, data.getFunctions());
|
||||
restoreContextProviders(agentId, data.getContextProviders());
|
||||
correctWordFileService.saveAgentCorrectWords(agentId, nullToEmpty(data.getCorrectWordFileIds()));
|
||||
restoreTags(agentId, data);
|
||||
}
|
||||
|
||||
private void insertSnapshotWithRetry(AgentSnapshotEntity entity) {
|
||||
for (int attempt = 0; attempt < 3; attempt++) {
|
||||
Integer maxVersionNo = agentSnapshotDao.selectMaxVersionNo(entity.getAgentId());
|
||||
entity.setVersionNo((maxVersionNo == null ? 0 : maxVersionNo) + 1);
|
||||
try {
|
||||
agentSnapshotDao.insert(entity);
|
||||
return;
|
||||
} catch (DuplicateKeyException e) {
|
||||
if (attempt == 2) {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private AgentSnapshotEntity getSnapshotEntity(String agentId, String snapshotId) {
|
||||
AgentSnapshotEntity entity = selectById(snapshotId);
|
||||
if (entity == null || !Objects.equals(agentId, entity.getAgentId())) {
|
||||
throw new RenException("快照不存在");
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
||||
private AgentSnapshotDataDTO buildSnapshotData(String agentId) {
|
||||
AgentInfoVO agent = agentDao.selectAgentInfoById(agentId);
|
||||
if (agent == null) {
|
||||
throw new RenException(ErrorCode.AGENT_NOT_FOUND);
|
||||
}
|
||||
|
||||
AgentSnapshotDataDTO data = new AgentSnapshotDataDTO();
|
||||
data.setAgentCode(agent.getAgentCode());
|
||||
data.setAgentName(agent.getAgentName());
|
||||
data.setAsrModelId(agent.getAsrModelId());
|
||||
data.setVadModelId(agent.getVadModelId());
|
||||
data.setLlmModelId(agent.getLlmModelId());
|
||||
data.setSlmModelId(agent.getSlmModelId());
|
||||
data.setVllmModelId(agent.getVllmModelId());
|
||||
data.setTtsModelId(agent.getTtsModelId());
|
||||
data.setTtsVoiceId(agent.getTtsVoiceId());
|
||||
data.setTtsLanguage(agent.getTtsLanguage());
|
||||
data.setTtsVolume(agent.getTtsVolume());
|
||||
data.setTtsRate(agent.getTtsRate());
|
||||
data.setTtsPitch(agent.getTtsPitch());
|
||||
data.setMemModelId(agent.getMemModelId());
|
||||
data.setIntentModelId(agent.getIntentModelId());
|
||||
data.setChatHistoryConf(agent.getChatHistoryConf());
|
||||
data.setSystemPrompt(agent.getSystemPrompt());
|
||||
data.setSummaryMemory(agent.getSummaryMemory());
|
||||
data.setLangCode(agent.getLangCode());
|
||||
data.setLanguage(agent.getLanguage());
|
||||
data.setSort(agent.getSort());
|
||||
data.setFunctions(toFunctionInfo(agent.getFunctions()));
|
||||
data.setContextProviders(getContextProviders(agentId));
|
||||
data.setCorrectWordFileIds(nullToEmpty(correctWordFileService.getAgentCorrectWordFileIds(agentId)));
|
||||
List<AgentSnapshotTagDTO> tags = getSnapshotTags(agentId);
|
||||
data.setTags(tags);
|
||||
data.setTagNames(tags.stream().map(AgentSnapshotTagDTO::getTagName).toList());
|
||||
return data;
|
||||
}
|
||||
|
||||
private List<AgentSnapshotTagDTO> getSnapshotTags(String agentId) {
|
||||
List<AgentTagEntity> tags = agentTagDao.selectByAgentId(agentId);
|
||||
if (tags == null || tags.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<AgentSnapshotTagDTO> snapshotTags = new ArrayList<>();
|
||||
for (int i = 0; i < tags.size(); i++) {
|
||||
AgentTagEntity tag = tags.get(i);
|
||||
AgentSnapshotTagDTO snapshotTag = new AgentSnapshotTagDTO();
|
||||
snapshotTag.setId(tag.getId());
|
||||
snapshotTag.setTagName(tag.getTagName());
|
||||
snapshotTag.setSort(i);
|
||||
snapshotTags.add(snapshotTag);
|
||||
}
|
||||
return snapshotTags;
|
||||
}
|
||||
|
||||
private List<AgentUpdateDTO.FunctionInfo> toFunctionInfo(List<AgentPluginMapping> mappings) {
|
||||
if (mappings == null || mappings.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return mappings.stream().map(mapping -> {
|
||||
AgentUpdateDTO.FunctionInfo info = new AgentUpdateDTO.FunctionInfo();
|
||||
info.setPluginId(mapping.getPluginId());
|
||||
info.setParamInfo(parseParamInfo(mapping.getParamInfo()));
|
||||
return info;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private HashMap<String, Object> parseParamInfo(String paramInfo) {
|
||||
if (StringUtils.isBlank(paramInfo)) {
|
||||
return new HashMap<>();
|
||||
}
|
||||
return JsonUtils.parseObject(paramInfo, PARAM_INFO_TYPE);
|
||||
}
|
||||
|
||||
private List<xiaozhi.modules.agent.dto.ContextProviderDTO> getContextProviders(String agentId) {
|
||||
AgentContextProviderEntity contextEntity = agentContextProviderService.getByAgentId(agentId);
|
||||
if (contextEntity == null || contextEntity.getContextProviders() == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return contextEntity.getContextProviders();
|
||||
}
|
||||
|
||||
private List<String> getChangedFields(AgentSnapshotDataDTO current, AgentUpdateDTO pendingUpdate) {
|
||||
if (pendingUpdate == null) {
|
||||
return List.of("restore");
|
||||
}
|
||||
|
||||
List<String> fields = new ArrayList<>();
|
||||
compare(fields, "agentCode", current.getAgentCode(), pendingUpdate.getAgentCode());
|
||||
compare(fields, "agentName", current.getAgentName(), pendingUpdate.getAgentName());
|
||||
compare(fields, "asrModelId", current.getAsrModelId(), pendingUpdate.getAsrModelId());
|
||||
compare(fields, "vadModelId", current.getVadModelId(), pendingUpdate.getVadModelId());
|
||||
compare(fields, "llmModelId", current.getLlmModelId(), pendingUpdate.getLlmModelId());
|
||||
compare(fields, "slmModelId", current.getSlmModelId(), pendingUpdate.getSlmModelId());
|
||||
compare(fields, "vllmModelId", current.getVllmModelId(), pendingUpdate.getVllmModelId());
|
||||
compare(fields, "ttsModelId", current.getTtsModelId(), pendingUpdate.getTtsModelId());
|
||||
compare(fields, "ttsVoiceId", current.getTtsVoiceId(), pendingUpdate.getTtsVoiceId());
|
||||
compare(fields, "ttsLanguage", current.getTtsLanguage(), pendingUpdate.getTtsLanguage());
|
||||
compare(fields, "ttsVolume", current.getTtsVolume(), pendingUpdate.getTtsVolume());
|
||||
compare(fields, "ttsRate", current.getTtsRate(), pendingUpdate.getTtsRate());
|
||||
compare(fields, "ttsPitch", current.getTtsPitch(), pendingUpdate.getTtsPitch());
|
||||
compare(fields, "memModelId", current.getMemModelId(), pendingUpdate.getMemModelId());
|
||||
compare(fields, "intentModelId", current.getIntentModelId(), pendingUpdate.getIntentModelId());
|
||||
compare(fields, "chatHistoryConf", current.getChatHistoryConf(), pendingUpdate.getChatHistoryConf());
|
||||
compare(fields, "systemPrompt", current.getSystemPrompt(), pendingUpdate.getSystemPrompt());
|
||||
compare(fields, "summaryMemory", current.getSummaryMemory(), pendingUpdate.getSummaryMemory());
|
||||
compare(fields, "langCode", current.getLangCode(), pendingUpdate.getLangCode());
|
||||
compare(fields, "language", current.getLanguage(), pendingUpdate.getLanguage());
|
||||
compare(fields, "sort", current.getSort(), pendingUpdate.getSort());
|
||||
compare(fields, "functions", current.getFunctions(), pendingUpdate.getFunctions());
|
||||
compare(fields, "contextProviders", current.getContextProviders(), pendingUpdate.getContextProviders());
|
||||
compare(fields, "correctWordFileIds", current.getCorrectWordFileIds(), pendingUpdate.getCorrectWordFileIds());
|
||||
compare(fields, "tagNames", current.getTagNames(), pendingUpdate.getTagNames());
|
||||
compare(fields, "tagIds", current.getTags().stream().map(AgentSnapshotTagDTO::getId).toList(),
|
||||
pendingUpdate.getTagIds());
|
||||
return fields;
|
||||
}
|
||||
|
||||
private <T> void compare(List<String> fields, String name, T current, T next) {
|
||||
if (next != null && !Objects.equals(current, next)) {
|
||||
fields.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyAgentFields(AgentEntity agent, AgentSnapshotDataDTO data) {
|
||||
agent.setAgentCode(data.getAgentCode());
|
||||
agent.setAgentName(data.getAgentName());
|
||||
agent.setAsrModelId(data.getAsrModelId());
|
||||
agent.setVadModelId(data.getVadModelId());
|
||||
agent.setLlmModelId(data.getLlmModelId());
|
||||
agent.setSlmModelId(data.getSlmModelId());
|
||||
agent.setVllmModelId(data.getVllmModelId());
|
||||
agent.setTtsModelId(data.getTtsModelId());
|
||||
agent.setTtsVoiceId(data.getTtsVoiceId());
|
||||
agent.setTtsLanguage(data.getTtsLanguage());
|
||||
agent.setTtsVolume(data.getTtsVolume());
|
||||
agent.setTtsRate(data.getTtsRate());
|
||||
agent.setTtsPitch(data.getTtsPitch());
|
||||
agent.setMemModelId(data.getMemModelId());
|
||||
agent.setIntentModelId(data.getIntentModelId());
|
||||
agent.setChatHistoryConf(data.getChatHistoryConf());
|
||||
agent.setSystemPrompt(data.getSystemPrompt());
|
||||
agent.setSummaryMemory(data.getSummaryMemory());
|
||||
agent.setLangCode(data.getLangCode());
|
||||
agent.setLanguage(data.getLanguage());
|
||||
agent.setSort(data.getSort());
|
||||
}
|
||||
|
||||
private void validateRestoreParams(AgentEntity agent) {
|
||||
if (agent == null || StringUtils.isBlank(agent.getLlmModelId())) {
|
||||
return;
|
||||
}
|
||||
|
||||
ModelConfigEntity llmModelData = modelConfigService.selectById(agent.getLlmModelId());
|
||||
if (llmModelData == null || llmModelData.getConfigJson() == null) {
|
||||
throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
|
||||
}
|
||||
Object typeValue = llmModelData.getConfigJson().get("type");
|
||||
String type = typeValue == null ? "" : typeValue.toString();
|
||||
if ("openai".equals(type) || "ollama".equals(type)) {
|
||||
return;
|
||||
}
|
||||
if ("Intent_function_call".equals(agent.getIntentModelId())) {
|
||||
throw new RenException(ErrorCode.LLM_INTENT_PARAMS_MISMATCH);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyMemoryPolicy(AgentEntity agent) {
|
||||
if (agent == null || StringUtils.isBlank(agent.getMemModelId())) {
|
||||
return;
|
||||
}
|
||||
if (Constant.MEMORY_NO_MEM.equals(agent.getMemModelId())) {
|
||||
agentChatHistoryService.deleteByAgentId(agent.getId(), true, true);
|
||||
agent.setSummaryMemory("");
|
||||
} else if (Constant.MEMORY_MEM_REPORT_ONLY.equals(agent.getMemModelId())) {
|
||||
agent.setSummaryMemory("");
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreFunctions(String agentId, List<AgentUpdateDTO.FunctionInfo> functions) {
|
||||
agentPluginMappingService.deleteByAgentId(agentId);
|
||||
if (functions == null || functions.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<AgentPluginMapping> mappings = functions.stream().map(info -> {
|
||||
AgentPluginMapping mapping = new AgentPluginMapping();
|
||||
mapping.setAgentId(agentId);
|
||||
mapping.setPluginId(info.getPluginId());
|
||||
mapping.setParamInfo(JsonUtils.toJsonString(info.getParamInfo()));
|
||||
return mapping;
|
||||
}).toList();
|
||||
agentPluginMappingService.saveBatch(mappings);
|
||||
}
|
||||
|
||||
private void restoreContextProviders(String agentId,
|
||||
List<xiaozhi.modules.agent.dto.ContextProviderDTO> contextProviders) {
|
||||
AgentContextProviderEntity entity = new AgentContextProviderEntity();
|
||||
entity.setAgentId(agentId);
|
||||
entity.setContextProviders(nullToEmpty(contextProviders));
|
||||
entity.setUpdater(SecurityUser.getUserId());
|
||||
entity.setUpdatedAt(new Date());
|
||||
agentContextProviderService.saveOrUpdateByAgentId(entity);
|
||||
}
|
||||
|
||||
private void restoreTags(String agentId, AgentSnapshotDataDTO data) {
|
||||
List<AgentSnapshotTagDTO> tags = data.getTags();
|
||||
if (tags == null || tags.isEmpty()) {
|
||||
agentTagService.saveAgentTags(agentId, null, nullToEmpty(data.getTagNames()));
|
||||
return;
|
||||
}
|
||||
|
||||
agentTagRelationDao.deleteByAgentId(agentId);
|
||||
List<AgentTagRelationEntity> relations = new ArrayList<>();
|
||||
Date now = new Date();
|
||||
for (int i = 0; i < tags.size(); i++) {
|
||||
AgentSnapshotTagDTO snapshotTag = tags.get(i);
|
||||
String tagId = restoreTag(snapshotTag, now);
|
||||
if (StringUtils.isBlank(tagId)) {
|
||||
continue;
|
||||
}
|
||||
AgentTagRelationEntity relation = new AgentTagRelationEntity();
|
||||
relation.setId(UUID.randomUUID().toString().replace("-", ""));
|
||||
relation.setAgentId(agentId);
|
||||
relation.setTagId(tagId);
|
||||
relation.setSort(i);
|
||||
relation.setCreator(SecurityUser.getUserId());
|
||||
relation.setCreatedAt(now);
|
||||
relation.setUpdater(SecurityUser.getUserId());
|
||||
relation.setUpdatedAt(now);
|
||||
relations.add(relation);
|
||||
}
|
||||
if (!relations.isEmpty()) {
|
||||
agentTagRelationDao.batchInsertRelation(relations);
|
||||
}
|
||||
}
|
||||
|
||||
private String restoreTag(AgentSnapshotTagDTO snapshotTag, Date now) {
|
||||
if (snapshotTag == null || StringUtils.isBlank(snapshotTag.getTagName())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
AgentTagEntity tag = null;
|
||||
if (StringUtils.isNotBlank(snapshotTag.getId())) {
|
||||
tag = agentTagDao.selectById(snapshotTag.getId());
|
||||
}
|
||||
if (tag == null) {
|
||||
tag = agentTagDao.selectOne(new QueryWrapper<AgentTagEntity>()
|
||||
.eq("tag_name", snapshotTag.getTagName())
|
||||
.last("LIMIT 1"));
|
||||
}
|
||||
if (tag != null) {
|
||||
if (Objects.equals(tag.getDeleted(), 1)) {
|
||||
tag.setDeleted(0);
|
||||
tag.setUpdater(SecurityUser.getUserId());
|
||||
tag.setUpdatedAt(now);
|
||||
agentTagDao.updateById(tag);
|
||||
}
|
||||
return tag.getId();
|
||||
}
|
||||
|
||||
AgentTagEntity newTag = new AgentTagEntity();
|
||||
if (StringUtils.isNotBlank(snapshotTag.getId())) {
|
||||
newTag.setId(snapshotTag.getId());
|
||||
}
|
||||
newTag.setTagName(snapshotTag.getTagName());
|
||||
newTag.setSort(snapshotTag.getSort() == null ? 0 : snapshotTag.getSort());
|
||||
newTag.setDeleted(0);
|
||||
newTag.setCreator(SecurityUser.getUserId());
|
||||
newTag.setCreatedAt(now);
|
||||
newTag.setUpdater(SecurityUser.getUserId());
|
||||
newTag.setUpdatedAt(now);
|
||||
agentTagDao.insert(newTag);
|
||||
return newTag.getId();
|
||||
}
|
||||
|
||||
private AgentSnapshotVO toVO(AgentSnapshotEntity entity, boolean includeData) {
|
||||
AgentSnapshotVO vo = new AgentSnapshotVO();
|
||||
vo.setId(entity.getId());
|
||||
vo.setAgentId(entity.getAgentId());
|
||||
vo.setUserId(entity.getUserId());
|
||||
vo.setVersionNo(entity.getVersionNo());
|
||||
vo.setChangedFields(parseChangedFields(entity.getChangedFields()));
|
||||
vo.setSource(entity.getSource());
|
||||
vo.setCreator(entity.getCreator());
|
||||
vo.setCreatedAt(entity.getCreatedAt());
|
||||
if (includeData) {
|
||||
vo.setSnapshotData(JsonUtils.parseObject(entity.getSnapshotData(), AgentSnapshotDataDTO.class));
|
||||
vo.setAfterSnapshotData(getAfterSnapshotData(entity));
|
||||
}
|
||||
return vo;
|
||||
}
|
||||
|
||||
private AgentSnapshotDataDTO getAfterSnapshotData(AgentSnapshotEntity entity) {
|
||||
AgentSnapshotEntity nextSnapshot = agentSnapshotDao.selectNextSnapshot(entity.getAgentId(), entity.getVersionNo());
|
||||
if (nextSnapshot != null) {
|
||||
return JsonUtils.parseObject(nextSnapshot.getSnapshotData(), AgentSnapshotDataDTO.class);
|
||||
}
|
||||
return buildSnapshotData(entity.getAgentId());
|
||||
}
|
||||
|
||||
private List<String> parseChangedFields(String changedFields) {
|
||||
if (StringUtils.isBlank(changedFields)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return JsonUtils.parseObject(changedFields, STRING_LIST_TYPE);
|
||||
}
|
||||
|
||||
private <T> List<T> nullToEmpty(List<T> list) {
|
||||
return list == null ? Collections.emptyList() : list;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
package xiaozhi.modules.agent.vo;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
@@ -20,7 +18,6 @@ import java.util.List;
|
||||
public class AgentInfoVO extends AgentEntity
|
||||
{
|
||||
@Schema(description = "插件列表Id")
|
||||
@TableField(typeHandler = JacksonTypeHandler.class)
|
||||
private List<AgentPluginMapping> functions;
|
||||
|
||||
@Schema(description = "上下文源配置")
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package xiaozhi.modules.agent.vo;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import xiaozhi.modules.agent.dto.AgentSnapshotDataDTO;
|
||||
|
||||
@Data
|
||||
@Schema(description = "智能体配置快照")
|
||||
public class AgentSnapshotVO {
|
||||
private String id;
|
||||
private String agentId;
|
||||
private Long userId;
|
||||
private Integer versionNo;
|
||||
private List<String> changedFields;
|
||||
private String source;
|
||||
private Long creator;
|
||||
private Date createdAt;
|
||||
private AgentSnapshotDataDTO snapshotData;
|
||||
private AgentSnapshotDataDTO afterSnapshotData;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
-- liquibase formatted sql
|
||||
|
||||
-- changeset xiaozhi:202607071530
|
||||
CREATE TABLE IF NOT EXISTS `ai_agent_snapshot` (
|
||||
`id` VARCHAR(32) NOT NULL COMMENT '快照ID',
|
||||
`agent_id` VARCHAR(32) NOT NULL COMMENT '智能体ID',
|
||||
`user_id` BIGINT DEFAULT NULL COMMENT '所属用户ID',
|
||||
`version_no` INT UNSIGNED NOT NULL COMMENT '版本号',
|
||||
`snapshot_data` JSON NOT NULL COMMENT '快照数据',
|
||||
`changed_fields` JSON DEFAULT NULL COMMENT '变更字段',
|
||||
`source` VARCHAR(32) DEFAULT 'config' COMMENT '快照来源',
|
||||
`creator` BIGINT DEFAULT NULL COMMENT '创建者',
|
||||
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_agent_version` (`agent_id`, `version_no`),
|
||||
INDEX `idx_agent_created_at` (`agent_id`, `created_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='智能体配置快照表';
|
||||
@@ -697,3 +697,10 @@ databaseChangeLog:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202607011405.sql
|
||||
- changeSet:
|
||||
id: 202607071530
|
||||
author: Codex
|
||||
changes:
|
||||
- sqlFile:
|
||||
encoding: utf8
|
||||
path: classpath:db/changelog/202607071530.sql
|
||||
|
||||
@@ -25,9 +25,6 @@
|
||||
<result column="memModelId" property="memModelId"/>
|
||||
<result column="intentModelId" property="intentModelId"/>
|
||||
|
||||
<result column="functions" property="functions"
|
||||
typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"/>
|
||||
|
||||
<result column="chatHistoryConf" property="chatHistoryConf"/>
|
||||
<result column="systemPrompt" property="systemPrompt"/>
|
||||
<result column="summaryMemory" property="summaryMemory"/>
|
||||
@@ -38,6 +35,10 @@
|
||||
<result column="createdAt" property="createdAt"/>
|
||||
<result column="updater" property="updater"/>
|
||||
<result column="updatedAt" property="updatedAt"/>
|
||||
<collection property="functions"
|
||||
ofType="xiaozhi.modules.agent.entity.AgentPluginMapping"
|
||||
column="{agentId=id}"
|
||||
select="selectAgentFunctionsByAgentId"/>
|
||||
</resultMap>
|
||||
|
||||
<select id="selectAgentInfoById" resultMap="AgentInfoMap">
|
||||
@@ -58,19 +59,6 @@
|
||||
a.tts_pitch AS ttsPitch,
|
||||
a.mem_model_id AS memModelId,
|
||||
a.intent_model_id AS intentModelId,
|
||||
COALESCE(
|
||||
(SELECT JSON_ARRAYAGG(
|
||||
JSON_OBJECT(
|
||||
'id', m.id,
|
||||
'agentId', m.agent_id,
|
||||
'pluginId', m.plugin_id,
|
||||
'paramInfo', m.param_info
|
||||
)
|
||||
)
|
||||
FROM ai_agent_plugin_mapping m
|
||||
WHERE m.agent_id = a.id),
|
||||
JSON_ARRAY()
|
||||
) AS functions,
|
||||
a.chat_history_conf AS chatHistoryConf,
|
||||
a.system_prompt AS systemPrompt,
|
||||
a.summary_memory AS summaryMemory,
|
||||
@@ -84,4 +72,13 @@
|
||||
FROM ai_agent a
|
||||
WHERE a.id = #{agentId}
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
<select id="selectAgentFunctionsByAgentId" resultType="xiaozhi.modules.agent.entity.AgentPluginMapping">
|
||||
SELECT id,
|
||||
agent_id AS agentId,
|
||||
plugin_id AS pluginId,
|
||||
param_info AS paramInfo
|
||||
FROM ai_agent_plugin_mapping
|
||||
WHERE agent_id = #{agentId}
|
||||
</select>
|
||||
</mapper>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
||||
<mapper namespace="xiaozhi.modules.agent.dao.AgentSnapshotDao">
|
||||
|
||||
<select id="selectMaxVersionNo" resultType="java.lang.Integer">
|
||||
SELECT COALESCE(MAX(version_no), 0)
|
||||
FROM ai_agent_snapshot
|
||||
WHERE agent_id = #{agentId}
|
||||
</select>
|
||||
|
||||
<select id="selectNextSnapshot" resultType="xiaozhi.modules.agent.entity.AgentSnapshotEntity">
|
||||
SELECT id,
|
||||
agent_id AS agentId,
|
||||
user_id AS userId,
|
||||
version_no AS versionNo,
|
||||
snapshot_data AS snapshotData,
|
||||
changed_fields AS changedFields,
|
||||
source,
|
||||
creator,
|
||||
created_at AS createdAt
|
||||
FROM ai_agent_snapshot
|
||||
WHERE agent_id = #{agentId}
|
||||
AND version_no > #{versionNo}
|
||||
ORDER BY version_no ASC
|
||||
LIMIT 1
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
@@ -81,6 +81,52 @@ export default {
|
||||
});
|
||||
}).send();
|
||||
},
|
||||
// 获取智能体配置快照列表
|
||||
getAgentSnapshots(agentId, params, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}/snapshots`)
|
||||
.method('GET')
|
||||
.data(params)
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getAgentSnapshots(agentId, params, callback);
|
||||
});
|
||||
}).send();
|
||||
},
|
||||
// 获取智能体配置快照详情
|
||||
getAgentSnapshot(agentId, snapshotId, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}/snapshots/${snapshotId}`)
|
||||
.method('GET')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.getAgentSnapshot(agentId, snapshotId, callback);
|
||||
});
|
||||
}).send();
|
||||
},
|
||||
// 恢复智能体配置快照
|
||||
restoreAgentSnapshot(agentId, snapshotId, callback) {
|
||||
RequestService.sendRequest()
|
||||
.url(`${getServiceUrl()}/agent/${agentId}/snapshots/${snapshotId}/restore`)
|
||||
.method('POST')
|
||||
.success((res) => {
|
||||
RequestService.clearRequestTime();
|
||||
callback(res);
|
||||
})
|
||||
.networkFail(() => {
|
||||
RequestService.reAjaxFun(() => {
|
||||
this.restoreAgentSnapshot(agentId, snapshotId, callback);
|
||||
});
|
||||
}).send();
|
||||
},
|
||||
// 新增方法:获取智能体模板
|
||||
getAgentTemplate(callback) { // 移除templateName参数
|
||||
RequestService.sendRequest()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -806,6 +806,7 @@ export default {
|
||||
'roleConfig.addTag': 'Add New Tag',
|
||||
'roleConfig.restartNotice': 'After saving the configuration, you need to restart the device for the new configuration to take effect.',
|
||||
'roleConfig.saveConfig': 'Save Configuration',
|
||||
'roleConfig.snapshotHistory': 'History',
|
||||
'roleConfig.reset': 'Reset',
|
||||
'roleConfig.agentName': 'Nickname',
|
||||
'roleConfig.roleTemplate': 'Template',
|
||||
@@ -863,6 +864,59 @@ export default {
|
||||
'roleConfig.cannotPlayAudio': 'Cannot play audio',
|
||||
'roleConfig.audioPlayError': 'Error occurred during audio playback',
|
||||
|
||||
'agentSnapshot.title': 'Version History',
|
||||
'agentSnapshot.empty': 'No versions yet',
|
||||
'agentSnapshot.version': 'Version',
|
||||
'agentSnapshot.createdAt': 'Saved At',
|
||||
'agentSnapshot.source': 'Source',
|
||||
'agentSnapshot.changedFields': 'Changes',
|
||||
'agentSnapshot.actions': 'Actions',
|
||||
'agentSnapshot.view': 'View',
|
||||
'agentSnapshot.restore': 'Restore',
|
||||
'agentSnapshot.detailTitle': 'Change Details',
|
||||
'agentSnapshot.restorePreviewTitle': 'Restore Preview',
|
||||
'agentSnapshot.confirmRestore': 'Confirm Restore',
|
||||
'agentSnapshot.before': 'Before',
|
||||
'agentSnapshot.after': 'After',
|
||||
'agentSnapshot.emptyValue': 'None',
|
||||
'agentSnapshot.noChangedContent': 'No changes to display',
|
||||
'agentSnapshot.restoreConfirm': 'Restore to version #{version}? The current configuration will be saved as a new version first.',
|
||||
'agentSnapshot.restoreSuccess': 'Version restored',
|
||||
'agentSnapshot.restoreFailed': 'Failed to restore version',
|
||||
'agentSnapshot.fetchFailed': 'Failed to fetch versions',
|
||||
'agentSnapshot.detailFailed': 'Failed to fetch snapshot details',
|
||||
'agentSnapshot.currentVersion': 'Current Version',
|
||||
'agentSnapshot.source.config': 'Config Save',
|
||||
'agentSnapshot.source.current': 'Current Config',
|
||||
'agentSnapshot.source.restore': 'Before Restore',
|
||||
'agentSnapshot.field.restore': 'Restore',
|
||||
'agentSnapshot.field.agentCode': 'Agent Code',
|
||||
'agentSnapshot.field.agentName': 'Nickname',
|
||||
'agentSnapshot.field.asrModelId': 'Speech Recognition',
|
||||
'agentSnapshot.field.vadModelId': 'Voice Detect',
|
||||
'agentSnapshot.field.llmModelId': 'Main Language Model',
|
||||
'agentSnapshot.field.slmModelId': 'Small Language Model',
|
||||
'agentSnapshot.field.vllmModelId': 'Vision Model',
|
||||
'agentSnapshot.field.ttsModelId': 'Text-to-Speech',
|
||||
'agentSnapshot.field.ttsVoiceId': 'Voice',
|
||||
'agentSnapshot.field.ttsLanguage': 'Language',
|
||||
'agentSnapshot.field.ttsVolume': 'Volume',
|
||||
'agentSnapshot.field.ttsRate': 'Speed',
|
||||
'agentSnapshot.field.ttsPitch': 'Pitch',
|
||||
'agentSnapshot.field.memModelId': 'Memory Model',
|
||||
'agentSnapshot.field.intentModelId': 'Intent Recognition',
|
||||
'agentSnapshot.field.chatHistoryConf': 'Chat History',
|
||||
'agentSnapshot.field.systemPrompt': 'Introduction',
|
||||
'agentSnapshot.field.summaryMemory': 'Memory',
|
||||
'agentSnapshot.field.langCode': 'Language Code',
|
||||
'agentSnapshot.field.language': 'Interaction Language',
|
||||
'agentSnapshot.field.sort': 'Sort',
|
||||
'agentSnapshot.field.functions': 'Functions',
|
||||
'agentSnapshot.field.contextProviders': 'Context Providers',
|
||||
'agentSnapshot.field.correctWordFileIds': 'Replacement Words',
|
||||
'agentSnapshot.field.tagNames': 'Tags',
|
||||
'agentSnapshot.field.tagIds': 'Tag IDs',
|
||||
|
||||
// Form field Tooltip descriptions
|
||||
'roleConfig.tooltip.agentName': 'Set the name of your AI agent for identification and recognition',
|
||||
'roleConfig.tooltip.roleTemplate': 'Choose from preset role templates to quickly configure your agent\'s basic settings',
|
||||
@@ -1554,4 +1608,4 @@ export default {
|
||||
// Header navigation
|
||||
'header.addressBook': 'Address Book',
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -806,6 +806,7 @@ export default {
|
||||
'roleConfig.addTag': '添加新标签',
|
||||
'roleConfig.restartNotice': '保存配置后,需要重启设备,新的配置才会生效。',
|
||||
'roleConfig.saveConfig': '保存配置',
|
||||
'roleConfig.snapshotHistory': '历史版本',
|
||||
'roleConfig.reset': '重置',
|
||||
'roleConfig.agentName': '助手昵称',
|
||||
'roleConfig.roleTemplate': '角色模版',
|
||||
@@ -863,6 +864,59 @@ export default {
|
||||
'roleConfig.cannotPlayAudio': '无法播放音频',
|
||||
'roleConfig.audioPlayError': '播放音频过程出错',
|
||||
|
||||
'agentSnapshot.title': '历史版本',
|
||||
'agentSnapshot.empty': '暂无历史版本',
|
||||
'agentSnapshot.version': '版本',
|
||||
'agentSnapshot.createdAt': '保存时间',
|
||||
'agentSnapshot.source': '来源',
|
||||
'agentSnapshot.changedFields': '变更内容',
|
||||
'agentSnapshot.actions': '操作',
|
||||
'agentSnapshot.view': '查看',
|
||||
'agentSnapshot.restore': '恢复',
|
||||
'agentSnapshot.detailTitle': '变更详情',
|
||||
'agentSnapshot.restorePreviewTitle': '恢复确认',
|
||||
'agentSnapshot.confirmRestore': '确认恢复',
|
||||
'agentSnapshot.before': '变化前',
|
||||
'agentSnapshot.after': '变化后',
|
||||
'agentSnapshot.emptyValue': '无',
|
||||
'agentSnapshot.noChangedContent': '暂无可展示的变更内容',
|
||||
'agentSnapshot.restoreConfirm': '确定恢复到版本 #{version} 吗?当前配置会先自动保存为新的历史版本。',
|
||||
'agentSnapshot.restoreSuccess': '已恢复历史版本',
|
||||
'agentSnapshot.restoreFailed': '恢复历史版本失败',
|
||||
'agentSnapshot.fetchFailed': '获取历史版本失败',
|
||||
'agentSnapshot.detailFailed': '获取快照详情失败',
|
||||
'agentSnapshot.currentVersion': '当前版本',
|
||||
'agentSnapshot.source.config': '配置保存',
|
||||
'agentSnapshot.source.current': '当前配置',
|
||||
'agentSnapshot.source.restore': '恢复前',
|
||||
'agentSnapshot.field.restore': '恢复操作',
|
||||
'agentSnapshot.field.agentCode': '助手编码',
|
||||
'agentSnapshot.field.agentName': '助手昵称',
|
||||
'agentSnapshot.field.asrModelId': '语音识别',
|
||||
'agentSnapshot.field.vadModelId': '语音活动检测',
|
||||
'agentSnapshot.field.llmModelId': '主语言模型',
|
||||
'agentSnapshot.field.slmModelId': '小参数模型',
|
||||
'agentSnapshot.field.vllmModelId': '视觉大模型',
|
||||
'agentSnapshot.field.ttsModelId': '语音合成',
|
||||
'agentSnapshot.field.ttsVoiceId': '声音音色',
|
||||
'agentSnapshot.field.ttsLanguage': '对话语言',
|
||||
'agentSnapshot.field.ttsVolume': '音量',
|
||||
'agentSnapshot.field.ttsRate': '语速',
|
||||
'agentSnapshot.field.ttsPitch': '音调',
|
||||
'agentSnapshot.field.memModelId': '记忆模式',
|
||||
'agentSnapshot.field.intentModelId': '意图识别',
|
||||
'agentSnapshot.field.chatHistoryConf': '聊天记录配置',
|
||||
'agentSnapshot.field.systemPrompt': '角色介绍',
|
||||
'agentSnapshot.field.summaryMemory': '记忆',
|
||||
'agentSnapshot.field.langCode': '语言编码',
|
||||
'agentSnapshot.field.language': '交互语种',
|
||||
'agentSnapshot.field.sort': '排序',
|
||||
'agentSnapshot.field.functions': '功能插件',
|
||||
'agentSnapshot.field.contextProviders': '上下文源',
|
||||
'agentSnapshot.field.correctWordFileIds': '替换词',
|
||||
'agentSnapshot.field.tagNames': '标签',
|
||||
'agentSnapshot.field.tagIds': '标签ID',
|
||||
|
||||
// 表单字段 Tooltip 提示说明
|
||||
'roleConfig.tooltip.agentName': '设置智能体的名称,用于标识和识别您的AI助手',
|
||||
'roleConfig.tooltip.roleTemplate': '从预设的角色模板中选择,快速配置智能体的基础设定',
|
||||
@@ -1551,4 +1605,4 @@ export default {
|
||||
'addressBookManagement.monthsAgo': '{months}个月前',
|
||||
'addressBookManagement.yearsAgo': '{years}年前',
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ function formatDateTool(date, fmt) {
|
||||
const o = {
|
||||
'M+': date.getMonth() + 1,
|
||||
'd+': date.getDate(),
|
||||
'H+': date.getHours(),
|
||||
'h+': date.getHours(),
|
||||
'm+': date.getMinutes(),
|
||||
's+': date.getSeconds()
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
<img loading="lazy" src="@/assets/home/setting-user.png" alt="" />
|
||||
</div>
|
||||
<span class="header-title">{{ form.agentName }}</span>
|
||||
<span v-if="currentVersionNo" class="current-version-tag">
|
||||
当前版本 #{{ currentVersionNo }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="header-tags">
|
||||
<el-tag
|
||||
@@ -45,6 +48,9 @@
|
||||
<img loading="lazy" src="@/assets/home/info.png" alt="" />
|
||||
<span>{{ $t("roleConfig.restartNotice") }}</span>
|
||||
</div>
|
||||
<el-button class="history-btn" @click="showSnapshotDialog = true">
|
||||
{{ $t("roleConfig.snapshotHistory") }}
|
||||
</el-button>
|
||||
<el-button type="primary" class="save-btn" @click="saveConfig">
|
||||
{{ $t("roleConfig.saveConfig") }}
|
||||
</el-button>
|
||||
@@ -457,6 +463,13 @@
|
||||
:checked-replacement-word-ids="checkedReplacementWordIds"
|
||||
@save="handleTtsSettingsSave"
|
||||
/>
|
||||
<agent-snapshot-dialog
|
||||
v-if="$route.query.agentId"
|
||||
:visible.sync="showSnapshotDialog"
|
||||
:agent-id="$route.query.agentId"
|
||||
:current-version-no="currentVersionNo"
|
||||
@restored="handleSnapshotRestored"
|
||||
/>
|
||||
<el-footer>
|
||||
<version-footer />
|
||||
</el-footer>
|
||||
@@ -470,6 +483,7 @@ import RequestService from "@/apis/httpRequest";
|
||||
import FunctionDialog from "@/components/FunctionDialog.vue";
|
||||
import ContextProviderDialog from "@/components/ContextProviderDialog.vue";
|
||||
import TtsAdvancedSettings from "@/components/TtsAdvancedSettings.vue";
|
||||
import AgentSnapshotDialog from "@/components/AgentSnapshotDialog.vue";
|
||||
import HeaderBar from "@/components/HeaderBar.vue";
|
||||
import i18n from "@/i18n";
|
||||
import featureManager from "@/utils/featureManager";
|
||||
@@ -477,11 +491,12 @@ import VersionFooter from "@/components/VersionFooter.vue";
|
||||
|
||||
export default {
|
||||
name: "RoleConfigPage",
|
||||
components: { HeaderBar, FunctionDialog, ContextProviderDialog, TtsAdvancedSettings, VersionFooter },
|
||||
components: { HeaderBar, FunctionDialog, ContextProviderDialog, TtsAdvancedSettings, AgentSnapshotDialog, VersionFooter },
|
||||
data() {
|
||||
return {
|
||||
showContextProviderDialog: false,
|
||||
showTtsAdvancedDialog: false,
|
||||
showSnapshotDialog: false,
|
||||
ttsSettings: {
|
||||
volume: 0,
|
||||
speed: 0,
|
||||
@@ -529,6 +544,7 @@ export default {
|
||||
voiceOptions: [],
|
||||
voiceDetails: {}, // 保存完整的音色信息
|
||||
showFunctionDialog: false,
|
||||
currentVersionNo: null,
|
||||
currentFunctions: [],
|
||||
currentContextProviders: [],
|
||||
allFunctions: [],
|
||||
@@ -555,14 +571,26 @@ export default {
|
||||
goToHome() {
|
||||
this.$router.push("/home");
|
||||
},
|
||||
async saveConfig() {
|
||||
try {
|
||||
await this.handleSaveAgentTags(this.$route.query.agentId);
|
||||
} catch (error) {
|
||||
console.error('保存标签失败:', error);
|
||||
return;
|
||||
normalizeFunctionParams(params, fallback = {}) {
|
||||
if (params === null || params === undefined || params === '') {
|
||||
return { ...fallback };
|
||||
}
|
||||
|
||||
if (typeof params === 'string') {
|
||||
try {
|
||||
const parsed = JSON.parse(params);
|
||||
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
||||
? parsed
|
||||
: { ...fallback };
|
||||
} catch (error) {
|
||||
return { ...fallback };
|
||||
}
|
||||
}
|
||||
if (typeof params === 'object' && !Array.isArray(params)) {
|
||||
return { ...params };
|
||||
}
|
||||
return { ...fallback };
|
||||
},
|
||||
async saveConfig() {
|
||||
const configData = {
|
||||
agentCode: this.form.agentCode,
|
||||
agentName: this.form.agentName,
|
||||
@@ -585,11 +613,12 @@ export default {
|
||||
functions: this.currentFunctions.map((item) => {
|
||||
return {
|
||||
pluginId: item.id,
|
||||
paramInfo: item.params,
|
||||
paramInfo: this.normalizeFunctionParams(item.params),
|
||||
};
|
||||
}),
|
||||
contextProviders: this.currentContextProviders,
|
||||
correctWordFileIds: this.checkedReplacementWordIds,
|
||||
tagNames: this.dynamicTags.map(tag => tag.tagName),
|
||||
};
|
||||
|
||||
// 只在用户设置了TTS参数时才传递(不为null/undefined)
|
||||
@@ -608,6 +637,7 @@ export default {
|
||||
message: i18n.t("roleConfig.saveSuccess"),
|
||||
showClose: true,
|
||||
});
|
||||
this.fetchCurrentVersion(this.$route.query.agentId);
|
||||
} else {
|
||||
this.$message.error({
|
||||
message: data.msg || i18n.t("roleConfig.saveFailed"),
|
||||
@@ -617,6 +647,32 @@ export default {
|
||||
});
|
||||
|
||||
},
|
||||
handleSnapshotRestored() {
|
||||
const agentId = this.$route.query.agentId;
|
||||
if (agentId) {
|
||||
this.fetchAgentConfig(agentId);
|
||||
this.getAgentTags(agentId);
|
||||
this.fetchCurrentVersion(agentId);
|
||||
}
|
||||
},
|
||||
fetchCurrentVersion(agentId) {
|
||||
if (!agentId) {
|
||||
this.currentVersionNo = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Api.agent.getAgentSnapshots(
|
||||
agentId,
|
||||
{ page: "1", limit: "1" },
|
||||
({ data }) => {
|
||||
if (data.code === 0) {
|
||||
const latest = data.data?.list?.[0];
|
||||
const latestVersionNo = Number(latest?.versionNo) || 0;
|
||||
this.currentVersionNo = latestVersionNo + 1;
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
resetConfig() {
|
||||
this.$confirm(i18n.t("roleConfig.confirmReset"), i18n.t("message.info"), {
|
||||
confirmButtonText: i18n.t("button.ok"),
|
||||
@@ -754,7 +810,7 @@ export default {
|
||||
id: mapping.pluginId,
|
||||
name: meta.name,
|
||||
// 后端如果还有 paramInfo 字段就用 mapping.paramInfo,否则用 meta.params 默认值
|
||||
params: mapping.paramInfo || { ...meta.params },
|
||||
params: this.normalizeFunctionParams(mapping.paramInfo, meta.params),
|
||||
fieldsMeta: meta.fieldsMeta, // 保留以便对话框渲染 tooltip
|
||||
};
|
||||
});
|
||||
@@ -1382,6 +1438,7 @@ export default {
|
||||
this.fetchAgentConfig(agentId);
|
||||
this.getAgentTags(agentId);
|
||||
this.fetchAllFunctions();
|
||||
this.fetchCurrentVersion(agentId);
|
||||
}
|
||||
this.fetchModelOptions();
|
||||
this.fetchTemplates();
|
||||
@@ -1507,6 +1564,18 @@ export default {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.current-version-tag {
|
||||
flex-shrink: 0;
|
||||
padding: 3px 9px;
|
||||
border: 1px solid #dfe7ff;
|
||||
border-radius: 999px;
|
||||
background: #f4f7ff;
|
||||
color: #5778ff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.more-tag {
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
@@ -1785,6 +1854,16 @@ export default {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.header-actions .history-btn {
|
||||
background: #ffffff;
|
||||
color: #4d5b7c;
|
||||
border: 1px solid #d8dce8;
|
||||
border-radius: 18px;
|
||||
padding: 8px 16px;
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.header-actions .reset-btn {
|
||||
background: #e6ebff;
|
||||
color: #5778ff;
|
||||
|
||||
Reference in New Issue
Block a user