mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(tools): preserve cron job id tool inputs
This commit is contained in:
parent
04a61fda19
commit
c4751ea69c
@ -217,17 +217,18 @@ public class CronJobTool {
|
||||
@Tool(description = "Enable or disable a scheduled task by its job ID. "
|
||||
+ "Use list_cron_jobs first to find the job ID.")
|
||||
public String toggle_cron_job(
|
||||
@ToolParam(description = "Job ID (number)") Long jobId,
|
||||
@ToolParam(description = "Job ID. Must be passed as a string to preserve large integer precision") String jobId,
|
||||
@ToolParam(description = "true to enable, false to disable") Boolean enabled,
|
||||
@Nullable ToolContext ctx) {
|
||||
try {
|
||||
Long parsedJobId = parseJobId(jobId);
|
||||
// RFC-083: scope toggle to the originating workspace.
|
||||
Long workspaceId = workspaceFromContext(ctx);
|
||||
cronJobService.toggle(jobId, enabled, workspaceId);
|
||||
CronJobDTO updated = cronJobService.getById(jobId, workspaceId);
|
||||
cronJobService.toggle(parsedJobId, enabled, workspaceId);
|
||||
CronJobDTO updated = cronJobService.getById(parsedJobId, workspaceId);
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("jobId", jobId);
|
||||
result.put("jobId", parsedJobId);
|
||||
result.put("name", updated.getName());
|
||||
result.put("enabled", updated.getEnabled());
|
||||
result.put("nextRunTime", updated.getNextRunTime() != null ? updated.getNextRunTime().toString() : "");
|
||||
@ -242,14 +243,15 @@ public class CronJobTool {
|
||||
@Tool(description = "Delete a scheduled task by its job ID. This action requires user approval. "
|
||||
+ "Use list_cron_jobs first to find the job ID.")
|
||||
public String delete_cron_job(
|
||||
@ToolParam(description = "Job ID (number) to delete") Long jobId,
|
||||
@ToolParam(description = "Job ID to delete. Must be passed as a string to preserve large integer precision") String jobId,
|
||||
@Nullable ToolContext ctx) {
|
||||
try {
|
||||
Long parsedJobId = parseJobId(jobId);
|
||||
// RFC-083: scope delete to the originating workspace.
|
||||
Long workspaceId = workspaceFromContext(ctx);
|
||||
CronJobDTO job = cronJobService.getById(jobId, workspaceId);
|
||||
CronJobDTO job = cronJobService.getById(parsedJobId, workspaceId);
|
||||
String jobName = job.getName();
|
||||
cronJobService.delete(jobId, workspaceId);
|
||||
cronJobService.delete(parsedJobId, workspaceId);
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("success", true);
|
||||
result.put("deleted", jobName);
|
||||
@ -267,6 +269,18 @@ public class CronJobTool {
|
||||
return writeJson(result);
|
||||
}
|
||||
|
||||
private Long parseJobId(String jobId) {
|
||||
String trimmed = jobId != null ? jobId.trim() : "";
|
||||
if (trimmed.isEmpty()) {
|
||||
throw new IllegalArgumentException("jobId is required");
|
||||
}
|
||||
try {
|
||||
return Long.parseLong(trimmed);
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("jobId must be a numeric string");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize tool output through the id-safe application ObjectMapper so every
|
||||
* {@code Long} (notably {@code jobId}) is rendered as a string. Falls back to
|
||||
|
||||
@ -1,11 +1,14 @@
|
||||
package vip.mate.tool.builtin;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.ai.support.ToolCallbacks;
|
||||
import org.springframework.ai.tool.ToolCallback;
|
||||
import vip.mate.cron.model.CronJobDTO;
|
||||
import vip.mate.cron.service.CronJobService;
|
||||
|
||||
@ -52,4 +55,29 @@ class CronJobToolIdPrecisionTest {
|
||||
assertFalse(out.contains(": " + bigId) || out.contains(":" + bigId),
|
||||
"jobId must NOT appear as a bare JSON number");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("cron mutating tools publish jobId as a string parameter so LLM tool calls preserve precision")
|
||||
void cronJobIdSchemasAreString() throws Exception {
|
||||
CronJobTool tool = new CronJobTool(mock(CronJobService.class), idSafeMapper());
|
||||
|
||||
assertJobIdIsString(tool, "toggle_cron_job");
|
||||
assertJobIdIsString(tool, "delete_cron_job");
|
||||
}
|
||||
|
||||
private static void assertJobIdIsString(Object tool, String name) throws Exception {
|
||||
JsonNode root = idSafeMapper().readTree(callback(tool, name).getToolDefinition().inputSchema());
|
||||
|
||||
assertTrue("string".equals(root.at("/properties/jobId/type").asText()),
|
||||
name + " jobId must be a string schema");
|
||||
}
|
||||
|
||||
private static ToolCallback callback(Object tool, String name) {
|
||||
for (ToolCallback callback : ToolCallbacks.from(tool)) {
|
||||
if (name.equals(callback.getToolDefinition().name())) {
|
||||
return callback;
|
||||
}
|
||||
}
|
||||
throw new AssertionError("Missing tool callback: " + name);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user