mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(team): attach generated deliverables from worker replies (#606)
This commit is contained in:
parent
d1a553ed77
commit
e9b3c1697f
@ -15,6 +15,7 @@ import vip.mate.team.model.AgentTeamEntity;
|
||||
import vip.mate.team.model.TeamTaskEntity;
|
||||
import vip.mate.team.model.TeamTaskEventEntity;
|
||||
import vip.mate.team.model.TeamTaskStatus;
|
||||
import vip.mate.tool.document.GeneratedFileCache;
|
||||
import vip.mate.workspace.conversation.ConversationService;
|
||||
|
||||
import java.util.HashMap;
|
||||
@ -28,6 +29,8 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Dispatches board tasks to their assigned member agents and closes the
|
||||
@ -51,6 +54,9 @@ public class TeamDispatchService {
|
||||
/** Result summaries are capped before persisting to keep the board readable. */
|
||||
static final int MAX_RESULT_CHARS = 8000;
|
||||
|
||||
private static final Pattern GENERATED_FILE_MARKDOWN_LINK = Pattern.compile(
|
||||
"\\[([^\\]\\r\\n]{1,200})]\\(((?:https?://[^/\\s)\\]]+)?/api/v1/files/generated/[A-Za-z0-9-]+)\\)");
|
||||
|
||||
/** One JDK 21 virtual thread per member-agent run. */
|
||||
private static final ExecutorService DISPATCH_EXECUTOR =
|
||||
Executors.newVirtualThreadPerTaskExecutor();
|
||||
@ -248,7 +254,8 @@ public class TeamDispatchService {
|
||||
return;
|
||||
}
|
||||
if (TeamTaskStatus.IN_PROGRESS.equals(current.getStatus())) {
|
||||
String invalidReason = invalidResultReason(current, reply);
|
||||
boolean attachedGeneratedFile = attachGeneratedFileDeliverable(current, reply);
|
||||
String invalidReason = invalidResultReason(current, reply, attachedGeneratedFile);
|
||||
if (invalidReason != null) {
|
||||
int attempts = current.getDispatchCount() == null ? 0 : current.getDispatchCount();
|
||||
if (attempts < TeamTaskService.MAX_DISPATCHES
|
||||
@ -316,7 +323,8 @@ public class TeamDispatchService {
|
||||
announceService.announceTaskSettled(current);
|
||||
}
|
||||
|
||||
private String invalidResultReason(TeamTaskEntity task, String reply) {
|
||||
private String invalidResultReason(TeamTaskEntity task, String reply,
|
||||
boolean attachedGeneratedFile) {
|
||||
if (reply == null || reply.isBlank()) {
|
||||
return "member produced no result";
|
||||
}
|
||||
@ -325,12 +333,37 @@ public class TeamDispatchService {
|
||||
|| normalized.equals("(no output)")) {
|
||||
return "member response generation failed";
|
||||
}
|
||||
if (requiresDeliverable(task) && taskService.listDeliverables(task).isEmpty()) {
|
||||
if (requiresDeliverable(task) && !attachedGeneratedFile
|
||||
&& taskService.listDeliverables(task).isEmpty()) {
|
||||
return "required deliverable was not attached";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean attachGeneratedFileDeliverable(TeamTaskEntity task, String reply) {
|
||||
if (!requiresDeliverable(task) || reply == null || reply.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
Matcher link = GENERATED_FILE_MARKDOWN_LINK.matcher(reply);
|
||||
while (link.find()) {
|
||||
String name = link.group(1).trim();
|
||||
String url = link.group(2).trim();
|
||||
if (!GeneratedFileCache.GENERATED_URL_PATTERN.matcher(url).matches()) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
taskService.addDeliverable(task.getId(), task.getAssigneeAgentId(), name, url);
|
||||
log.info("Team task #{} auto-attached generated deliverable from member reply: {}",
|
||||
task.getTaskNumber(), name);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
log.warn("Team task #{} generated deliverable auto-attach failed for {}: {}",
|
||||
task.getTaskNumber(), name, e.getMessage());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean requiresDeliverable(TeamTaskEntity task) {
|
||||
try {
|
||||
return task.getMetadata() != null
|
||||
|
||||
@ -211,6 +211,28 @@ class TeamDispatchServiceTest {
|
||||
verify(taskService, never()).completeTask(any(), any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("a returnDirect generated-file link satisfies a declared deliverable task")
|
||||
void settleGeneratedFileLinkAttachesDeliverable() {
|
||||
TeamTaskEntity running = task(1L, MEMBER_A);
|
||||
running.setStatus(TeamTaskStatus.IN_PROGRESS);
|
||||
running.setMetadata("{\"deliverableRequired\":true}");
|
||||
TeamTaskEntity completed = task(1L, MEMBER_A);
|
||||
completed.setStatus(TeamTaskStatus.COMPLETED);
|
||||
when(taskService.getTask(1L)).thenReturn(running, completed);
|
||||
when(taskService.listDeliverables(running)).thenReturn(List.of());
|
||||
when(taskService.completeTask(eq(1L), isNull(), anyString())).thenReturn(List.of());
|
||||
|
||||
String reply = "文档已生成:[report.docx](/api/v1/files/generated/file-123)(链接 7 天内有效)。";
|
||||
service.settleOutcome(running, reply);
|
||||
|
||||
verify(taskService).addDeliverable(1L, MEMBER_A, "report.docx",
|
||||
"/api/v1/files/generated/file-123");
|
||||
verify(taskService, never()).requeueUnusableResult(any(), anyString());
|
||||
verify(taskService).completeTask(1L, null, reply);
|
||||
verify(announceService).announceTaskSettled(completed);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("a long-running checkpoint tracker stays active until its terminal round")
|
||||
void settleParksCheckpointTracker() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user