@@ -271,37 +271,10 @@ protected async Task AutoGroupNotes()
271271
272272 ChatOptions chatOptions = new ( )
273273 {
274- Tools = [
275- AIFunctionFactory . Create (
276- this . MakeNoteGroup ,
277- new AIFunctionFactoryCreateOptions
278- {
279- Name = "Create group" ,
280- Parameters = [
281- new ( "title" ) { Description = "The name of the group to create" , IsRequired = true , ParameterType = typeof ( string ) } ,
282- ]
283- }
284- ) ,
285- AIFunctionFactory . Create (
286- this . AddNoteToGroup ,
287- new AIFunctionFactoryCreateOptions
288- {
289- Name = "Assign note to group" ,
290- Parameters = [
291- new ( "noteGroup" ) { Description = "Title of the group created with the \" Create note group\" tool" , IsRequired = true , ParameterType = typeof ( int ) } ,
292- new ( "noteId" ) { Description = "Integer ID of the note to move to the group" , IsRequired = true , ParameterType = typeof ( int ) } ,
293- ] ,
294- ReturnParameter = new ( ) {
295- Description = "Returns if the move was successful" ,
296- ParameterType = typeof ( string )
297- }
298- }
299- ) ,
300- ] ,
301- ToolMode = ChatToolMode . RequireAny ,
302274 TopP = 0.9f ,
303275 Temperature = 0.2f ,
304- TopK = 10
276+ TopK = 10 ,
277+ ResponseFormat = ChatResponseFormat . Json
305278 } ;
306279
307280 List < ChatMessage > chatMessages =
@@ -315,11 +288,7 @@ protected async Task AutoGroupNotes()
315288- Do not include the same note in more than one group.
316289- Assign each group a concise title (maximum of 5 words) summarizing its content.
317290
318- Workflow:
319- 1. Create group with a title
320- 2. For each note belonging to the group: invoke the ""add note to group"" tool
321-
322- What now follows is a list of notes to divide into groups. Do not response with a summary, invoke the tool.
291+ What now follows is a list of notes to divide into groups.
323292Each note is starts with [NOTE ID]. Each note ends with [END NOTE].
324293
325294Example note with ID 123:
@@ -339,17 +308,45 @@ [NOTE 123] Some text here [END NOTE]"
339308 long startTime = Stopwatch . GetTimestamp ( ) ;
340309
341310 IChatClient client = new ChatClientBuilder ( )
342- . UseFunctionInvocation ( f =>
343- {
344- f . RetryOnError = true ;
345- } )
346311 . UseLogging ( this . Logger )
347312 . Use ( this . ChatClient ) ;
348313
349314 Logger . LogDebug ( "Invoking AI with {Count} messages" , chatMessages . Count ) ;
350315 try
351316 {
352- ChatCompletion response = await client . CompleteAsync ( chatMessages , chatOptions ) ;
317+ ChatCompletion < AIGroupedNote [ ] > response = await client . CompleteAsync < AIGroupedNote [ ] > ( chatMessages , chatOptions ) ;
318+
319+ if ( response . TryGetResult ( out AIGroupedNote [ ] result ) )
320+ {
321+ foreach ( AIGroupedNote groupedNote in result )
322+ {
323+ RetrospectiveNoteGroup createdGroup = await this . Mediator . Send ( new AddNoteGroupCommand ( this . RetroId . StringId , this . Lane . Id ) ) ;
324+
325+ this . Contents . Groups . Add ( createdGroup ) ;
326+ await this . Mediator . Send ( new UpdateNoteGroupCommand ( this . RetroId . StringId , createdGroup . Id , groupedNote . GroupTitle ) ) ;
327+
328+ createdGroup = this . Contents . Groups . FirstOrDefault ( x => x . Id == createdGroup . Id ) ?? createdGroup ;
329+ createdGroup . Title = groupedNote . GroupTitle ;
330+
331+ foreach ( int noteId in groupedNote . NoteIds )
332+ {
333+ if ( this . ExecuteNoteMove ( noteId , createdGroup . Id ) )
334+ {
335+ await this . Mediator . Send ( new MoveNoteCommand ( noteId , createdGroup . Id ) ) ;
336+ }
337+ else
338+ {
339+ Logger . LogWarning ( "Invalid attempt to move note {NoteId} to group {Title}" , noteId , createdGroup . Title ) ;
340+ }
341+ }
342+ }
343+
344+ Logger . LogInformation ( "Resulting grouped notes: {@Results}" , [ result ] ) ;
345+ }
346+ else
347+ {
348+ Logger . LogWarning ( "Cannot decode response: {@Response}" , response ) ;
349+ }
353350
354351 Logger . LogTrace ( "AI response: {@RawResponse}" , response ) ;
355352 }
@@ -365,58 +362,7 @@ [NOTE 123] Some text here [END NOTE]"
365362 Logger . LogDebug ( "Completed AI invocation in {Elapsed}" , Stopwatch . GetElapsedTime ( startTime ) ) ;
366363 }
367364
368-
369- private async Task MakeNoteGroup ( string title )
370- {
371- try
372- {
373- Logger . LogInformation ( "AI tool callback: Make note group with title {Title}" , title ) ;
374-
375- RetrospectiveNoteGroup result = await this . Mediator . Send ( new AddNoteGroupCommand ( this . RetroId . StringId , this . Lane . Id ) ) ;
376-
377- this . Contents . Groups . Add ( result ) ;
378- await this . Mediator . Send ( new UpdateNoteGroupCommand ( this . RetroId . StringId , result . Id , title ) ) ;
379-
380- result = this . Contents . Groups . FirstOrDefault ( x => x . Id == result . Id ) ?? result ;
381- result . Title = title ;
382- }
383- catch ( Exception ex )
384- {
385- Logger . LogError ( ex , "Error processing AI invocation" ) ;
386- throw ;
387- }
388- }
389-
390- private async Task < string > AddNoteToGroup ( string noteGroup , int noteId )
391- {
392- try
393- {
394- Logger . LogInformation ( "AI tool callback: Move note {NoteId} to group {GroupTitle}" ,
395- noteId ,
396- noteGroup ) ;
397-
398- RetrospectiveNoteGroup noteGroupInstance = this . Contents . Groups . FirstOrDefault ( x => String . Equals ( noteGroup . Trim ( ) , x . Title . Trim ( ) , StringComparison . OrdinalIgnoreCase ) ) ;
399- if ( noteGroupInstance is null ) return $ "Note group \" { noteGroup } \" does not exist";
400-
401- IEnumerable < string > notes = this . Contents . Notes . Where ( x => x . Id == noteId ) . Select ( x => x . Text ) ;
402- foreach ( string note in notes ) {
403- Logger . LogDebug ( "Note selected for group {Title}: {NoteText}" , noteGroupInstance . Title , note ) ;
404- }
405-
406- if ( this . ExecuteNoteMove ( noteId , noteGroupInstance . Id ) )
407- {
408- await this . Mediator . Send ( new MoveNoteCommand ( noteId , noteGroupInstance . Id ) ) ;
409- return "Success" ;
410- }
411- }
412- catch ( Exception ex )
413- {
414- Logger . LogError ( ex , "Error processing AI invocation" ) ;
415- throw ;
416- }
417-
418- return "Invalid move" ;
419- }
365+ private sealed record AIGroupedNote ( string GroupTitle , int [ ] NoteIds ) ;
420366
421367 public Task OnNoteAdded ( NoteAddedNotification notification ) {
422368 if ( notification . LaneId != this . Lane ? . Id ||
0 commit comments