package vip.mate.agent.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import vip.mate.agent.AgentService;
import vip.mate.agent.model.AgentEntity;
import vip.mate.agent.model.TemplateDTO;
import vip.mate.exception.MateClawException;
import vip.mate.workspace.document.WorkspaceFileService;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* Agent 模板服务
*
* 扫描 classpath:templates/*.json 下的模板文件,
* 支持列出模板和应用模板创建 Agent。
*
* @author MateClaw Team
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class TemplateService {
private final AgentService agentService;
private final WorkspaceFileService workspaceFileService;
private final ObjectMapper objectMapper;
/**
* 列出所有可用模板
*/
public List listTemplates() {
List templates = new ArrayList<>();
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
try {
Resource[] resources = resolver.getResources("classpath:templates/*.json");
for (Resource resource : resources) {
try (InputStream is = resource.getInputStream()) {
TemplateDTO dto = objectMapper.readValue(is, TemplateDTO.class);
templates.add(dto);
} catch (IOException e) {
log.warn("Failed to parse template file: {}", resource.getFilename(), e);
}
}
} catch (IOException e) {
log.error("Failed to scan template files", e);
}
templates.sort(Comparator.comparing(TemplateDTO::getId));
return templates;
}
/**
* 应用模板创建 Agent 及其工作区文件
*
* @param templateId 模板 ID
* @return 创建的 AgentEntity
*/
@Transactional
public AgentEntity applyTemplate(String templateId) {
TemplateDTO template = listTemplates().stream()
.filter(t -> t.getId().equals(templateId))
.findFirst()
.orElseThrow(() -> new MateClawException("模板不存在: " + templateId));
// 1. 创建 Agent
AgentEntity agent = new AgentEntity();
agent.setName(template.getName());
agent.setDescription(template.getDescription());
agent.setAgentType(template.getAgentType());
agent.setIcon(template.getIcon());
agent.setTags(template.getTags());
agent.setMaxIterations(template.getMaxIterations());
AgentEntity created = agentService.createAgent(agent);
// 2. 创建工作区文件
if (template.getWorkspaceFiles() != null && !template.getWorkspaceFiles().isEmpty()) {
for (TemplateDTO.WorkspaceFileTemplate wf : template.getWorkspaceFiles()) {
workspaceFileService.saveFile(created.getId(), wf.getFilename(), wf.getContent());
}
// 3. 启用指定文件并按 sortOrder 排序(setPromptFiles 用列表索引作为排序值)
List enabledFilenames = template.getWorkspaceFiles().stream()
.filter(wf -> Boolean.TRUE.equals(wf.getEnabled()))
.sorted((a, b) -> {
int sa = a.getSortOrder() != null ? a.getSortOrder() : 0;
int sb = b.getSortOrder() != null ? b.getSortOrder() : 0;
return Integer.compare(sa, sb);
})
.map(TemplateDTO.WorkspaceFileTemplate::getFilename)
.toList();
if (!enabledFilenames.isEmpty()) {
workspaceFileService.setPromptFiles(created.getId(), enabledFilenames);
}
}
return created;
}
}