|
28 | 28 | import com.dbaagent.service.brain.core.BrainNoteService; |
29 | 29 | import com.dbaagent.service.brain.core.BrainTaskService; |
30 | 30 | import com.dbaagent.service.brain.core.NoteSuggestionService; |
| 31 | +import com.dbaagent.service.brain.core.BrainNoteProposalService; |
| 32 | +import com.dbaagent.dto.BrainNoteProposalRequest; |
| 33 | +import com.dbaagent.dto.BrainNoteProposalResponse; |
31 | 34 | import com.dbaagent.service.QueryExecutorService; |
32 | 35 | import com.dbaagent.service.SchemaSnapshotService; |
33 | 36 | import com.dbaagent.service.brain.analysis.ColumnDisambiguationService; |
@@ -94,6 +97,7 @@ public class BrainController { |
94 | 97 | private final BrainNoteService brainNoteService; |
95 | 98 | private final BrainTaskService brainTaskService; |
96 | 99 | private final NoteSuggestionService noteSuggestionService; |
| 100 | + private final BrainNoteProposalService brainNoteProposalService; |
97 | 101 | private final ColumnProfilingService columnProfilingService; |
98 | 102 | private final ColumnDisambiguationService columnDisambiguationService; |
99 | 103 | private final SchemaSnapshotService schemaSnapshotService; |
@@ -240,6 +244,51 @@ public ResponseEntity<NoteSuggestionDTO.Response> getNoteSuggestions( |
240 | 244 | } |
241 | 245 | } |
242 | 246 |
|
| 247 | + /** |
| 248 | + * Draft a shared-brain note from an Agent turn. Overlaps with existing notes |
| 249 | + * or business rules are merged into one intent. Read-only — does not persist. |
| 250 | + */ |
| 251 | + @PostMapping("/notes/propose") |
| 252 | + public ResponseEntity<BrainNoteProposalResponse> proposeNoteFromTurn( |
| 253 | + @org.springframework.web.bind.annotation.RequestBody BrainNoteProposalRequest request |
| 254 | + ) { |
| 255 | + try { |
| 256 | + if (request == null || request.getConnectionId() == null || request.getConnectionId().isBlank()) { |
| 257 | + return ResponseEntity.badRequest().build(); |
| 258 | + } |
| 259 | + accessControlService.assertCanReadConnectionContent(request.getConnectionId()); |
| 260 | + return brainNoteProposalService.proposeFromTurn(request) |
| 261 | + .map(ResponseEntity::ok) |
| 262 | + .orElseGet(() -> ResponseEntity.noContent().build()); |
| 263 | + } catch (ResponseStatusException e) { |
| 264 | + throw e; |
| 265 | + } catch (Exception e) { |
| 266 | + log.error("Error proposing a brain note from an agent turn", e); |
| 267 | + return ResponseEntity.internalServerError().build(); |
| 268 | + } |
| 269 | + } |
| 270 | + |
| 271 | + /** |
| 272 | + * Accept a proposed note. If it overlaps existing documentation, update that |
| 273 | + * row instead of creating a second copy of the same intent. |
| 274 | + */ |
| 275 | + @PostMapping("/notes/accept") |
| 276 | + public ResponseEntity<BrainNoteResponse> acceptNote( |
| 277 | + @org.springframework.web.bind.annotation.RequestBody BrainNoteRequest request |
| 278 | + ) { |
| 279 | + try { |
| 280 | + accessControlService.assertCanManageConnectionContent(request.getConnectionId()); |
| 281 | + return ResponseEntity.ok(brainNoteProposalService.accept(request)); |
| 282 | + } catch (IllegalArgumentException e) { |
| 283 | + return ResponseEntity.badRequest().build(); |
| 284 | + } catch (ResponseStatusException e) { |
| 285 | + throw e; |
| 286 | + } catch (Exception e) { |
| 287 | + log.error("Error accepting a brain note proposal", e); |
| 288 | + return ResponseEntity.internalServerError().build(); |
| 289 | + } |
| 290 | + } |
| 291 | + |
243 | 292 | @GetMapping("/notes/{connectionId}") |
244 | 293 | public ResponseEntity<List<BrainNoteResponse>> getNotes( |
245 | 294 | @PathVariable String connectionId, |
|
0 commit comments