From 96840b01267f418f5983e690e6edbee4c128d39d Mon Sep 17 00:00:00 2001 From: matevip Date: Fri, 8 May 2026 15:02:08 +0800 Subject: [PATCH] chore: bump version to 1.3.0-SNAPSHOT --- .../vip/mate/skill/mcp/McpSkillBridge.java | 3 +- .../tool/mcp/runtime/McpClientManager.java | 49 +- .../vip/mate/trigger/model/TriggerEntity.java | 71 -- .../trigger/model/TriggerEventEntity.java | 31 - .../repository/TriggerEventMapper.java | 9 - .../trigger/repository/TriggerMapper.java | 9 - .../java/vip/mate/wiki/WikiProperties.java | 15 - .../mate/wiki/controller/WikiController.java | 17 - ...WikiEmbeddingProviderFailingException.java | 40 - .../wiki/model/WikiRawMaterialEntity.java | 11 +- .../wiki/service/WikiEmbeddingService.java | 33 +- .../wiki/service/WikiProcessingService.java | 91 +- .../wiki/service/WikiRawMaterialService.java | 43 - .../mate/workflow/model/WorkflowEntity.java | 61 -- .../workflow/model/WorkflowPayloadEntity.java | 48 - .../model/WorkflowRevisionEntity.java | 41 - .../workflow/model/WorkflowRunEntity.java | 61 -- .../model/WorkflowRunPauseEntity.java | 51 - .../workflow/model/WorkflowRunStepEntity.java | 68 -- .../workflow/repository/WorkflowMapper.java | 9 - .../repository/WorkflowPayloadMapper.java | 9 - .../repository/WorkflowRevisionMapper.java | 9 - .../repository/WorkflowRunMapper.java | 9 - .../repository/WorkflowRunPauseMapper.java | 9 - .../repository/WorkflowRunStepMapper.java | 9 - .../conversation/ConversationService.java | 31 - .../h2/V95__wiki_raw_material_cancel.sql | 6 - .../h2/V96__workflow_foundations.sql | 173 ---- .../mysql/V95__wiki_raw_material_cancel.sql | 9 - .../mysql/V96__workflow_foundations.sql | 162 ---- mateclaw-ui/src/api/index.ts | 2 - .../components/channels/ChannelEditModal.vue | 5 +- .../src/components/chat/MessageBubble.vue | 3 +- mateclaw-ui/src/i18n/locales/en-US.ts | 5 - mateclaw-ui/src/i18n/locales/zh-CN.ts | 5 - mateclaw-ui/src/utils/clipboard.ts | 18 - mateclaw-ui/src/views/AgentContext.vue | 3 +- mateclaw-ui/src/views/ChatConsole.vue | 3 +- .../Models/modals/DeviceCodeDialog.vue | 3 +- .../src/views/Wiki/components/JobStageBar.vue | 44 +- .../Wiki/components/RawMaterialPanel.vue | 93 +- .../__pycache__/build_svg_diagrams.cpython-312.pyc | Bin 0 -> 27622 bytes .../__pycache__/build_vibe_design_doc.cpython-312.pyc | Bin 0 -> 77941 bytes .../insert_diagrams_into_doc.cpython-312.pyc | Bin 0 -> 4665 bytes ...®¡理平台系统升级-VibeCoding实施方案与工作量清单-含设计图.docx | Bin 0 -> 1125677 bytes ...Š¡综合管理平台系统升级-VibeCoding实施方案与工作量清单.docx | Bin 0 -> 71294 bytes ...团票务综合管理平台系统升级-详细设计与Codex实施方案.docx | Bin 0 -> 46443 bytes outputs/北京公交票务评估/build_design_doc.py | 367 ++++++++ .../北京公交票务评估/build_svg_diagrams.py | 340 +++++++ .../北京公交票务评估/build_vibe_design_doc.py | 883 ++++++++++++++++++ .../insert_diagrams_into_doc.py | 72 ++ ...¥¨务综合管理平台系统升级-详细设计与Codex实施方案.docx.png | Bin 0 -> 523023 bytes ...理平台系统升级-VibeCoding实施方案与工作量清单-含设计图.docx.png | Bin 0 -> 442978 bytes ...理平台系统升级-VibeCoding实施方案与工作量清单-含设计图.docx.png | Bin 0 -> 491740 bytes .../svg设计图/01-总体技术架构图.svg | 121 +++ .../svg设计图/02-核心票务业务流转图.svg | 126 +++ .../svg设计图/03-部署与可运维性架构图.svg | 122 +++ .../svg设计图/04-权限与数据范围设计图.svg | 97 ++ .../svg设计图/05-SDD协同交付流程图.svg | 98 ++ .../svg设计图/06-数据湖与外部系统对接架构图.svg | 123 +++ .../svg设计图/07-系统功能地图.svg | 117 +++ outputs/北京公交票务评估/svg设计图/README.md | 9 + .../svg设计图/png预览/01-总体技术架构图.png | Bin 0 -> 172995 bytes .../svg设计图/png预览/02-核心票务业务流转图.png | Bin 0 -> 162191 bytes .../svg设计图/png预览/03-部署与可运维性架构图.png | Bin 0 -> 172801 bytes .../svg设计图/png预览/04-权限与数据范围设计图.png | Bin 0 -> 137170 bytes .../svg设计图/png预览/05-SDD协同交付流程图.png | Bin 0 -> 142345 bytes .../png预览/06-数据湖与外部系统对接架构图.png | Bin 0 -> 178024 bytes .../svg设计图/png预览/07-系统功能地图.png | Bin 0 -> 152565 bytes .../svg设计图/quicklook/01-总体技术架构图.svg.png | Bin 0 -> 285705 bytes .../北京公交票务平台功能开发清单.xlsx | Bin 0 -> 3363 bytes ...综合管理平台系统升级-SDD实施方案与工作量清单-含设计图.docx | Bin 0 -> 1147315 bytes ...团票务综合管理平台系统升级-SDD实施方案与工作量清单.docx | Bin 0 -> 71907 bytes 73 files changed, 2515 insertions(+), 1331 deletions(-) delete mode 100644 mateclaw-server/src/main/java/vip/mate/trigger/model/TriggerEntity.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/trigger/model/TriggerEventEntity.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/trigger/repository/TriggerEventMapper.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/trigger/repository/TriggerMapper.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/wiki/job/WikiEmbeddingProviderFailingException.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowEntity.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowPayloadEntity.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRevisionEntity.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunEntity.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunPauseEntity.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunStepEntity.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowMapper.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowPayloadMapper.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRevisionMapper.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunMapper.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunPauseMapper.java delete mode 100644 mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunStepMapper.java delete mode 100644 mateclaw-server/src/main/resources/db/migration/h2/V95__wiki_raw_material_cancel.sql delete mode 100644 mateclaw-server/src/main/resources/db/migration/h2/V96__workflow_foundations.sql delete mode 100644 mateclaw-server/src/main/resources/db/migration/mysql/V95__wiki_raw_material_cancel.sql delete mode 100644 mateclaw-server/src/main/resources/db/migration/mysql/V96__workflow_foundations.sql delete mode 100644 mateclaw-ui/src/utils/clipboard.ts create mode 100644 outputs/北京公交票务评估/__pycache__/build_svg_diagrams.cpython-312.pyc create mode 100644 outputs/北京公交票务评估/__pycache__/build_vibe_design_doc.cpython-312.pyc create mode 100644 outputs/北京公交票务评估/__pycache__/insert_diagrams_into_doc.cpython-312.pyc create mode 100644 outputs/北京公交票务评估/_archive/北京公交集团票务综合管理平台系统升级-VibeCoding实施方案与工作量清单-含设计图.docx create mode 100644 outputs/北京公交票务评估/_archive/北京公交集团票务综合管理平台系统升级-VibeCoding实施方案与工作量清单.docx create mode 100644 outputs/北京公交票务评估/_archive/北京公交集团票务综合管理平台系统升级-详细设计与Codex实施方案.docx create mode 100644 outputs/北京公交票务评估/build_design_doc.py create mode 100644 outputs/北京公交票务评估/build_svg_diagrams.py create mode 100644 outputs/北京公交票务评估/build_vibe_design_doc.py create mode 100644 outputs/北京公交票务评估/insert_diagrams_into_doc.py create mode 100644 outputs/北京公交票务评估/quicklook/北京公交集团票务综合管理平台系统升级-详细设计与Codex实施方案.docx.png create mode 100644 outputs/北京公交票务评估/quicklook含图/北京公交集团票务综合管理平台系统升级-VibeCoding实施方案与工作量清单-含设计图.docx.png create mode 100644 outputs/北京公交票务评估/quicklook最终/北京公交集团票务综合管理平台系统升级-VibeCoding实施方案与工作量清单-含设计图.docx.png create mode 100644 outputs/北京公交票务评估/svg设计图/01-总体技术架构图.svg create mode 100644 outputs/北京公交票务评估/svg设计图/02-核心票务业务流转图.svg create mode 100644 outputs/北京公交票务评估/svg设计图/03-部署与可运维性架构图.svg create mode 100644 outputs/北京公交票务评估/svg设计图/04-权限与数据范围设计图.svg create mode 100644 outputs/北京公交票务评估/svg设计图/05-SDD协同交付流程图.svg create mode 100644 outputs/北京公交票务评估/svg设计图/06-数据湖与外部系统对接架构图.svg create mode 100644 outputs/北京公交票务评估/svg设计图/07-系统功能地图.svg create mode 100644 outputs/北京公交票务评估/svg设计图/README.md create mode 100644 outputs/北京公交票务评估/svg设计图/png预览/01-总体技术架构图.png create mode 100644 outputs/北京公交票务评估/svg设计图/png预览/02-核心票务业务流转图.png create mode 100644 outputs/北京公交票务评估/svg设计图/png预览/03-部署与可运维性架构图.png create mode 100644 outputs/北京公交票务评估/svg设计图/png预览/04-权限与数据范围设计图.png create mode 100644 outputs/北京公交票务评估/svg设计图/png预览/05-SDD协同交付流程图.png create mode 100644 outputs/北京公交票务评估/svg设计图/png预览/06-数据湖与外部系统对接架构图.png create mode 100644 outputs/北京公交票务评估/svg设计图/png预览/07-系统功能地图.png create mode 100644 outputs/北京公交票务评估/svg设计图/quicklook/01-总体技术架构图.svg.png create mode 100644 outputs/北京公交票务评估/北京公交票务平台功能开发清单.xlsx create mode 100644 outputs/北京公交票务评估/北京公交集团票务综合管理平台系统升级-SDD实施方案与工作量清单-含设计图.docx create mode 100644 outputs/北京公交票务评估/北京公交集团票务综合管理平台系统升级-SDD实施方案与工作量清单.docx diff --git a/mateclaw-server/src/main/java/vip/mate/skill/mcp/McpSkillBridge.java b/mateclaw-server/src/main/java/vip/mate/skill/mcp/McpSkillBridge.java index d29eef6c..4f120141 100644 --- a/mateclaw-server/src/main/java/vip/mate/skill/mcp/McpSkillBridge.java +++ b/mateclaw-server/src/main/java/vip/mate/skill/mcp/McpSkillBridge.java @@ -135,7 +135,8 @@ public class McpSkillBridge { s.setId(virtualIdFor(server)); s.setName(slugify(server.getName())); s.setNameEn(displayName(server)); - s.setNameZh(displayName(server)); + s.setNameZh(server.getDescription() != null && !server.getDescription().isBlank() + ? displayName(server) : null); s.setDescription(buildDescription(server)); s.setSkillType("mcp"); s.setIcon(iconFor(server)); diff --git a/mateclaw-server/src/main/java/vip/mate/tool/mcp/runtime/McpClientManager.java b/mateclaw-server/src/main/java/vip/mate/tool/mcp/runtime/McpClientManager.java index ad835a4f..e55245aa 100644 --- a/mateclaw-server/src/main/java/vip/mate/tool/mcp/runtime/McpClientManager.java +++ b/mateclaw-server/src/main/java/vip/mate/tool/mcp/runtime/McpClientManager.java @@ -17,8 +17,6 @@ import org.springframework.stereotype.Component; import vip.mate.tool.mcp.model.McpServerEntity; import jakarta.annotation.PreDestroy; -import java.net.URI; -import java.net.URISyntaxException; import java.time.Duration; import java.time.LocalDateTime; import java.util.*; @@ -373,9 +371,7 @@ public class McpClientManager { Duration connectTimeout = Duration.ofSeconds( server.getConnectTimeoutSeconds() != null ? server.getConnectTimeoutSeconds() : 30); - HttpEndpointConfig endpointConfig = splitHttpUrl(server.getUrl(), "/sse"); - var builder = HttpClientSseClientTransport.builder(endpointConfig.baseUrl()) - .sseEndpoint(endpointConfig.endpoint()) + var builder = HttpClientSseClientTransport.builder(server.getUrl()) .connectTimeout(connectTimeout); // Add headers via request customizer @@ -395,9 +391,7 @@ public class McpClientManager { Duration connectTimeout = Duration.ofSeconds( server.getConnectTimeoutSeconds() != null ? server.getConnectTimeoutSeconds() : 30); - HttpEndpointConfig endpointConfig = splitHttpUrl(server.getUrl(), "/mcp"); - var builder = HttpClientStreamableHttpTransport.builder(endpointConfig.baseUrl()) - .endpoint(endpointConfig.endpoint()) + var builder = HttpClientStreamableHttpTransport.builder(server.getUrl()) .connectTimeout(connectTimeout); // Add headers via request customizer @@ -413,45 +407,6 @@ public class McpClientManager { return builder.build(); } - /** - * Splits a full HTTP MCP URL into a {@code scheme://authority} base and a - * {@code path[?query]} endpoint suffix. The underlying SDK builders take - * the two halves separately and resolve them via {@link URI#resolve(URI)}, - * which replaces the base URL's path with the endpoint when the endpoint - * starts with {@code /}. Passing a full URL as the base would therefore - * silently route every request to the SDK's default endpoint - * (e.g. {@code /mcp}) and drop any user-configured path or query string. - * - * @param url the user-configured full URL - * @param defaultEndpoint endpoint to use when the URL has no path - */ - static HttpEndpointConfig splitHttpUrl(String url, String defaultEndpoint) { - String trimmed = url != null ? url.trim() : ""; - if (trimmed.isEmpty()) { - throw new IllegalArgumentException("MCP server URL must not be empty"); - } - URI uri; - try { - uri = new URI(trimmed); - } catch (URISyntaxException e) { - throw new IllegalArgumentException("Invalid MCP server URL: " + url, e); - } - if (uri.getScheme() == null || uri.getRawAuthority() == null) { - throw new IllegalArgumentException("MCP server URL must include scheme and host: " + url); - } - String path = uri.getRawPath(); - String endpoint = (path == null || path.isEmpty() || "/".equals(path)) ? defaultEndpoint : path; - String query = uri.getRawQuery(); - if (query != null && !query.isEmpty()) { - endpoint += "?" + query; - } - String baseUrl = uri.getScheme() + "://" + uri.getRawAuthority(); - return new HttpEndpointConfig(baseUrl, endpoint); - } - - record HttpEndpointConfig(String baseUrl, String endpoint) { - } - private Map parseHeaders(McpServerEntity server) { if (server.getHeadersJson() != null && !server.getHeadersJson().isBlank()) { Map headers = JSONUtil.toBean(server.getHeadersJson(), diff --git a/mateclaw-server/src/main/java/vip/mate/trigger/model/TriggerEntity.java b/mateclaw-server/src/main/java/vip/mate/trigger/model/TriggerEntity.java deleted file mode 100644 index aa295eaf..00000000 --- a/mateclaw-server/src/main/java/vip/mate/trigger/model/TriggerEntity.java +++ /dev/null @@ -1,71 +0,0 @@ -package vip.mate.trigger.model; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.FieldStrategy; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableLogic; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * Trigger row. {@code patternVersion} is a lamport counter that fire callbacks - * compare against the row on every fire; mismatches mean another instance has - * updated the cron expression and the local schedule must self-cancel. - */ -@Data -@TableName("mate_trigger") -public class TriggerEntity { - - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - private Long workspaceId; - - @TableField(value = "name", updateStrategy = FieldStrategy.ALWAYS) - private String name; - - /** Pattern flavour: cron / webhook / channel_message / agent_lifecycle / content_match / workflow_completion. */ - private String patternType; - - @TableField(value = "pattern_json", updateStrategy = FieldStrategy.ALWAYS) - private String patternJson; - - /** Routing target type: agent or workflow. */ - private String targetType; - - private Long targetId; - - @TableField(value = "payload_template", updateStrategy = FieldStrategy.ALWAYS) - private String payloadTemplate; - - private Integer rateLimitPerMin; - - private Integer dedupWindowSecs; - - private Boolean botSelfFilter; - - private Boolean enabled; - - private Long fireCount; - - private Long maxFires; - - @TableField(value = "last_fired_at", updateStrategy = FieldStrategy.ALWAYS) - private LocalDateTime lastFiredAt; - - /** Lamport counter — bump on every cron expression / payload template change. */ - private Long patternVersion; - - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createTime; - - @TableField(fill = FieldFill.INSERT_UPDATE) - private LocalDateTime updateTime; - - @TableLogic - private Integer deleted; -} diff --git a/mateclaw-server/src/main/java/vip/mate/trigger/model/TriggerEventEntity.java b/mateclaw-server/src/main/java/vip/mate/trigger/model/TriggerEventEntity.java deleted file mode 100644 index d76907a1..00000000 --- a/mateclaw-server/src/main/java/vip/mate/trigger/model/TriggerEventEntity.java +++ /dev/null @@ -1,31 +0,0 @@ -package vip.mate.trigger.model; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * Trigger dedup-window row. {@code dedupKey} carries envelope.eventId, falling - * back to a content sha256 when the upstream channel did not provide a stable - * id. {@code expiresAt} is set on insert to {@code receivedAt + dedupWindowSecs} - * so the cleanup task can sweep expired rows. - */ -@Data -@TableName("mate_trigger_event") -public class TriggerEventEntity { - - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - private Long triggerId; - - private String dedupKey; - - /** Filled by DB DEFAULT CURRENT_TIMESTAMP when left null on insert. */ - private LocalDateTime receivedAt; - - private LocalDateTime expiresAt; -} diff --git a/mateclaw-server/src/main/java/vip/mate/trigger/repository/TriggerEventMapper.java b/mateclaw-server/src/main/java/vip/mate/trigger/repository/TriggerEventMapper.java deleted file mode 100644 index 2fd612d6..00000000 --- a/mateclaw-server/src/main/java/vip/mate/trigger/repository/TriggerEventMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package vip.mate.trigger.repository; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import vip.mate.trigger.model.TriggerEventEntity; - -@Mapper -public interface TriggerEventMapper extends BaseMapper { -} diff --git a/mateclaw-server/src/main/java/vip/mate/trigger/repository/TriggerMapper.java b/mateclaw-server/src/main/java/vip/mate/trigger/repository/TriggerMapper.java deleted file mode 100644 index 54f49629..00000000 --- a/mateclaw-server/src/main/java/vip/mate/trigger/repository/TriggerMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package vip.mate.trigger.repository; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import vip.mate.trigger.model.TriggerEntity; - -@Mapper -public interface TriggerMapper extends BaseMapper { -} diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/WikiProperties.java b/mateclaw-server/src/main/java/vip/mate/wiki/WikiProperties.java index 5ec7d38e..cf3b4c00 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/WikiProperties.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/WikiProperties.java @@ -130,21 +130,6 @@ public class WikiProperties { */ private int embeddingMaxChars = 6000; - /** - * Circuit-breaker threshold: abort an embedding pass after this many - * consecutive batch failures (auth / rate-limit / network errors that - * cause an entire batch to embed zero chunks). Without it, a broken - * provider would silently iterate through every pending chunk in the - * KB, producing only log noise and wasted wall-clock time before the - * user can intervene. - *

- * Set too low and a transient blip aborts a healthy pass; set too - * high and the user waits forever on a clearly-broken provider. - * Default 5 covers most real outages while tolerating a couple of - * isolated 5xx hiccups. - */ - private int embeddingConsecutiveFailureThreshold = 5; - /** 混合搜索默认模式:keyword / semantic / hybrid */ private String searchDefaultMode = "hybrid"; diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/controller/WikiController.java b/mateclaw-server/src/main/java/vip/mate/wiki/controller/WikiController.java index 0de20d85..b765f46d 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/controller/WikiController.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/controller/WikiController.java @@ -300,23 +300,6 @@ public class WikiController { return R.ok(); } - @RequireWorkspaceRole("member") - @Operation(summary = "请求取消正在进行的处理(仅在 processing 状态有效)") - @PostMapping("/knowledge-bases/{kbId}/raw/{rawId}/cancel") - public R cancelRaw(@PathVariable Long kbId, @PathVariable Long rawId, - @RequestHeader(value = "X-Workspace-Id", required = false) Long workspaceId) { - verifyKBWorkspace(kbId, workspaceId); - WikiRawMaterialEntity raw = rawService.getById(rawId); - if (raw == null || !kbId.equals(raw.getKbId())) { - return R.fail("Raw material not found in this knowledge base"); - } - // requestCancel is idempotent: a no-op when the row is not processing, - // so repeated clicks (or a click after the run already finished) are - // safe and do not surface an error to the user. - rawService.requestCancel(rawId); - return R.ok(); - } - @RequireWorkspaceRole("viewer") @Operation(summary = "下载原始材料") @GetMapping("/knowledge-bases/{kbId}/raw/{rawId}/download") diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/job/WikiEmbeddingProviderFailingException.java b/mateclaw-server/src/main/java/vip/mate/wiki/job/WikiEmbeddingProviderFailingException.java deleted file mode 100644 index 48964bee..00000000 --- a/mateclaw-server/src/main/java/vip/mate/wiki/job/WikiEmbeddingProviderFailingException.java +++ /dev/null @@ -1,40 +0,0 @@ -package vip.mate.wiki.job; - -/** - * Thrown by {@link vip.mate.wiki.service.WikiEmbeddingService#embedMissingChunks(Long)} - * when the embedding provider has failed N batches in a row, where N is - * controlled by {@code mate.wiki.embedding-consecutive-failure-threshold}. - * - *

Without this circuit, a misconfigured or unavailable provider (out - * of credits, wrong API key, network partition) silently churns through - * every pending chunk one batch at a time — producing log noise but no - * actual progress, and consuming wall-clock time the user sees as a - * stuck "task in loop". The circuit lets the embedding pass abort fast - * so the user can fix configuration and retry. - * - *

This is a soft failure: the next call into {@code embedMissingChunks} - * starts a fresh counter and will retry the provider, so once the user - * has corrected the configuration the embedding pass picks up where it - * left off without manual intervention. - */ -public class WikiEmbeddingProviderFailingException extends RuntimeException { - - private final int consecutiveFailures; - private final int remainingChunks; - - public WikiEmbeddingProviderFailingException(String message, - int consecutiveFailures, - int remainingChunks) { - super(message); - this.consecutiveFailures = consecutiveFailures; - this.remainingChunks = remainingChunks; - } - - public int getConsecutiveFailures() { - return consecutiveFailures; - } - - public int getRemainingChunks() { - return remainingChunks; - } -} diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiRawMaterialEntity.java b/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiRawMaterialEntity.java index f623ccdf..06bcdbba 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiRawMaterialEntity.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/model/WikiRawMaterialEntity.java @@ -46,18 +46,9 @@ public class WikiRawMaterialEntity { /** 文件大小(字节) */ private Long fileSize; - /** 处理状态:pending / processing / completed / failed / partial / cancelled */ + /** 处理状态:pending / processing / completed / failed */ private String processingStatus; - /** - * User-requested cancellation flag. Set to {@code true} via the cancel - * endpoint while a raw material is in {@code processing}. The pipeline - * observes the flag at its abort checkpoints and exits early with - * {@code processingStatus = "cancelled"}; the flag is cleared on the - * next successful claim for processing. - */ - private Boolean cancelRequested; - /** 上次处理时间 */ private LocalDateTime lastProcessedAt; diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiEmbeddingService.java b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiEmbeddingService.java index b4ccf373..74aec2c7 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiEmbeddingService.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiEmbeddingService.java @@ -140,12 +140,7 @@ public class WikiEmbeddingService { int batchSize = Math.max(1, properties.getEmbeddingBatchSize()); int maxChars = Math.max(500, properties.getEmbeddingMaxChars()); - int threshold = Math.max(1, properties.getEmbeddingConsecutiveFailureThreshold()); int total = 0; - // Consecutive failure counter: resets on any successful batch / long - // chunk, increments when a unit returns zero progress. Crossing the - // threshold trips the circuit and aborts the rest of this pass. - int consecutiveFailures = 0; for (int offset = 0; offset < pending.size(); offset += batchSize) { List batch = pending.subList(offset, Math.min(offset + batchSize, pending.size())); @@ -165,30 +160,13 @@ public class WikiEmbeddingService { // Short chunks: existing batch path if (!shortBatch.isEmpty()) { - int embedded = embedShortBatch(shortBatch, r.model(), modelName, kbId); - total += embedded; - if (embedded == 0) { - consecutiveFailures++; - if (consecutiveFailures >= threshold) { - int remaining = pending.size() - (offset + batch.size()); - throw circuitOpen(kbId, modelName, consecutiveFailures, remaining); - } - } else { - consecutiveFailures = 0; - } + total += embedShortBatch(shortBatch, r.model(), modelName, kbId); } // Long chunks: each goes through sub-segment split + mean pool for (WikiChunkEntity longChunk : longChunks) { if (embedLongChunk(longChunk, r.model(), modelName, maxChars)) { total++; - consecutiveFailures = 0; - } else { - consecutiveFailures++; - if (consecutiveFailures >= threshold) { - int remaining = pending.size() - (offset + batch.size()); - throw circuitOpen(kbId, modelName, consecutiveFailures, remaining); - } } } } @@ -203,15 +181,6 @@ public class WikiEmbeddingService { return total; } - private vip.mate.wiki.job.WikiEmbeddingProviderFailingException circuitOpen( - Long kbId, String modelName, int failures, int remaining) { - String message = "Embedding provider unavailable: " + failures - + " consecutive batch failures (kbId=" + kbId + ", model=" + modelName - + "). Aborted with " + remaining + " chunk(s) still pending."; - log.warn("[WikiEmbedding] Circuit opened — {}", message); - return new vip.mate.wiki.job.WikiEmbeddingProviderFailingException(message, failures, remaining); - } - /** * Embed a batch of chunks whose content fits within the per-segment char limit. * One API call per batch; individual results are persisted independently. diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiProcessingService.java b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiProcessingService.java index d23761f9..9ea5bf25 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiProcessingService.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiProcessingService.java @@ -303,17 +303,7 @@ public class WikiProcessingService { String finalStatus; String finalDetail = null; - // Cancellation takes precedence over the normal terminal-state logic: - // chunks that observed the cancel flag returned early as "failed", but - // those aren't real failures — the user asked to stop. Surface that - // intent explicitly so the UI can show "cancelled" instead of "failed" - // or "partial". - if (rawService.isCancelRequested(rawId)) { - finalDetail = "Cancelled by user (" + totalPages + " page(s) generated, " - + (totalChunks - failedChunks) + "/" + totalChunks + " chunks completed before stop)."; - rawService.updateProcessingStatus(rawId, "cancelled", finalDetail); - finalStatus = "cancelled"; - } else if (totalPages == 0) { + if (totalPages == 0) { // RFC-051 follow-up: previously this was an unconditional "failed". // But chunks were already persisted (and the materials are searchable // via wiki_semantic_search) — the only thing that actually went wrong @@ -381,34 +371,31 @@ public class WikiProcessingService { var terminalStage = switch (finalStatus) { case "failed" -> vip.mate.wiki.job.WikiJobStage.FAILED; case "partial" -> vip.mate.wiki.job.WikiJobStage.PARTIAL; - case "cancelled" -> vip.mate.wiki.job.WikiJobStage.CANCELLED; default -> vip.mate.wiki.job.WikiJobStage.COMPLETED; }; wikiJobService.transition(jobId, terminalStage); } catch (Exception ignored) {} } - // Skip the post-terminal side effects (log line, overview rebuild, - // KB-dirty event) for cancelled and failed runs. A cancelled run - // means the user explicitly stopped — don't burn LLM tokens on - // overview regeneration over an unstable partial state. - boolean nonTerminalSideEffects = !"failed".equals(finalStatus) && !"cancelled".equals(finalStatus); - if (logService != null && nonTerminalSideEffects) { + // RFC-051 PR-2c: log every non-failed eager ingest. Failures already get a + // RAW_FAILED broadcast and an error message in the raw row. Title goes first + // so the log reads as "what just landed" instead of an opaque raw id. + if (logService != null && !"failed".equals(finalStatus)) { String title = (raw.getTitle() == null || raw.getTitle().isBlank()) ? ("raw#" + rawId) : raw.getTitle(); logService.append(kb.getId(), WikiLogService.EventType.INGEST, "eager " + finalStatus + " · " + title + " · " + totalPages + " pages · " + totalChunks + " chunks"); } - // Refresh overview stats whenever a raw lands in a terminal state - // (completed or partial). Failures and cancellations don't shift the stats meaningfully. - if (overviewService != null && nonTerminalSideEffects) { + // RFC-051 PR-2b: refresh overview stats whenever a raw lands in a terminal state + // (completed or partial). Failures don't shift the stats meaningfully. + if (overviewService != null && !"failed".equals(finalStatus)) { overviewService.rebuild(kb.getId()); } // Tier 2: signal "KB content is dirty" so WikiNarrativeService can // schedule (debounced) an LLM-generated overview narrative refresh. // Stats rebuild above is sync; narrative regen runs after-commit. - if (nonTerminalSideEffects) { + if (!"failed".equals(finalStatus)) { eventPublisher.publishEvent(new vip.mate.wiki.event.WikiKbDirtyEvent(this, kb.getId())); } @@ -423,12 +410,7 @@ public class WikiProcessingService { // RFC-051 follow-up: trigger embedding whenever chunks landed, not only when // pages were produced. Otherwise the partial-with-no-pages case above ends up // with chunks in DB but never embedded, so semantic search silently misses them. - // Skip the post-ingest embedding sweep when this run was cancelled. - // The user almost certainly stopped because the embedding provider - // is failing (out of credits, wrong key, etc.); kicking off another - // embedding pass on the same provider would just churn through - // every pending chunk and produce more "all chunks failed" noise. - if (totalChunks > 0 && !"cancelled".equals(finalStatus)) { + if (totalChunks > 0) { final Long fKbId = kb.getId(); WIKI_EXECUTOR.submit(() -> { try { @@ -436,12 +418,6 @@ public class WikiProcessingService { if (embedded > 0) { log.info("[Wiki] Async embedding completed: kbId={}, embedded={}", fKbId, embedded); } - } catch (vip.mate.wiki.job.WikiEmbeddingProviderFailingException ex) { - // Circuit-breaker tripped — the provider has consistently failed. - // The exception's own log line in WikiEmbeddingService is enough; - // emit a calmer notice here instead of a generic failure log. - log.warn("[Wiki] Async embedding aborted by circuit-breaker for kbId={}: {}", - fKbId, ex.getMessage()); } catch (Exception ex) { log.warn("[Wiki] Async embedding failed for kbId={}: {}", fKbId, ex.getMessage()); } @@ -449,39 +425,16 @@ public class WikiProcessingService { } } catch (Exception e) { - // If the user requested cancellation while this run was in flight, - // surface the abort as 'cancelled' rather than 'failed' even when - // the exception bubbled up from somewhere mid-pipeline (e.g. a - // checkpoint rejected between chunks). - boolean cancelled = rawService.isCancelRequested(rawId); - String terminalStatus = cancelled ? "cancelled" : "failed"; - String detail = cancelled - ? "Cancelled by user (interrupted: " + (e.getMessage() == null ? "unknown" : e.getMessage()) + ")" - : e.getMessage(); - if (cancelled) { - log.info("[Wiki] Processing cancelled for raw={}: {}", rawId, e.getMessage()); - } else { - log.error("[Wiki] Processing failed for raw={}: {}", rawId, e.getMessage(), e); - } - rawService.updateProcessingStatus(rawId, terminalStatus, detail); + log.error("[Wiki] Processing failed for raw={}: {}", rawId, e.getMessage(), e); + rawService.updateProcessingStatus(rawId, "failed", e.getMessage()); kbService.updateStatus(kb.getId(), "active"); + // Transition job to failed if (wikiJobService != null && jobId != null) { - try { - wikiJobService.transition(jobId, cancelled - ? vip.mate.wiki.job.WikiJobStage.CANCELLED - : vip.mate.wiki.job.WikiJobStage.FAILED); - } catch (Exception ignored) {} - } - // Broadcast: cancelled rows reuse the COMPLETED event with status="cancelled" - // so subscribers can render the terminal-but-not-error UI; only true failures - // go through RAW_FAILED (which the UI surfaces as a red banner). - if (cancelled) { - progressBus.broadcast(kb.getId(), WikiProgressBus.EVENT_RAW_COMPLETED, - java.util.Map.of("rawId", rawId, "status", "cancelled")); - } else { - progressBus.broadcast(kb.getId(), WikiProgressBus.EVENT_RAW_FAILED, - java.util.Map.of("rawId", rawId, "error", e.getMessage() == null ? "unknown" : e.getMessage())); + try { wikiJobService.transition(jobId, vip.mate.wiki.job.WikiJobStage.FAILED); } catch (Exception ignored) {} } + // RFC-012 M3:广播异常终态 + progressBus.broadcast(kb.getId(), WikiProgressBus.EVENT_RAW_FAILED, + java.util.Map.of("rawId", rawId, "error", e.getMessage() == null ? "unknown" : e.getMessage())); } finally { // RFC-012 M2 v2 UI v2:写入最终进度并清理共享计数器 ProgressCounter pc = progressCounters.remove(rawId); @@ -2211,15 +2164,10 @@ public class WikiProcessingService { * @return {@code true} if the raw is gone; caller should stop work */ private boolean isAborted(Long rawId, String ctx) { - WikiRawMaterialEntity raw = rawService.getById(rawId); - if (raw == null) { + if (rawService.getById(rawId) == null) { log.info("[Wiki] Aborting {} for raw={}: raw was deleted mid-processing", ctx, rawId); return true; } - if (Boolean.TRUE.equals(raw.getCancelRequested())) { - log.info("[Wiki] Aborting {} for raw={}: cancellation requested by user", ctx, rawId); - return true; - } return false; } @@ -2312,9 +2260,6 @@ public class WikiProcessingService { if (embedded > 0) { log.info("[Wiki] Lazy async embedding completed: kbId={}, embedded={}", fKbId, embedded); } - } catch (vip.mate.wiki.job.WikiEmbeddingProviderFailingException ex) { - log.warn("[Wiki] Lazy async embedding aborted by circuit-breaker for kbId={}: {}", - fKbId, ex.getMessage()); } catch (Exception ex) { log.warn("[Wiki] Lazy async embedding failed for kbId={}: {}", fKbId, ex.getMessage()); } diff --git a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiRawMaterialService.java b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiRawMaterialService.java index 1d8f005b..f71f158b 100644 --- a/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiRawMaterialService.java +++ b/mateclaw-server/src/main/java/vip/mate/wiki/service/WikiRawMaterialService.java @@ -232,47 +232,10 @@ public class WikiRawMaterialService { entity.setProgressPhase(null); entity.setProgressTotal(0); entity.setProgressDone(0); - // Fresh start clears any stale cancel request from a previous run. - entity.setCancelRequested(Boolean.FALSE); rawMapper.updateById(entity); return true; } - /** - * Mark a raw material for cancellation. Only valid while it is currently - * being processed; for any other status this is a no-op so the call is - * idempotent and safe to retry from the UI. - * - * @return {@code true} if the flag was set, {@code false} otherwise - */ - @Transactional - public boolean requestCancel(Long id) { - WikiRawMaterialEntity entity = rawMapper.selectById(id); - if (entity == null) { - return false; - } - if (!"processing".equals(entity.getProcessingStatus())) { - return false; - } - if (Boolean.TRUE.equals(entity.getCancelRequested())) { - // Already requested; treat as success without redundant write. - return true; - } - entity.setCancelRequested(Boolean.TRUE); - rawMapper.updateById(entity); - return true; - } - - /** - * Returns {@code true} if the user has asked to cancel this raw material's - * current processing run. Used by abort checkpoints inside the processing - * pipeline to bail out early. - */ - public boolean isCancelRequested(Long id) { - WikiRawMaterialEntity entity = rawMapper.selectById(id); - return entity != null && Boolean.TRUE.equals(entity.getCancelRequested()); - } - /** * RFC-012 M2 v2 UI:更新 wiki 两阶段消化的进度字段。 *

@@ -303,12 +266,6 @@ public class WikiRawMaterialService { if ("completed".equals(status)) { entity.setLastProcessedAt(java.time.LocalDateTime.now()); } - // Cancellation flag is only meaningful while a row is being processed. - // Any transition out of 'processing' clears it so the field reflects - // an idle row's true state and the next reprocess starts clean. - if (!"processing".equals(status)) { - entity.setCancelRequested(Boolean.FALSE); - } rawMapper.updateById(entity); } diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowEntity.java b/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowEntity.java deleted file mode 100644 index 7a1ced5e..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowEntity.java +++ /dev/null @@ -1,61 +0,0 @@ -package vip.mate.workflow.model; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.FieldStrategy; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableLogic; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * Stable workflow identity. The current draft is stored inline (1:1 with the - * workflow row) so PK uniqueness automatically guarantees a single draft; - * published snapshots live in {@code mate_workflow_revision}. - */ -@Data -@TableName("mate_workflow") -public class WorkflowEntity { - - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - private Long workspaceId; - - private String name; - - private String description; - - private Boolean enabled; - - /** Inline draft graph_json; null when there is no active draft. */ - @TableField(value = "draft_json", updateStrategy = FieldStrategy.ALWAYS) - private String draftJson; - - @TableField(value = "draft_schema_version", updateStrategy = FieldStrategy.ALWAYS) - private String draftSchemaVersion; - - @TableField(value = "draft_updated_by", updateStrategy = FieldStrategy.ALWAYS) - private Long draftUpdatedBy; - - @TableField(value = "draft_updated_at", updateStrategy = FieldStrategy.ALWAYS) - private LocalDateTime draftUpdatedAt; - - /** Pointer to the most recently published revision; null if never published. */ - @TableField(value = "latest_revision_id", updateStrategy = FieldStrategy.ALWAYS) - private Long latestRevisionId; - - private Long createdBy; - - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createTime; - - @TableField(fill = FieldFill.INSERT_UPDATE) - private LocalDateTime updateTime; - - @TableLogic - private Integer deleted; -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowPayloadEntity.java b/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowPayloadEntity.java deleted file mode 100644 index 22233c11..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowPayloadEntity.java +++ /dev/null @@ -1,48 +0,0 @@ -package vip.mate.workflow.model; - -import com.baomidou.mybatisplus.annotation.FieldStrategy; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * Payload body addressed by a stable URI. Small payloads (< 256KB) live - * inline in {@code contentBytes}; larger payloads point at filesystem or - * object storage via {@code storageKind} + {@code storageRef}. {@code sha256} - * is for tamper detection only — v0 does not deduplicate across runs. - */ -@Data -@TableName("mate_workflow_payload") -public class WorkflowPayloadEntity { - - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - private String payloadUri; - - private Long workspaceId; - - @TableField(value = "content_bytes", updateStrategy = FieldStrategy.ALWAYS) - private byte[] contentBytes; - - /** Storage flavour: inline / fs / s3 / oss. */ - private String storageKind; - - @TableField(value = "storage_ref", updateStrategy = FieldStrategy.ALWAYS) - private String storageRef; - - @TableField(value = "content_type", updateStrategy = FieldStrategy.ALWAYS) - private String contentType; - - @TableField(value = "sha256", updateStrategy = FieldStrategy.ALWAYS) - private String sha256; - - @TableField(value = "size_bytes", updateStrategy = FieldStrategy.ALWAYS) - private Long sizeBytes; - - private LocalDateTime createdAt; -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRevisionEntity.java b/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRevisionEntity.java deleted file mode 100644 index 733766b9..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRevisionEntity.java +++ /dev/null @@ -1,41 +0,0 @@ -package vip.mate.workflow.model; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.FieldStrategy; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * Immutable published snapshot of a workflow. The {@code revision} column is - * monotonic per workflow; rows are append-only after publish. - */ -@Data -@TableName("mate_workflow_revision") -public class WorkflowRevisionEntity { - - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - private Long workflowId; - - private Integer revision; - - @TableField(value = "graph_json", updateStrategy = FieldStrategy.ALWAYS) - private String graphJson; - - private String schemaVersion; - - @TableField(value = "published_note", updateStrategy = FieldStrategy.ALWAYS) - private String publishedNote; - - @TableField(value = "published_by", updateStrategy = FieldStrategy.ALWAYS) - private Long publishedBy; - - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createTime; -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunEntity.java b/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunEntity.java deleted file mode 100644 index 4f31b9f1..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunEntity.java +++ /dev/null @@ -1,61 +0,0 @@ -package vip.mate.workflow.model; - -import com.baomidou.mybatisplus.annotation.FieldFill; -import com.baomidou.mybatisplus.annotation.FieldStrategy; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableLogic; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * Workflow run instance. Run is locked to a specific revision for stability - * even when later revisions are published. Initial input and final output are - * stored as payload URIs to avoid bloating the run row. - */ -@Data -@TableName("mate_workflow_run") -public class WorkflowRunEntity { - - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - private Long workflowId; - - private Long revisionId; - - private Long workspaceId; - - /** State machine value: pending / running / paused / succeeded / failed / cancelled / timed_out. */ - private String state; - - @TableField(value = "triggered_by", updateStrategy = FieldStrategy.ALWAYS) - private String triggeredBy; - - @TableField(value = "triggered_meta", updateStrategy = FieldStrategy.ALWAYS) - private String triggeredMeta; - - @TableField(value = "initial_input_ref", updateStrategy = FieldStrategy.ALWAYS) - private String initialInputRef; - - @TableField(value = "final_output_ref", updateStrategy = FieldStrategy.ALWAYS) - private String finalOutputRef; - - @TableField(value = "error_message", updateStrategy = FieldStrategy.ALWAYS) - private String errorMessage; - - @TableField(value = "started_at", updateStrategy = FieldStrategy.ALWAYS) - private LocalDateTime startedAt; - - @TableField(value = "completed_at", updateStrategy = FieldStrategy.ALWAYS) - private LocalDateTime completedAt; - - @TableField(fill = FieldFill.INSERT) - private LocalDateTime createTime; - - @TableLogic - private Integer deleted; -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunPauseEntity.java b/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunPauseEntity.java deleted file mode 100644 index 0f5fe913..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunPauseEntity.java +++ /dev/null @@ -1,51 +0,0 @@ -package vip.mate.workflow.model; - -import com.baomidou.mybatisplus.annotation.FieldStrategy; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * Durable workflow pause row. Holds the resume token and links back to the - * external approval row (or other callback source) so that resume can be - * triggered idempotently after a JVM restart. - */ -@Data -@TableName("mate_workflow_run_pause") -public class WorkflowRunPauseEntity { - - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - private Long runId; - - private Long stepId; - - /** Source of the pause: await_approval, external_callback, etc. */ - private String pauseKind; - - /** Random server-generated token used as the resume entry key. */ - private String pauseToken; - - @TableField(value = "external_approval_id", updateStrategy = FieldStrategy.ALWAYS) - private Long externalApprovalId; - - private LocalDateTime pausedAt; - - @TableField(value = "resume_deadline", updateStrategy = FieldStrategy.ALWAYS) - private LocalDateTime resumeDeadline; - - @TableField(value = "resume_payload_ref", updateStrategy = FieldStrategy.ALWAYS) - private String resumePayloadRef; - - @TableField(value = "resumed_at", updateStrategy = FieldStrategy.ALWAYS) - private LocalDateTime resumedAt; - - /** Outcome on resume: approved / rejected / timeout / cancelled. */ - @TableField(value = "resume_outcome", updateStrategy = FieldStrategy.ALWAYS) - private String resumeOutcome; -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunStepEntity.java b/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunStepEntity.java deleted file mode 100644 index e4d0d706..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/model/WorkflowRunStepEntity.java +++ /dev/null @@ -1,68 +0,0 @@ -package vip.mate.workflow.model; - -import com.baomidou.mybatisplus.annotation.FieldStrategy; -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.TableField; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import lombok.Data; - -import java.time.LocalDateTime; - -/** - * Per-step execution row. {@code stepIndex} is the zero-based index in the - * revision's steps array; {@code iterationIndex} is reserved for fan_out - * iterations (and future loop bodies). - */ -@Data -@TableName("mate_workflow_run_step") -public class WorkflowRunStepEntity { - - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - private Long runId; - - private Integer stepIndex; - - @TableField(value = "iteration_index", updateStrategy = FieldStrategy.ALWAYS) - private Integer iterationIndex; - - @TableField(value = "step_name", updateStrategy = FieldStrategy.ALWAYS) - private String stepName; - - @TableField(value = "agent_id", updateStrategy = FieldStrategy.ALWAYS) - private Long agentId; - - private String state; - - @TableField(value = "input_ref", updateStrategy = FieldStrategy.ALWAYS) - private String inputRef; - - @TableField(value = "output_ref", updateStrategy = FieldStrategy.ALWAYS) - private String outputRef; - - @TableField(value = "output_summary", updateStrategy = FieldStrategy.ALWAYS) - private String outputSummary; - - @TableField(value = "output_content_type", updateStrategy = FieldStrategy.ALWAYS) - private String outputContentType; - - @TableField(value = "error_message", updateStrategy = FieldStrategy.ALWAYS) - private String errorMessage; - - @TableField(value = "duration_ms", updateStrategy = FieldStrategy.ALWAYS) - private Long durationMs; - - @TableField(value = "token_input", updateStrategy = FieldStrategy.ALWAYS) - private Integer tokenInput; - - @TableField(value = "token_output", updateStrategy = FieldStrategy.ALWAYS) - private Integer tokenOutput; - - @TableField(value = "started_at", updateStrategy = FieldStrategy.ALWAYS) - private LocalDateTime startedAt; - - @TableField(value = "completed_at", updateStrategy = FieldStrategy.ALWAYS) - private LocalDateTime completedAt; -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowMapper.java b/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowMapper.java deleted file mode 100644 index c4c6b662..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package vip.mate.workflow.repository; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import vip.mate.workflow.model.WorkflowEntity; - -@Mapper -public interface WorkflowMapper extends BaseMapper { -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowPayloadMapper.java b/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowPayloadMapper.java deleted file mode 100644 index 8b50cd2a..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowPayloadMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package vip.mate.workflow.repository; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import vip.mate.workflow.model.WorkflowPayloadEntity; - -@Mapper -public interface WorkflowPayloadMapper extends BaseMapper { -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRevisionMapper.java b/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRevisionMapper.java deleted file mode 100644 index 857009f0..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRevisionMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package vip.mate.workflow.repository; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import vip.mate.workflow.model.WorkflowRevisionEntity; - -@Mapper -public interface WorkflowRevisionMapper extends BaseMapper { -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunMapper.java b/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunMapper.java deleted file mode 100644 index 2fd57eb1..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package vip.mate.workflow.repository; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import vip.mate.workflow.model.WorkflowRunEntity; - -@Mapper -public interface WorkflowRunMapper extends BaseMapper { -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunPauseMapper.java b/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunPauseMapper.java deleted file mode 100644 index 5a9c2094..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunPauseMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package vip.mate.workflow.repository; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import vip.mate.workflow.model.WorkflowRunPauseEntity; - -@Mapper -public interface WorkflowRunPauseMapper extends BaseMapper { -} diff --git a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunStepMapper.java b/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunStepMapper.java deleted file mode 100644 index f7f78e03..00000000 --- a/mateclaw-server/src/main/java/vip/mate/workflow/repository/WorkflowRunStepMapper.java +++ /dev/null @@ -1,9 +0,0 @@ -package vip.mate.workflow.repository; - -import com.baomidou.mybatisplus.core.mapper.BaseMapper; -import org.apache.ibatis.annotations.Mapper; -import vip.mate.workflow.model.WorkflowRunStepEntity; - -@Mapper -public interface WorkflowRunStepMapper extends BaseMapper { -} diff --git a/mateclaw-server/src/main/java/vip/mate/workspace/conversation/ConversationService.java b/mateclaw-server/src/main/java/vip/mate/workspace/conversation/ConversationService.java index 6de391be..fea4f499 100644 --- a/mateclaw-server/src/main/java/vip/mate/workspace/conversation/ConversationService.java +++ b/mateclaw-server/src/main/java/vip/mate/workspace/conversation/ConversationService.java @@ -556,7 +556,6 @@ public class ConversationService { case "text" -> appendSegment(text, part.getText()); case "thinking", "tool_call", "parse_error" -> { /* skip — frontend reads these from contentParts directly */ } case "file" -> appendSegment(text, renderFilePart(part)); - case "image", "video", "audio", "model3d" -> appendSegment(text, renderMediaPart(part)); default -> appendSegment(text, part.getText()); } } @@ -615,36 +614,6 @@ public class ConversationService { return "[附件] " + name + "(路径: " + path + ")"; } - /** - * Render an image/video/audio/3D-model content part for the LLM prompt. - *

- * Without this marker, media parts are invisible in the rendered text — the LLM - * sees only the user's accompanying text and has no idea an attachment was sent. - * That fails closed when the multimodal Media injection in {@code BaseAgent} is - * upstream-stripped (model heuristic claims vision but the actual provider drops - * the image), leaving the agent to ask "which image?" for an attachment the user - * already uploaded. The path lets file-reading tools ({@code read_file}, - * {@code extract_document_text}, {@code detect_file_type}) work as a fallback. - */ - private String renderMediaPart(MessageContentPart part) { - String label = switch (part.getType()) { - case "image" -> "[图片]"; - case "video" -> "[视频]"; - case "audio" -> "[音频]"; - case "model3d" -> "[3D 模型]"; - default -> "[附件]"; - }; - String name = safe(part.getFileName()); - if (name.isBlank()) { - name = "未命名"; - } - String path = safe(part.getPath()); - if (path.isBlank()) { - return label + " " + name; - } - return label + " " + name + "(路径: " + path + ")"; - } - private void appendSegment(StringBuilder builder, String text) { String safeText = safe(text); if (safeText.isBlank()) { diff --git a/mateclaw-server/src/main/resources/db/migration/h2/V95__wiki_raw_material_cancel.sql b/mateclaw-server/src/main/resources/db/migration/h2/V95__wiki_raw_material_cancel.sql deleted file mode 100644 index 7de8846a..00000000 --- a/mateclaw-server/src/main/resources/db/migration/h2/V95__wiki_raw_material_cancel.sql +++ /dev/null @@ -1,6 +0,0 @@ --- V95: cancellation flag for in-progress wiki raw material processing. --- Lets the user request a stop on a long-running PDF analysis (e.g. when --- the embedding model has run out of credits) without having to delete --- the raw material. The processing pipeline checks the flag at its --- existing abort checkpoints and bails out with a 'cancelled' status. -ALTER TABLE mate_wiki_raw_material ADD COLUMN IF NOT EXISTS cancel_requested BOOLEAN NOT NULL DEFAULT FALSE; diff --git a/mateclaw-server/src/main/resources/db/migration/h2/V96__workflow_foundations.sql b/mateclaw-server/src/main/resources/db/migration/h2/V96__workflow_foundations.sql deleted file mode 100644 index 2990d072..00000000 --- a/mateclaw-server/src/main/resources/db/migration/h2/V96__workflow_foundations.sql +++ /dev/null @@ -1,173 +0,0 @@ --- V96: Foundational schema for the workflow runtime. --- Eight tables establish workflow identity (workflow + immutable revisions), --- run state (run + per-step rows + durable pause rows for await_approval), --- payload URI storage with inline / filesystem fallback, and trigger --- definitions paired with a dedup-window table for envelope-based event --- governance. H2 dialect uses CLOB for MEDIUMTEXT and BLOB for LONGBLOB; --- secondary indexes are emitted as separate CREATE INDEX statements per --- project convention. - --- 1. Stable workflow identity + draft (1:1 with workflow row). -CREATE TABLE IF NOT EXISTS mate_workflow ( - id BIGINT NOT NULL PRIMARY KEY, - workspace_id BIGINT NOT NULL, - name VARCHAR(128) NOT NULL, - description VARCHAR(1024), - enabled BOOLEAN NOT NULL DEFAULT TRUE, - draft_json CLOB, - draft_schema_version VARCHAR(8), - draft_updated_by BIGINT, - draft_updated_at TIMESTAMP, - latest_revision_id BIGINT, - created_by BIGINT, - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted INT NOT NULL DEFAULT 0 -); -CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_workspace_name - ON mate_workflow (workspace_id, name, deleted); - --- 2. Immutable published revisions; integer revision is monotonic per workflow. -CREATE TABLE IF NOT EXISTS mate_workflow_revision ( - id BIGINT NOT NULL PRIMARY KEY, - workflow_id BIGINT NOT NULL, - revision INT NOT NULL, - graph_json CLOB NOT NULL, - schema_version VARCHAR(8) NOT NULL, - published_note VARCHAR(512), - published_by BIGINT, - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_revision - ON mate_workflow_revision (workflow_id, revision); - --- 3. Workflow run instance; payload bodies live behind URIs in mate_workflow_payload. -CREATE TABLE IF NOT EXISTS mate_workflow_run ( - id BIGINT NOT NULL PRIMARY KEY, - workflow_id BIGINT NOT NULL, - revision_id BIGINT NOT NULL, - workspace_id BIGINT NOT NULL, - state VARCHAR(16) NOT NULL, - triggered_by VARCHAR(32), - triggered_meta CLOB, - initial_input_ref VARCHAR(256), - final_output_ref VARCHAR(256), - error_message VARCHAR(2048), - started_at TIMESTAMP, - completed_at TIMESTAMP, - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted INT NOT NULL DEFAULT 0 -); -CREATE INDEX IF NOT EXISTS idx_workflow_run_started - ON mate_workflow_run (workflow_id, started_at); - --- 4. Per-step run row; iteration_index reserved for fan_out (and future loop). -CREATE TABLE IF NOT EXISTS mate_workflow_run_step ( - id BIGINT NOT NULL PRIMARY KEY, - run_id BIGINT NOT NULL, - step_index INT NOT NULL, - iteration_index INT, - step_name VARCHAR(128), - agent_id BIGINT, - state VARCHAR(16), - input_ref VARCHAR(256), - output_ref VARCHAR(256), - output_summary VARCHAR(512), - output_content_type VARCHAR(64), - error_message VARCHAR(2048), - duration_ms BIGINT, - token_input INT, - token_output INT, - started_at TIMESTAMP, - completed_at TIMESTAMP -); -CREATE INDEX IF NOT EXISTS idx_workflow_run_step - ON mate_workflow_run_step (run_id, step_index, iteration_index); - --- 5. Durable pause rows so await_approval can resume across restarts. --- pause_token is the resume entry key; external_approval_id ties back to --- ApprovalWorkflowService rows so the approval callback can find the pause. -CREATE TABLE IF NOT EXISTS mate_workflow_run_pause ( - id BIGINT NOT NULL PRIMARY KEY, - run_id BIGINT NOT NULL, - step_id BIGINT NOT NULL, - pause_kind VARCHAR(32) NOT NULL, - pause_token VARCHAR(128) NOT NULL, - external_approval_id BIGINT, - paused_at TIMESTAMP NOT NULL, - resume_deadline TIMESTAMP, - resume_payload_ref VARCHAR(256), - resumed_at TIMESTAMP, - resume_outcome VARCHAR(32) -); -CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_pause_run_step - ON mate_workflow_run_pause (run_id, step_id); -CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_pause_token - ON mate_workflow_run_pause (pause_token); -CREATE INDEX IF NOT EXISTS idx_workflow_pause_external_approval - ON mate_workflow_run_pause (external_approval_id); -CREATE INDEX IF NOT EXISTS idx_workflow_pause_open_deadline - ON mate_workflow_run_pause (resumed_at, resume_deadline); - --- 6. Payload URI storage. Inline blob for < 256KB; storage_kind=fs/s3/oss --- carries the external object key in storage_ref. sha256 is for tamper --- detection only — v0 does not deduplicate across runs. -CREATE TABLE IF NOT EXISTS mate_workflow_payload ( - id BIGINT NOT NULL PRIMARY KEY, - payload_uri VARCHAR(256) NOT NULL, - workspace_id BIGINT NOT NULL, - content_bytes BLOB, - storage_kind VARCHAR(16) NOT NULL, - storage_ref VARCHAR(512), - content_type VARCHAR(64), - sha256 CHAR(64), - size_bytes BIGINT, - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP -); -CREATE UNIQUE INDEX IF NOT EXISTS uk_workflow_payload_uri - ON mate_workflow_payload (payload_uri); -CREATE INDEX IF NOT EXISTS idx_workflow_payload_workspace_created - ON mate_workflow_payload (workspace_id, created_at); - --- 7. Trigger definitions. pattern_version is a lamport counter that fire --- callbacks compare against on every fire to detect that another instance --- has updated the cron expression and self-cancel the local schedule. -CREATE TABLE IF NOT EXISTS mate_trigger ( - id BIGINT NOT NULL PRIMARY KEY, - workspace_id BIGINT NOT NULL, - name VARCHAR(128), - pattern_type VARCHAR(32) NOT NULL, - pattern_json CLOB NOT NULL, - target_type VARCHAR(16) NOT NULL, - target_id BIGINT NOT NULL, - payload_template CLOB, - rate_limit_per_min INT NOT NULL DEFAULT 60, - dedup_window_secs INT NOT NULL DEFAULT 60, - bot_self_filter BOOLEAN NOT NULL DEFAULT TRUE, - enabled BOOLEAN NOT NULL DEFAULT TRUE, - fire_count BIGINT NOT NULL DEFAULT 0, - max_fires BIGINT NOT NULL DEFAULT 0, - last_fired_at TIMESTAMP, - pattern_version BIGINT NOT NULL DEFAULT 1, - create_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - deleted INT NOT NULL DEFAULT 0 -); -CREATE INDEX IF NOT EXISTS idx_trigger_workspace_enabled - ON mate_trigger (workspace_id, enabled, deleted); -CREATE INDEX IF NOT EXISTS idx_trigger_target - ON mate_trigger (target_type, target_id); - --- 8. Event dedup window. dedup_key is envelope.eventId, falling back to --- sourceHash when the upstream channel did not provide a stable id. -CREATE TABLE IF NOT EXISTS mate_trigger_event ( - id BIGINT NOT NULL PRIMARY KEY, - trigger_id BIGINT NOT NULL, - dedup_key VARCHAR(128) NOT NULL, - received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - expires_at TIMESTAMP NOT NULL -); -CREATE UNIQUE INDEX IF NOT EXISTS uk_trigger_dedup - ON mate_trigger_event (trigger_id, dedup_key); -CREATE INDEX IF NOT EXISTS idx_trigger_event_expires - ON mate_trigger_event (expires_at); diff --git a/mateclaw-server/src/main/resources/db/migration/mysql/V95__wiki_raw_material_cancel.sql b/mateclaw-server/src/main/resources/db/migration/mysql/V95__wiki_raw_material_cancel.sql deleted file mode 100644 index 3a5e5130..00000000 --- a/mateclaw-server/src/main/resources/db/migration/mysql/V95__wiki_raw_material_cancel.sql +++ /dev/null @@ -1,9 +0,0 @@ --- V95: cancellation flag for in-progress wiki raw material processing. --- Lets the user request a stop on a long-running PDF analysis (e.g. when --- the embedding model has run out of credits) without having to delete --- the raw material. The processing pipeline checks the flag at its --- existing abort checkpoints and bails out with a 'cancelled' status. --- MySQL lacks `ADD COLUMN IF NOT EXISTS`; use INFORMATION_SCHEMA guard instead. -SET @c := (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mate_wiki_raw_material' AND COLUMN_NAME = 'cancel_requested'); -SET @s := IF(@c = 0, 'ALTER TABLE mate_wiki_raw_material ADD COLUMN cancel_requested BOOLEAN NOT NULL DEFAULT FALSE', 'SELECT 1'); -PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt; diff --git a/mateclaw-server/src/main/resources/db/migration/mysql/V96__workflow_foundations.sql b/mateclaw-server/src/main/resources/db/migration/mysql/V96__workflow_foundations.sql deleted file mode 100644 index edc23508..00000000 --- a/mateclaw-server/src/main/resources/db/migration/mysql/V96__workflow_foundations.sql +++ /dev/null @@ -1,162 +0,0 @@ --- V96: Foundational schema for the workflow runtime. --- Eight tables establish workflow identity (workflow + immutable revisions), --- run state (run + per-step rows + durable pause rows for await_approval), --- payload URI storage with inline / filesystem fallback, and trigger --- definitions paired with a dedup-window table for envelope-based event --- governance. CREATE TABLE IF NOT EXISTS is itself idempotent on MySQL. - --- 1. Stable workflow identity + draft (1:1 with workflow row). -CREATE TABLE IF NOT EXISTS mate_workflow ( - id BIGINT NOT NULL PRIMARY KEY, - workspace_id BIGINT NOT NULL, - name VARCHAR(128) NOT NULL, - description VARCHAR(1024), - enabled TINYINT NOT NULL DEFAULT 1, - draft_json MEDIUMTEXT, - draft_schema_version VARCHAR(8), - draft_updated_by BIGINT, - draft_updated_at DATETIME(3), - latest_revision_id BIGINT, - created_by BIGINT, - create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), - deleted INT NOT NULL DEFAULT 0, - UNIQUE KEY uk_workflow_workspace_name (workspace_id, name, deleted) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Workflow definition with stable identity and inline draft.'; - --- 2. Immutable published revisions; integer revision is monotonic per workflow. -CREATE TABLE IF NOT EXISTS mate_workflow_revision ( - id BIGINT NOT NULL PRIMARY KEY, - workflow_id BIGINT NOT NULL, - revision INT NOT NULL, - graph_json MEDIUMTEXT NOT NULL, - schema_version VARCHAR(8) NOT NULL, - published_note VARCHAR(512), - published_by BIGINT, - create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - UNIQUE KEY uk_workflow_revision (workflow_id, revision) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Immutable published workflow revisions.'; - --- 3. Workflow run instance; payload bodies live behind URIs in mate_workflow_payload. -CREATE TABLE IF NOT EXISTS mate_workflow_run ( - id BIGINT NOT NULL PRIMARY KEY, - workflow_id BIGINT NOT NULL, - revision_id BIGINT NOT NULL, - workspace_id BIGINT NOT NULL, - state VARCHAR(16) NOT NULL, - triggered_by VARCHAR(32), - triggered_meta MEDIUMTEXT, - initial_input_ref VARCHAR(256), - final_output_ref VARCHAR(256), - error_message VARCHAR(2048), - started_at DATETIME(3), - completed_at DATETIME(3), - create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - deleted INT NOT NULL DEFAULT 0, - KEY idx_workflow_run_started (workflow_id, started_at) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Workflow run instances locked to a specific revision.'; - --- 4. Per-step run row; iteration_index reserved for fan_out (and future loop). -CREATE TABLE IF NOT EXISTS mate_workflow_run_step ( - id BIGINT NOT NULL PRIMARY KEY, - run_id BIGINT NOT NULL, - step_index INT NOT NULL, - iteration_index INT, - step_name VARCHAR(128), - agent_id BIGINT, - state VARCHAR(16), - input_ref VARCHAR(256), - output_ref VARCHAR(256), - output_summary VARCHAR(512), - output_content_type VARCHAR(64), - error_message VARCHAR(2048), - duration_ms BIGINT, - token_input INT, - token_output INT, - started_at DATETIME(3), - completed_at DATETIME(3), - KEY idx_workflow_run_step (run_id, step_index, iteration_index) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Per-step run rows with input/output references and timings.'; - --- 5. Durable pause rows so await_approval can resume across restarts. -CREATE TABLE IF NOT EXISTS mate_workflow_run_pause ( - id BIGINT NOT NULL PRIMARY KEY, - run_id BIGINT NOT NULL, - step_id BIGINT NOT NULL, - pause_kind VARCHAR(32) NOT NULL, - pause_token VARCHAR(128) NOT NULL, - external_approval_id BIGINT, - paused_at DATETIME(3) NOT NULL, - resume_deadline DATETIME(3), - resume_payload_ref VARCHAR(256), - resumed_at DATETIME(3), - resume_outcome VARCHAR(32), - UNIQUE KEY uk_workflow_pause_run_step (run_id, step_id), - UNIQUE KEY uk_workflow_pause_token (pause_token), - KEY idx_workflow_pause_external_approval (external_approval_id), - KEY idx_workflow_pause_open_deadline (resumed_at, resume_deadline) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Durable workflow pause rows for await_approval resume.'; - --- 6. Payload URI storage. Inline blob for < 256KB; storage_kind=fs/s3/oss --- carries the external object key in storage_ref. -CREATE TABLE IF NOT EXISTS mate_workflow_payload ( - id BIGINT NOT NULL PRIMARY KEY, - payload_uri VARCHAR(256) NOT NULL, - workspace_id BIGINT NOT NULL, - content_bytes LONGBLOB, - storage_kind VARCHAR(16) NOT NULL, - storage_ref VARCHAR(512), - content_type VARCHAR(64), - sha256 CHAR(64), - size_bytes BIGINT, - created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - UNIQUE KEY uk_workflow_payload_uri (payload_uri), - KEY idx_workflow_payload_workspace_created (workspace_id, created_at) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Payload bodies addressed by stable URIs.'; - --- 7. Trigger definitions. pattern_version is a lamport counter that fire --- callbacks compare against on every fire to detect that another instance --- has updated the cron expression and self-cancel the local schedule. -CREATE TABLE IF NOT EXISTS mate_trigger ( - id BIGINT NOT NULL PRIMARY KEY, - workspace_id BIGINT NOT NULL, - name VARCHAR(128), - pattern_type VARCHAR(32) NOT NULL, - pattern_json MEDIUMTEXT NOT NULL, - target_type VARCHAR(16) NOT NULL, - target_id BIGINT NOT NULL, - payload_template MEDIUMTEXT, - rate_limit_per_min INT NOT NULL DEFAULT 60, - dedup_window_secs INT NOT NULL DEFAULT 60, - bot_self_filter TINYINT NOT NULL DEFAULT 1, - enabled TINYINT NOT NULL DEFAULT 1, - fire_count BIGINT NOT NULL DEFAULT 0, - max_fires BIGINT NOT NULL DEFAULT 0, - last_fired_at DATETIME(3), - pattern_version BIGINT NOT NULL DEFAULT 1, - create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3), - deleted INT NOT NULL DEFAULT 0, - KEY idx_trigger_workspace_enabled (workspace_id, enabled, deleted), - KEY idx_trigger_target (target_type, target_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Workflow / agent trigger definitions with pattern versioning.'; - --- 8. Event dedup window. dedup_key is envelope.eventId, falling back to --- sourceHash when the upstream channel did not provide a stable id. -CREATE TABLE IF NOT EXISTS mate_trigger_event ( - id BIGINT NOT NULL PRIMARY KEY, - trigger_id BIGINT NOT NULL, - dedup_key VARCHAR(128) NOT NULL, - received_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), - expires_at DATETIME(3) NOT NULL, - UNIQUE KEY uk_trigger_dedup (trigger_id, dedup_key), - KEY idx_trigger_event_expires (expires_at) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci - COMMENT = 'Per-trigger event dedup window with TTL-style expiry.'; diff --git a/mateclaw-ui/src/api/index.ts b/mateclaw-ui/src/api/index.ts index a1ec953d..6d1c7a9c 100644 --- a/mateclaw-ui/src/api/index.ts +++ b/mateclaw-ui/src/api/index.ts @@ -602,8 +602,6 @@ export const wikiApi = { http.delete(`/wiki/knowledge-bases/${kbId}/raw/${rawId}`), reprocessRaw: (kbId: number, rawId: number) => http.post(`/wiki/knowledge-bases/${kbId}/raw/${rawId}/reprocess`), - cancelRaw: (kbId: number, rawId: number) => - http.post(`/wiki/knowledge-bases/${kbId}/raw/${rawId}/cancel`), downloadRaw: (kbId: number, rawId: number) => http.get(`/wiki/knowledge-bases/${kbId}/raw/${rawId}/download`, { responseType: 'blob', diff --git a/mateclaw-ui/src/components/channels/ChannelEditModal.vue b/mateclaw-ui/src/components/channels/ChannelEditModal.vue index 03b24d54..26d17cbc 100644 --- a/mateclaw-ui/src/components/channels/ChannelEditModal.vue +++ b/mateclaw-ui/src/components/channels/ChannelEditModal.vue @@ -424,7 +424,6 @@ import { ref, computed, onMounted } from 'vue' import { useI18n } from 'vue-i18n' import { ElMessage } from 'element-plus' import { CHANNEL_FIELD_DEFS } from '@/types' -import { copyToClipboard } from '@/utils/clipboard' import type { Agent, Channel, ChannelFieldDef } from '@/types' import { buildConfigJson, @@ -630,7 +629,7 @@ const copyLabel = ref(t('channels.webhook.copy')) async function copyWebhookUrl() { try { - await copyToClipboard(webhookUrl.value) + await navigator.clipboard.writeText(webhookUrl.value) copyLabel.value = t('channels.webhook.copied') setTimeout(() => { copyLabel.value = t('channels.webhook.copy') }, 2000) } catch { @@ -640,7 +639,7 @@ async function copyWebhookUrl() { async function copyText(text: string) { try { - await copyToClipboard(text) + await navigator.clipboard.writeText(text) ElMessage.success(t('common.copied')) } catch { ElMessage.warning(t('channels.webhook.copyFailed')) diff --git a/mateclaw-ui/src/components/chat/MessageBubble.vue b/mateclaw-ui/src/components/chat/MessageBubble.vue index 3647a396..2586c036 100644 --- a/mateclaw-ui/src/components/chat/MessageBubble.vue +++ b/mateclaw-ui/src/components/chat/MessageBubble.vue @@ -366,7 +366,6 @@ import { useMarkdownRenderer } from '@/composables/useMarkdownRenderer' import { useAuthenticatedAttachment } from '@/composables/useAuthenticatedAttachment' import { useToolLabel } from '@/composables/useToolLabel' import { http } from '@/api' -import { copyToClipboard } from '@/utils/clipboard' import TypingCursor from './TypingCursor.vue' import BrowserTimeline from './BrowserTimeline.vue' import ToolCallSegment from './ToolCallSegment.vue' @@ -564,7 +563,7 @@ let copyTimer: ReturnType | null = null function copyMessage() { const text = displayContent.value || props.message.content || '' if (!text) return - copyToClipboard(text).then(() => { + navigator.clipboard.writeText(text).then(() => { copyState.value = 'copied' if (copyTimer) clearTimeout(copyTimer) copyTimer = setTimeout(() => { copyState.value = 'idle' }, 2000) diff --git a/mateclaw-ui/src/i18n/locales/en-US.ts b/mateclaw-ui/src/i18n/locales/en-US.ts index 30f8c4d6..915c0d65 100644 --- a/mateclaw-ui/src/i18n/locales/en-US.ts +++ b/mateclaw-ui/src/i18n/locales/en-US.ts @@ -1683,9 +1683,6 @@ export default { noRawMaterials: 'No raw materials yet', reprocess: 'Reprocess', resume: 'Resume', - cancel: 'Cancel processing', - cancelling: 'Cancelling…', - cancelledHint: 'Cancelled by user', download: 'Download original file', downloadFailed: 'Download failed', processAll: 'Process All Pending', @@ -1714,8 +1711,6 @@ export default { completed: 'COMPLETED', partial: 'PARTIAL', failed: 'FAILED', - cancelled: 'CANCELLED', - cancelling: 'CANCELLING…', }, progress: { preparing: 'Preparing…', diff --git a/mateclaw-ui/src/i18n/locales/zh-CN.ts b/mateclaw-ui/src/i18n/locales/zh-CN.ts index 5350ecdb..3ff982e3 100644 --- a/mateclaw-ui/src/i18n/locales/zh-CN.ts +++ b/mateclaw-ui/src/i18n/locales/zh-CN.ts @@ -1695,9 +1695,6 @@ export default { noRawMaterials: '暂无原始材料', reprocess: '重新处理', resume: '继续生成', - cancel: '取消处理', - cancelling: '正在取消…', - cancelledHint: '用户已取消处理', download: '下载原始文件', downloadFailed: '下载失败', processAll: '处理所有待处理材料', @@ -1726,8 +1723,6 @@ export default { completed: '已完成', partial: '部分完成', failed: '失败', - cancelled: '已取消', - cancelling: '正在取消…', }, progress: { preparing: '准备中…', diff --git a/mateclaw-ui/src/utils/clipboard.ts b/mateclaw-ui/src/utils/clipboard.ts deleted file mode 100644 index c1c881cf..00000000 --- a/mateclaw-ui/src/utils/clipboard.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copy text to clipboard with fallback for non-HTTPS contexts - * where navigator.clipboard is undefined. - */ -export async function copyToClipboard(text: string): Promise { - if (navigator.clipboard) { - return navigator.clipboard.writeText(text) - } - // Fallback: textarea + execCommand('copy') - const ta = document.createElement('textarea') - ta.value = text - ta.style.position = 'fixed' - ta.style.left = '-9999px' - document.body.appendChild(ta) - ta.select() - document.execCommand('copy') - document.body.removeChild(ta) -} diff --git a/mateclaw-ui/src/views/AgentContext.vue b/mateclaw-ui/src/views/AgentContext.vue index d69dac7c..830cfed6 100644 --- a/mateclaw-ui/src/views/AgentContext.vue +++ b/mateclaw-ui/src/views/AgentContext.vue @@ -208,7 +208,6 @@ import { useI18n } from 'vue-i18n' import { ElMessage } from 'element-plus' import { mcConfirm } from '@/components/common/useConfirm' import { agentApi, agentContextApi } from '@/api/index' -import { copyToClipboard } from '@/utils/clipboard' import type { Agent, WorkspaceFile } from '@/types/index' import { useMarkdownRenderer } from '@/composables/useMarkdownRenderer' import { plainTextIcon } from '@/composables/usePixelarticons' @@ -331,7 +330,7 @@ function handlePreviewClick(e: MouseEvent) { const encoded = btn.getAttribute('data-code') if (!encoded) return const code = decodeURIComponent(encoded) - copyToClipboard(code).then(() => { + navigator.clipboard.writeText(code).then(() => { btn.classList.add('copied') const textEl = btn.querySelector('.code-block__copy-text') if (textEl) textEl.textContent = t('chat.copied') || 'Copied' diff --git a/mateclaw-ui/src/views/ChatConsole.vue b/mateclaw-ui/src/views/ChatConsole.vue index 65ae2387..ef5dc920 100644 --- a/mateclaw-ui/src/views/ChatConsole.vue +++ b/mateclaw-ui/src/views/ChatConsole.vue @@ -317,7 +317,6 @@ import { mcConfirm } from '@/components/common/useConfirm' import { ChatDotRound, Delete, Plus, Setting, UploadFilled } from '@element-plus/icons-vue' import { conversationApi, agentApi, modelApi, chatApi, cronJobApi } from '@/api/index' import { channelIconUrl } from '@/utils/channelSource' -import { copyToClipboard } from '@/utils/clipboard' import { useChat } from '@/composables/chat/useChat' import { reconstructErrorInfo } from '@/types/chatError' import { reconcileMessages, extractMessages } from '@/utils/messageReconcile' @@ -1742,7 +1741,7 @@ function handleCodeCopy(e: MouseEvent) { const encoded = btn.getAttribute('data-code') if (!encoded) return const code = decodeURIComponent(encoded) - copyToClipboard(code).then(() => { + navigator.clipboard.writeText(code).then(() => { btn.classList.add('copied') const textEl = btn.querySelector('.code-block__copy-text') if (textEl) textEl.textContent = t('chat.copied') diff --git a/mateclaw-ui/src/views/Settings/Models/modals/DeviceCodeDialog.vue b/mateclaw-ui/src/views/Settings/Models/modals/DeviceCodeDialog.vue index 6e38057f..0b5e8319 100644 --- a/mateclaw-ui/src/views/Settings/Models/modals/DeviceCodeDialog.vue +++ b/mateclaw-ui/src/views/Settings/Models/modals/DeviceCodeDialog.vue @@ -41,7 +41,6 @@ import { computed, onBeforeUnmount, ref, watch } from 'vue' import { useI18n } from 'vue-i18n' import { ElMessage } from 'element-plus' -import { copyToClipboard } from '@/utils/clipboard' const props = defineProps<{ visible: boolean @@ -84,7 +83,7 @@ onBeforeUnmount(() => { async function copyCode() { try { - await copyToClipboard(props.userCode) + await navigator.clipboard.writeText(props.userCode) copied.value = true setTimeout(() => { copied.value = false }, 2000) } catch { diff --git a/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue b/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue index 62695660..be976b7e 100644 --- a/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue +++ b/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue @@ -15,15 +15,9 @@

-
- - {{ t(`wiki.jobStage.${stage.key}`) }} - -
+ + {{ t(`wiki.jobStage.${stage.key}`) }} +
@@ -180,16 +174,6 @@ const elapsed = computed(() => { flex: 1; } -/* The last cell only contains a dot (no trailing connector line). With - * flex:1 it would reserve a full segment of empty space after the dot, - * which makes the final stage look stranded — the connector going into - * it appears to stop short and there's a visible blank gap on the right. - * Pin the last cell to dot-width so the 6 connector lines distribute - * evenly between the 7 dots and the final dot anchors to the right edge. */ -.stage-dot-group:last-child { - flex: 0 0 auto; -} - .stage-dot { width: 10px; height: 10px; @@ -217,33 +201,15 @@ const elapsed = computed(() => { } .stage-line.done { background: var(--mc-primary); } -/* Labels mirror the dot-row's flex structure so each label cell is the - * same width as its matching dot cell. The last cell pins to dot-width - * (matching .stage-dot-group:last-child above), and inside every cell the - * label is centered horizontally over the dot at the cell's left edge by - * shifting it half the dot width and translating back by half its own - * width — keeping label text centered above each dot at any container - * width. */ .stage-labels { display: flex; - align-items: flex-start; -} -.stage-label-cell { - flex: 1; - display: flex; - justify-content: flex-start; - overflow: visible; -} -.stage-label-cell:last-child { - flex: 0 0 auto; + justify-content: space-between; } .stage-label { font-size: 9px; color: var(--mc-text-tertiary); text-align: center; - white-space: nowrap; - margin-left: 5px; - transform: translateX(-50%); + flex: 1; } .stage-label.active { color: var(--mc-primary); font-weight: 600; } .stage-label.done { color: var(--mc-success, #5a8a5a); font-weight: 600; } diff --git a/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue b/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue index bff2bb28..69c101a8 100644 --- a/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue +++ b/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue @@ -138,26 +138,15 @@ {{ raw.sourceType }}
- - {{ cancellingIds.has(raw.id) && raw.processingStatus === 'processing' - ? t('wiki.status.cancelling') - : t(`wiki.status.${raw.processingStatus}`) }} + + {{ t(`wiki.status.${raw.processingStatus}`) }} {{ raw.pageCount }} - {{ t('wiki.cancelledHint') }} - - {{ raw.errorMessage }} @@ -165,25 +154,7 @@
- -