@@ -10,7 +10,11 @@ namespace Return.Web.Components;
1010#nullable disable
1111
1212using System ;
13+ using System . Collections . Generic ;
14+ using System . Diagnostics ;
1315using System . Diagnostics . CodeAnalysis ;
16+ using System . Linq ;
17+ using System . Text ;
1418using System . Threading . Tasks ;
1519using Application . Common . Models ;
1620using Application . NoteGroups . Commands ;
@@ -258,6 +262,133 @@ protected async Task AddNoteGroup() {
258262 }
259263 }
260264
265+ protected bool IsAutoGrouping { get ; set ; } = false ;
266+
267+ protected async Task AutoGroupNotes ( )
268+ {
269+ IsAutoGrouping = true ;
270+ this . StateHasChanged ( ) ;
271+
272+ ChatOptions chatOptions = new ( )
273+ {
274+ Tools = [
275+ AIFunctionFactory . Create (
276+ this . MakeNoteGroup ,
277+ new AIFunctionFactoryCreateOptions
278+ {
279+ Name = "Create note group" ,
280+ Description = "Makes a group with a specified title and IDs of the relevant notes." ,
281+ Parameters = [
282+ new ( "title" ) { Description = "The name of the group to create" , IsRequired = true , ParameterType = typeof ( string ) } ,
283+ new ( "noteIds" ) { Description = "An array of note IDs of the notes to put into this group" , IsRequired = true , ParameterType = typeof ( int [ ] ) } ,
284+ ] ,
285+ ReturnParameter = new ( )
286+ {
287+ Description = "Indication of the note group created" ,
288+ }
289+ }
290+ )
291+ ] ,
292+ ToolMode = ChatToolMode . RequireSpecific ( "Create note group" ) ,
293+ TopP = 1.2f ,
294+ TopK = 25
295+ } ;
296+
297+ List < ChatMessage > chatMessages =
298+ [
299+ new (
300+ ChatRole . System ,
301+ $@ "Please group similar notes together using the following constraints:
302+ 1. Only group notes that fit in a group.
303+ 2. If a note cannot be grouped together with multiple other notes, then ignore.
304+ 3. Only group notes with the same subject.
305+ 4. To group notes, invoke the ""Create note group"" tool.
306+ 5. Do not to put a single note in multiple groups.
307+ 6. Give each group a title of 5 words maximum that summarizes the notes in the group.
308+
309+ What now follows is a list of notes to divide into groups. Do not response with a summary, please invoke the tool.
310+ Each note is starts with [NOTE ID]. Each note ends with [END NOTE].
311+
312+ Example note with ID 123:
313+ [NOTE 123] Some text here [END NOTE]"
314+ )
315+ ] ;
316+
317+ StringBuilder stringBuilder = new ( ) ;
318+
319+ foreach ( RetrospectiveNote note in this . Contents . Notes )
320+ {
321+ stringBuilder . AppendLine ( $ "[NOTE { note . Id } ] { note . Text } [END NOTE]") ;
322+ }
323+
324+ chatMessages . Add ( new ChatMessage ( ChatRole . User , stringBuilder . ToString ( ) ) ) ;
325+
326+ long startTime = Stopwatch . GetTimestamp ( ) ;
327+
328+ IChatClient client = new ChatClientBuilder ( )
329+ . UseFunctionInvocation ( f =>
330+ {
331+ f . RetryOnError = true ;
332+ } )
333+ . UseLogging ( this . Logger )
334+ . Use ( this . ChatClient ) ;
335+
336+ Logger . LogDebug ( "Invoking AI with {Count} messages" , chatMessages . Count ) ;
337+ try
338+ {
339+ ChatCompletion response = await client . CompleteAsync ( chatMessages , chatOptions ) ;
340+
341+ Logger . LogTrace ( "AI response: {@RawResponse}" , response ) ;
342+ }
343+ catch ( Exception ex )
344+ {
345+ Logger . LogError ( ex , "Error invoking AI" ) ;
346+ }
347+ finally
348+ {
349+ IsAutoGrouping = false ;
350+ }
351+
352+ Logger . LogDebug ( "Completed AI invocation in {Elapsed}" , Stopwatch . GetElapsedTime ( startTime ) ) ;
353+ }
354+
355+
356+ private async Task < string > MakeNoteGroup ( string title , int [ ] noteIds )
357+ {
358+ try
359+ {
360+ Logger . LogInformation ( "AI tool callback: Make note group with title {Title} and ids {@NoteIds}" ,
361+ title ,
362+ noteIds ) ;
363+
364+ IEnumerable < string > notes = this . Contents . Notes . Where ( x => noteIds . Contains ( x . Id ) ) . Select ( x => x . Text ) ;
365+ foreach ( string note in notes ) {
366+ Logger . LogDebug ( "Note selected for group {Title}: {NoteText}" , title , note ) ;
367+ }
368+
369+ RetrospectiveNoteGroup result = await this . Mediator . Send ( new AddNoteGroupCommand ( this . RetroId . StringId , this . Lane . Id ) ) ;
370+ result . Title = title ;
371+
372+ this . Contents . Groups . Add ( result ) ;
373+ await this . Mediator . Send ( new UpdateNoteGroupCommand ( this . RetroId . StringId , result . Id , title ) ) ;
374+
375+ foreach ( int noteId in noteIds )
376+ {
377+ if ( this . ExecuteNoteMove ( noteId , result . Id ) )
378+ {
379+ await this . Mediator . Send ( new MoveNoteCommand ( noteId , result . Id ) ) ;
380+ }
381+ }
382+
383+ return $ "Created note group \" { title } \" and with notes: { String . Join ( "," , noteIds ) } ";
384+ }
385+ catch ( Exception ex )
386+ {
387+ Logger . LogError ( ex , "Error processing AI invocation" ) ;
388+ return $ "An error occured making group { title } ";
389+ }
390+ }
391+
261392 public Task OnNoteAdded ( NoteAddedNotification notification ) {
262393 if ( notification . LaneId != this . Lane ? . Id ||
263394 notification . RetroId != this . RetroId . StringId ||
0 commit comments