Give a global input of a file kind somewhere to be uploaded to
The only endpoints for a global input took JSON, so an upload aimed at one was refused before it reached any handler: "Content-Type 'multipart/form-data' is not supported". A flow whose global input is a file - the plan document of the orchestrator flows, for one - could therefore never be given its file at all. Add the multipart pair the node inputs already had, named the same way because it is the same operation on the other scope. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
01d739ee7d
commit
4516105d74
|
|
@ -432,6 +432,46 @@ public class ExecutionsController {
|
|||
executionService.setGlobalInputs(visibleExecution(executionId, userDetails).getId(), values));
|
||||
}
|
||||
|
||||
/**
|
||||
* The file counterpart of {@link #setGlobalInput}, which takes JSON and so could never carry an
|
||||
* upload: a global input of a file kind had nowhere to be uploaded to, and the attempt came back
|
||||
* as an unsupported content type. Named like the node-input upload endpoints because it is the
|
||||
* same operation on the other scope.
|
||||
*/
|
||||
@PutMapping(path = "{executionId}/globals/{key}/file", consumes = "multipart/form-data")
|
||||
@Operation(summary = "Prepares a global file input",
|
||||
description = "Uploads one file and stores it as the value of a global flow input.")
|
||||
public ExecutionView prepareGlobalFileInput(@PathVariable String executionId, @PathVariable String key,
|
||||
@RequestParam MultipartFile file, @AuthenticationPrincipal LoginEntity userDetails) {
|
||||
try {
|
||||
File myFile = createUploadTempFile(key, file.getOriginalFilename());
|
||||
file.transferTo(myFile);
|
||||
return ExecutionView.fromExecution(
|
||||
executionService.setGlobalInput(visibleExecution(executionId, userDetails).getId(), key, myFile));
|
||||
} catch (IOException e) {
|
||||
throw new WebServerException("Error while creating file", e);
|
||||
}
|
||||
}
|
||||
|
||||
@PutMapping(path = "{executionId}/globals/{key}/files", consumes = "multipart/form-data")
|
||||
@Operation(summary = "Prepares a global file array input",
|
||||
description = "Uploads multiple files and stores them as the value of a global flow input.")
|
||||
public ExecutionView prepareGlobalFileArrayInput(@PathVariable String executionId, @PathVariable String key,
|
||||
@RequestParam List<MultipartFile> files, @AuthenticationPrincipal LoginEntity userDetails) {
|
||||
List<File> preparedFiles = new ArrayList<>();
|
||||
try {
|
||||
for (MultipartFile file : files) {
|
||||
File myFile = createUploadTempFile(key, file.getOriginalFilename());
|
||||
file.transferTo(myFile);
|
||||
preparedFiles.add(myFile);
|
||||
}
|
||||
return ExecutionView.fromExecution(
|
||||
executionService.setGlobalInput(visibleExecution(executionId, userDetails).getId(), key, preparedFiles));
|
||||
} catch (IOException e) {
|
||||
throw new WebServerException("Error while creating file", e);
|
||||
}
|
||||
}
|
||||
|
||||
private ExecutionObject visibleExecution(String id, LoginEntity userDetails) {
|
||||
if (userDetails == null) {
|
||||
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "Authentication required");
|
||||
|
|
|
|||
|
|
@ -779,6 +779,38 @@ public class ExecutionControllerTest {
|
|||
"the uploaded file keeps its extension, so whatever reads it back still sees a PDF: " + storedInput);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aGlobalInputOfAFileKindCanBeUploadedTo() {
|
||||
// The only endpoint for a global input took JSON, so a global declared as a file had nowhere
|
||||
// to be uploaded to: the request came back as HttpMediaTypeNotSupportedException.
|
||||
Block<LLMBlockType> block = blocksController.create(LLMBlockConfiguration.builder()
|
||||
.name("Summarize")
|
||||
.prompt("Summarize ${{global.document}}")
|
||||
.llmDescriptor(LLMDescriptor.builder().provider("testProvider").model("testModel").build())
|
||||
.build());
|
||||
|
||||
FlowCreateRequest request = new FlowCreateRequest(
|
||||
"Global File Input Flow",
|
||||
"Flow whose global input carries an uploaded file",
|
||||
FlowData.builder()
|
||||
.block(block)
|
||||
.globalInput(IODescriptor.of("document", IOType.FILE))
|
||||
.build());
|
||||
ResponseEntity<FlowView> createdFlow = flowController.createFlow(request, testUser());
|
||||
ExecutionView execution = executionsController.create(createdFlow.getBody().id(), testUser());
|
||||
|
||||
org.springframework.mock.web.MockMultipartFile upload = new org.springframework.mock.web.MockMultipartFile(
|
||||
"file", "plan.pdf", "application/pdf", "%PDF-1.7\nbody".getBytes(java.nio.charset.StandardCharsets.US_ASCII));
|
||||
|
||||
ExecutionView withFile = executionsController.prepareGlobalFileInput(execution.getId(), "document",
|
||||
upload, testUser());
|
||||
|
||||
Object stored = withFile.getContext().getGlobalInputs().get("document");
|
||||
org.junit.jupiter.api.Assertions.assertNotNull(stored);
|
||||
org.junit.jupiter.api.Assertions.assertTrue(String.valueOf(stored).endsWith("plan.pdf"),
|
||||
"the global holds the uploaded file, extension intact: " + stored);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancelExecutionClearsRuntimeStateAndMarksExecutionCancelled() {
|
||||
LLMDescriptor llmDescriptor = LLMDescriptor.builder()
|
||||
|
|
|
|||
Loading…
Reference in New Issue