Keep isFromGlobalInput out of the payload it is not part of
Jackson reads an is-prefixed no-arg boolean as a property, so the helper added with the field put a "fromGlobalInput" into every block payload and every persisted flow - a key the generated schema never declares, sitting next to the one it is derived from. ModelParameters.isEmpty carries the same guard for the same reason. Also cover the two steps between this field and the code that reads it: the generated schema has to carry it, and a configuration posted back by the editor has to survive deserialization into a block whose ports reflect the choice. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f4693ac6ab
commit
1d5d1eb78b
|
|
@ -1,5 +1,6 @@
|
|||
package it.cnr.isti.workflow.manager.blocks.configurations;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
|
||||
import it.cnr.isti.workflow.manager.configurations.annotations.FieldRetriever;
|
||||
|
|
@ -40,7 +41,15 @@ public record MCPAgentUploadInput(
|
|||
this(name, kind, multiple, null);
|
||||
}
|
||||
|
||||
/** Whether this attachment comes from a global input rather than a port of its own. */
|
||||
/**
|
||||
* Whether this attachment comes from a global input rather than a port of its own.
|
||||
*
|
||||
* <p>{@code @JsonIgnore} because Jackson reads an is-prefixed no-arg boolean as a property:
|
||||
* without it a "fromGlobalInput" field appeared in every API payload and every persisted flow,
|
||||
* in a shape the schema does not declare - the same trap {@code ModelParameters.isEmpty} carries
|
||||
* the same guard for.
|
||||
*/
|
||||
@JsonIgnore
|
||||
public boolean isFromGlobalInput() {
|
||||
return globalInput != null && !globalInput.isBlank();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
package it.cnr.isti.workflow.manager.blocks.configurations;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
|
||||
import tools.jackson.databind.JsonNode;
|
||||
|
||||
import it.cnr.isti.workflow.manager.app.ObjectMapperHolder;
|
||||
import it.cnr.isti.workflow.manager.blocks.Block;
|
||||
import it.cnr.isti.workflow.manager.blocks.types.MCPAgentBlockType;
|
||||
import it.cnr.isti.workflow.manager.controllers.BlocksController;
|
||||
|
||||
@SpringBootTest
|
||||
@TestPropertySource(locations = "classpath:test.properties")
|
||||
public class MCPAgentUploadGlobalSchemaTest {
|
||||
|
||||
@Autowired
|
||||
private JsonSchemaProducer schemaProducer;
|
||||
|
||||
@Autowired
|
||||
private BlocksController blocksController;
|
||||
|
||||
@Test
|
||||
public void theEditorIsToldAnUploadCanNameAGlobal() {
|
||||
JsonNode schema = schemaProducer.generateSchemaNode(MCPAgentBlockConfiguration.class);
|
||||
assertNotNull(schema);
|
||||
String asText = schema.toString();
|
||||
assertTrue(asText.contains("globalInput"), "the upload input schema must carry the new field: " + asText);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aBlockWhoseUploadNamesAGlobalSurvivesTheRoundTripTheEditorMakes() {
|
||||
// Exactly what the editor posts back when the field is filled from the dropdown.
|
||||
String payload = """
|
||||
{"type":"MCPAgentBlockConfiguration","name":"Read the plan","model":"llama3.1:8b",
|
||||
"prompt":"Summarize the attached plan","mcpServers":[],
|
||||
"uploadInputs":[{"name":"planDoc","kind":"DOCUMENT","multiple":false,"globalInput":"document"}],
|
||||
"shareSession":false,"useSharedSession":false}
|
||||
""";
|
||||
MCPAgentBlockConfiguration configuration =
|
||||
ObjectMapperHolder.mapper.readValue(payload, MCPAgentBlockConfiguration.class);
|
||||
assertEquals("document", configuration.getUploadInputs().getFirst().globalInput());
|
||||
|
||||
Block<MCPAgentBlockType> block = blocksController.create(configuration, null, null);
|
||||
|
||||
assertNotNull(block);
|
||||
assertTrue(block.getInputs().stream().noneMatch(input -> input.getName().equals("planDoc")));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package it.cnr.isti.workflow.manager.blocks.configurations;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import tools.jackson.databind.ObjectMapper;
|
||||
|
||||
public class MCPAgentUploadInputJsonTest {
|
||||
|
||||
private final ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
@Test
|
||||
public void readsAnUploadInputThatNamesAGlobal() {
|
||||
MCPAgentUploadInput upload = mapper.readValue(
|
||||
"{\"name\":\"planDoc\",\"kind\":\"DOCUMENT\",\"multiple\":false,\"globalInput\":\"document\"}",
|
||||
MCPAgentUploadInput.class);
|
||||
|
||||
assertEquals("planDoc", upload.name());
|
||||
assertEquals("document", upload.globalInput());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readsAnUploadInputSavedBeforeGlobalsWereAnOption() {
|
||||
MCPAgentUploadInput upload = mapper.readValue(
|
||||
"{\"name\":\"planDoc\",\"kind\":\"DOCUMENT\",\"multiple\":false}",
|
||||
MCPAgentUploadInput.class);
|
||||
|
||||
assertEquals("planDoc", upload.name());
|
||||
assertNull(upload.globalInput());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void doesNotInventAFieldTheSchemaNeverDeclared() {
|
||||
// Jackson reads an is-prefixed no-arg boolean as a property, so isFromGlobalInput() would
|
||||
// otherwise put a "fromGlobalInput" into every payload and every persisted flow.
|
||||
String written = mapper.writeValueAsString(
|
||||
new MCPAgentUploadInput("planDoc", MCPAgentUploadInput.MCPAgentUploadKind.DOCUMENT, false, "document"));
|
||||
|
||||
assertFalse(written.contains("fromGlobalInput"), written);
|
||||
assertTrue(written.contains("globalInput"), written);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue