Finalize async-only assistant flow endpoints, sync client flow edits into session
Removes the now-unused synchronous /flows/draft, /flows/refine and /flows/fix mappings in favor of the session-based submitMessage + polling flow, and lets submitMessage accept an optional flow snapshot so manual canvas edits made outside the chat are reflected before the assistant acts on the next message.
This commit is contained in:
parent
7f57b8683a
commit
ee695b2f5f
|
|
@ -101,6 +101,9 @@ public class AssistantConversationService {
|
|||
throw new ResponseStatusException(HttpStatus.CONFLICT,
|
||||
"A call is already in progress for this session");
|
||||
}
|
||||
if (request.flow() != null) {
|
||||
session.currentFlow = request.flow();
|
||||
}
|
||||
session.appendMessage(AssistantMessageRole.USER, request.message(), null);
|
||||
|
||||
Instant now = Instant.now();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,12 @@
|
|||
package it.cnr.isti.workflow.manager.assistant.model;
|
||||
|
||||
import it.cnr.isti.workflow.manager.flows.model.FlowCreateRequest;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
|
||||
public record AssistantSessionMessageRequest(@NotBlank String message) {
|
||||
public record AssistantSessionMessageRequest(@NotBlank String message, @Valid FlowCreateRequest flow) {
|
||||
|
||||
public AssistantSessionMessageRequest(String message) {
|
||||
this(message, null);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,45 +61,25 @@ public class AssistantController {
|
|||
@Value("${app.assistant.default-repair-model:${app.assistant.default-model}}")
|
||||
private String defaultRepairModel;
|
||||
|
||||
/** Convenience entry point for in-process callers; HTTP requests use the authenticated overload. */
|
||||
/**
|
||||
* Test-only entry point. Generation is exposed to real clients exclusively through the
|
||||
* async session flow (POST /sessions/{id}/messages + GET /calls/{callId}) so the caller
|
||||
* gets progress updates instead of blocking on a multi-minute request.
|
||||
*/
|
||||
public AssistantFlowResponse draft(AssistantGenerationRequest request) {
|
||||
return flowAssistantService.draft(request);
|
||||
}
|
||||
|
||||
@PostMapping("/flows/draft")
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
@Operation(summary = "Generate a flow draft", description = "Creates a flow draft from a natural language request.")
|
||||
public AssistantFlowResponse draftForUser(@RequestBody @Valid AssistantGenerationRequest request,
|
||||
@AuthenticationPrincipal LoginEntity userDetails) {
|
||||
return flowAssistantService.draft(request, userDetails.getUsername());
|
||||
}
|
||||
|
||||
/** Convenience entry point for in-process callers; HTTP requests use the authenticated overload. */
|
||||
/** Test-only entry point; see {@link #draft(AssistantGenerationRequest)}. */
|
||||
public AssistantFlowResponse refine(AssistantRefineRequest request) {
|
||||
return flowAssistantService.refine(request);
|
||||
}
|
||||
|
||||
@PostMapping("/flows/refine")
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
@Operation(summary = "Refine an existing flow", description = "Updates an existing flow definition using a natural language request.")
|
||||
public AssistantFlowResponse refineForUser(@RequestBody @Valid AssistantRefineRequest request,
|
||||
@AuthenticationPrincipal LoginEntity userDetails) {
|
||||
return flowAssistantService.refine(request, userDetails.getUsername());
|
||||
}
|
||||
|
||||
/** Convenience entry point for in-process callers; HTTP requests use the authenticated overload. */
|
||||
/** Test-only entry point; see {@link #draft(AssistantGenerationRequest)}. */
|
||||
public AssistantFlowResponse fix(AssistantFixRequest request) {
|
||||
return flowAssistantService.fix(request);
|
||||
}
|
||||
|
||||
@PostMapping("/flows/fix")
|
||||
@SecurityRequirement(name = "bearerAuth")
|
||||
@Operation(summary = "Fix an invalid flow", description = "Repairs a flow using backend validation errors and optional user guidance.")
|
||||
public AssistantFlowResponse fixForUser(@RequestBody @Valid AssistantFixRequest request,
|
||||
@AuthenticationPrincipal LoginEntity userDetails) {
|
||||
return flowAssistantService.fix(request, userDetails.getUsername());
|
||||
}
|
||||
|
||||
/** Convenience entry point for in-process callers; HTTP requests use the authenticated overload. */
|
||||
public AssistantExplainResponse explain(AssistantExplainRequest request) {
|
||||
return flowAssistantService.explain(request);
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import org.springframework.test.web.servlet.MockMvc;
|
|||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import it.cnr.isti.workflow.manager.app.ObjectMapperHolder;
|
||||
import it.cnr.isti.workflow.manager.assistant.FlowAssistantService;
|
||||
import it.cnr.isti.workflow.manager.assistant.model.AssistantCallAcceptedResponse;
|
||||
import it.cnr.isti.workflow.manager.assistant.model.AssistantCallStatus;
|
||||
import it.cnr.isti.workflow.manager.assistant.model.AssistantCallView;
|
||||
|
|
@ -95,6 +96,9 @@ public class AssistantControllerTest {
|
|||
@Autowired
|
||||
private AssistantController assistantController;
|
||||
|
||||
@Autowired
|
||||
private FlowAssistantService flowAssistantService;
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
|
|
@ -231,11 +235,11 @@ public class AssistantControllerTest {
|
|||
VaultSecretView credential = userSecretService.create("testuser",
|
||||
new VaultSecretCreateRequest("gemini assistant", "Gemini", null, "user-gemini-key"));
|
||||
|
||||
AssistantFlowResponse response = assistantController.draftForUser(new AssistantGenerationRequest(
|
||||
AssistantFlowResponse response = flowAssistantService.draft(new AssistantGenerationRequest(
|
||||
"create a flow that classifies incoming tickets",
|
||||
1,
|
||||
new AssistantLlmSelection("Gemini", MODEL, null, credential.id())),
|
||||
new LoginEntity("testuser", "testpassword"));
|
||||
"testuser");
|
||||
|
||||
assertTrue(response.valid(), () -> "Unexpected validation errors: " + response.validationErrors());
|
||||
LLMBlockConfiguration configuration = (LLMBlockConfiguration) response.flow().flow().getBlocks().getFirst()
|
||||
|
|
@ -252,11 +256,11 @@ public class AssistantControllerTest {
|
|||
Mockito.when(geminiLLMProvider.requiresAuthorization()).thenReturn(true);
|
||||
|
||||
ResponseStatusException exception = assertThrows(ResponseStatusException.class,
|
||||
() -> assistantController.draftForUser(new AssistantGenerationRequest(
|
||||
() -> flowAssistantService.draft(new AssistantGenerationRequest(
|
||||
"create a flow that classifies incoming tickets",
|
||||
1,
|
||||
new AssistantLlmSelection("Gemini", MODEL, null, null)),
|
||||
new LoginEntity("testuser", "testpassword")));
|
||||
"testuser"));
|
||||
|
||||
assertEquals(HttpStatus.BAD_REQUEST, exception.getStatusCode());
|
||||
assertTrue(exception.getReason().contains("credentialId"));
|
||||
|
|
|
|||
Loading…
Reference in New Issue