Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/Core.Application/Abstractions/IAuthorResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Goodtocode.SemanticKernel.Core.Application.Abstractions;

public interface IAuthorResponse
{
Guid AuthorId { get; }
string? Name { get; }
string Status { get; }
string? Message { get; }
}
4 changes: 2 additions & 2 deletions src/Core.Application/Abstractions/IAuthorsPlugin.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins;
namespace Goodtocode.SemanticKernel.Core.Application.Abstractions;

public interface IAuthorsPlugin
{
Task<string> GetAuthorInfoAsync(string authorId, CancellationToken cancellationToken);
Task<IAuthorResponse> GetAuthorNameAsync(Guid authorId, CancellationToken cancellationToken);
}
4 changes: 2 additions & 2 deletions src/Core.Application/Abstractions/IChatMessagesPlugin.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins;
namespace Goodtocode.SemanticKernel.Core.Application.Abstractions;

public interface IChatMessagesPlugin
{
Task<IEnumerable<string>> ListRecentMessagesAsync(DateTime? startDate, DateTime? endDate, CancellationToken cancellationToken);
Task<IEnumerable<string>> GetChatMessagesAsync(string sessionId, CancellationToken cancellationToken);
Task<IEnumerable<string>> GetChatMessagesAsync(Guid sessionId, CancellationToken cancellationToken);
}
4 changes: 2 additions & 2 deletions src/Core.Application/Abstractions/IChatSessionsPlugin.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins;
namespace Goodtocode.SemanticKernel.Core.Application.Abstractions;

public interface IChatSessionsPlugin
{
Task<IEnumerable<string>> ListRecentSessionsAsync(DateTime? startDate, DateTime? endDate, CancellationToken cancellationToken);
Task<string> UpdateChatSessionTitleAsync(string sessionId, string newTitle, CancellationToken cancellationToken);
Task<string> UpdateChatSessionTitleAsync(Guid sessionId, string newTitle, CancellationToken cancellationToken);
}
3 changes: 2 additions & 1 deletion src/Infrastructure.SemanticKernel/ConfigureServices.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Options;
using Goodtocode.SemanticKernel.Core.Application.Abstractions;
using Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Options;
using Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins;
using Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Services;
using Microsoft.Extensions.Configuration;
Expand Down
34 changes: 31 additions & 3 deletions src/Infrastructure.SemanticKernel/Plugins/AuthorsPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,45 @@

namespace Goodtocode.SemanticKernel.Infrastructure.SemanticKernel.Plugins;

public class AuthorResponse : IAuthorResponse
{
public Guid AuthorId { get; set; }
public string? Name { get; set; }
public string? Status { get; set; }

Check warning on line 12 in src/Infrastructure.SemanticKernel/Plugins/AuthorsPlugin.cs

View workflow job for this annotation

GitHub Actions / CI Build, Test, Code QL, Publish (9.x)

Nullability of reference types in return type of 'string? AuthorResponse.Status.get' doesn't match implicitly implemented member 'string IAuthorResponse.Status.get' (possibly because of nullability attributes).
public string? Message { get; set; }
}

public sealed class AuthorsPlugin(IServiceProvider serviceProvider) : IAuthorsPlugin
{
private readonly IServiceProvider _serviceProvider = serviceProvider;

[KernelFunction("get_author")]
[Description("Returns the author's name for the specified author ID, or 'Author not found' if no match exists.")]
public async Task<string> GetAuthorInfoAsync(string authorId, CancellationToken cancellationToken)
[Description("Returns structured author info including name, status, and explanation.")]
async Task<IAuthorResponse> IAuthorsPlugin.GetAuthorNameAsync(Guid authorId, CancellationToken cancellationToken)
{
using var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ISemanticKernelContext>();
var author = await context.Authors.FindAsync([authorId, cancellationToken], cancellationToken: cancellationToken);
return author?.Name ?? "Author not found";

if (author == null)
{
return new AuthorResponse
{
AuthorId = authorId,
Name = null,
Status = "NotFound",
Message = "No author found with the specified ID."
};
}

return new AuthorResponse
{
AuthorId = authorId,
Name = author.Name,
Status = string.IsNullOrWhiteSpace(author.Name) ? "Partial" : "Found",
Message = string.IsNullOrWhiteSpace(author.Name)
? "Author exists but name is not yet linked to Entra External ID."
: "Author found."
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ public async Task<IEnumerable<string>> ListRecentMessagesAsync(DateTime? startDa

[KernelFunction("get_messages")]
[Description("Retrieves all messages from a specific chat session.")]
public async Task<IEnumerable<string>> GetChatMessagesAsync(string sessionId,
public async Task<IEnumerable<string>> GetChatMessagesAsync(Guid sessionId,
CancellationToken cancellationToken = default)
{
// Get ISemanticKernelContext directly instead of constructor DI to allow this plugin to be registered via AddSingleton() and not scoped due to EF.
using var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ISemanticKernelContext>();

var messages = await context.ChatMessages
.Where(x => x.ChatSessionId.ToString() == sessionId)
.Where(x => x.ChatSessionId == sessionId)
.ToListAsync(cancellationToken);

return messages.Select(m => $"{m.ChatSessionId}: {m.Timestamp} - {m.Role}: {m.Content}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,15 @@ public async Task<IEnumerable<string>> ListRecentSessionsAsync(

[KernelFunction("change_title")]
[Description("Changes the title on this chat session.")]
public async Task<string> UpdateChatSessionTitleAsync(string sessionId, string newTitle,
public async Task<string> UpdateChatSessionTitleAsync(Guid sessionId, string newTitle,
CancellationToken cancellationToken = default)
{
// Get ISemanticKernelContext directly instead of constructor DI to allow this plugin to be registered via AddSingleton() and not scoped due to EF.
using var scope = _serviceProvider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<ISemanticKernelContext>();

var chatSession = await context.ChatSessions
.FirstOrDefaultAsync(x => x.Id.ToString() == sessionId, cancellationToken: cancellationToken);
.FirstOrDefaultAsync(x => x.Id == sessionId, cancellationToken: cancellationToken);
chatSession!.Title = newTitle;
context.ChatSessions.Update(chatSession);
await context.SaveChangesAsync(cancellationToken);
Expand Down
Loading