Add Luna.Core server with SignalR hub, session management, and tool system

Implement the core application server as an ASP.NET web host with:
- SignalR ChatHub for real-time client communication
- Session management with token estimation for context windowing
- Extensible tool system with attribute-based tool discovery
- Chat stream update builder for streaming AI responses
- App configuration with appsettings for development and production
This commit is contained in:
2026-04-04 04:10:58 +02:00
parent 29eb3851c9
commit 2f5841a1b9
23 changed files with 586 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
namespace Luna.Core.Tools.BuiltIn;
public class CoreTools : IToolbox
{
public string Name => "core";
public record Channel(string Name, string Health, bool IsConnected);
public record ListChannelsResult(IEnumerable<Channel> Channels);
[Tool("list_channels", "List available communication channels")]
public ListChannelsResult ListChannels()
{
return new ListChannelsResult([
new Channel("cli", "healthy", true),
new Channel("web", "healthy", false),
new Channel("telegram", "healthy", false)
]);
}
[Tool("change_channel", "Change the active communication channel")]
public void ChangeChannel(string channelName)
{
}
}
@@ -0,0 +1,19 @@
namespace Luna.Core.Tools.Extensions;
public static class ServiceCollectionExtensions
{
extension(IServiceCollection services)
{
public IServiceCollection AddTools()
{
services.AddSingleton<IToolsProvider, ToolsProvider>();
typeof(ToolsProvider).Assembly.GetTypes()
.Where(t => typeof(IToolbox).IsAssignableFrom(t) && !t.IsInterface && !t.IsAbstract)
.ToList()
.ForEach(t => services.AddTransient(typeof(IToolbox), t));
return services;
}
}
}
+30
View File
@@ -0,0 +1,30 @@
using System.Reflection;
using Microsoft.Extensions.AI;
namespace Luna.Core.Tools;
public interface IToolbox
{
public string Name { get; }
public sealed IEnumerable<AITool> GetTools()
{
var type = GetType();
List<(ToolAttribute Attribute, MethodInfo Method)> tools = [];
foreach (var method in type.GetMethods(BindingFlags.Public | BindingFlags.Instance))
{
var attribute = method.GetCustomAttribute<ToolAttribute>();
if (attribute is null) continue;
tools.Add((attribute, method));
}
return tools.Select(tool => AIFunctionFactory.Create(
tool.Method,
this,
$"{this.Name}/{tool.Attribute.Name}",
tool.Attribute.Description
));
}
}
+8
View File
@@ -0,0 +1,8 @@
using Microsoft.Extensions.AI;
namespace Luna.Core.Tools;
public interface IToolsProvider
{
IEnumerable<AITool> GetTools();
}
+8
View File
@@ -0,0 +1,8 @@
namespace Luna.Core.Tools;
[AttributeUsage(AttributeTargets.Method)]
public class ToolAttribute(string name, string description) : Attribute
{
public string Name { get; } = name;
public string Description { get; } = description;
}
+9
View File
@@ -0,0 +1,9 @@
using Microsoft.Extensions.AI;
namespace Luna.Core.Tools;
public sealed class ToolsProvider(IEnumerable<IToolbox> toolboxes) : IToolsProvider
{
public IEnumerable<AITool> GetTools()
=> toolboxes.SelectMany(t => t.GetTools());
}