Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
using MaIN.Core.Hub;
using MaIN.Domain.Configuration;
using MaIN.Domain.Entities;
using MaIN.Domain.Configuration.BackendInferenceParams;
using MaIN.Domain.Entities;
using MaIN.Domain.Exceptions;
using MaIN.Domain.Models;
using MaIN.Domain.Models.Concrete;

namespace MaIN.Core.IntegrationTests;
namespace MaIN.Core.E2ETests;

[Collection("E2ETests")]
public class BackendParamsTests : IntegrationTestBase
{
private const string TestQuestion = "What is 2+2? Answer with just the number.";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
using FuzzySharp;
using MaIN.Core.E2ETests.Helpers;
using MaIN.Core.Hub;
using MaIN.Core.IntegrationTests.Helpers;
using MaIN.Domain.Entities;
using MaIN.Domain.Models;
using MaIN.Domain.Models.Abstract;

namespace MaIN.Core.IntegrationTests;
namespace MaIN.Core.E2ETests;

[Collection("E2ETests")]
public class ChatTests : IntegrationTestBase
{
public ChatTests() : base()
Expand All @@ -16,7 +17,7 @@ public ChatTests() : base()
[Fact]
public async Task Should_AnswerQuestion_BasicChat()
{
var context = AIHub.Chat().WithModel(Models.Local.Gemma2_2b);
var context = AIHub.Chat().WithModel(Models.Local.Qwen2_5_0_5b);

var result = await context
.WithMessage("Where the hedgehog goes at night?")
Expand All @@ -28,28 +29,38 @@ public async Task Should_AnswerQuestion_BasicChat()
}

[Fact]
public async Task Should_AnswerDifferences_BetweenDocuments_ChatWithFiles()
public async Task Should_AnswerFileSubject_ChatWithFiles()
{
List<string> files = ["./Files/Nicolaus_Copernicus.pdf", "./Files/Galileo_Galilei.pdf"];
List<string> files = ["./Files/Nicolaus_Copernicus.pdf"];

var result = await AIHub.Chat()
.WithModel(Models.Local.Gemma2_2b)
.WithMessage("You have 2 documents in memory. Whats the difference of work between Galileo and Copernicus?. Give answer based on the documents.")
.WithModel(Models.Local.Qwen2_5_0_5b)
.WithMessage("Who is described in the file? Reply with ONLY their full name. No explanation, no punctuation. Example: Isaak Newton")
.WithMemoryParams(new MemoryParams { AnswerTokens = 10 })
.WithFiles(files)
.CompleteAsync();

Assert.True(result.Done);
Assert.NotNull(result.Message);
Assert.NotEmpty(result.Message.Content);
var ratio = Fuzz.PartialRatio("nicolaus copernicus", result.Message.Content.ToLowerInvariant());
Assert.True(ratio > 50,
$"""
Fuzzy match failed!
Expected > 50, but got {ratio}.
Expected: 'nicolaus copernicus'
Actual: '{result.Message.Content}'
""");
}

[Fact]
public async Task Should_AnswerQuestion_FromExistingChat()
{
var result = AIHub.Chat()
.WithModel(Models.Local.Gemma2_2b);
.WithModel(Models.Local.Qwen2_5_0_5b);

await result.WithMessage("What do you think about math theories?")
.WithMemoryParams(new MemoryParams { AnswerTokens = 10 })
.CompleteAsync();

await result.WithMessage("And about physics?")
Expand All @@ -62,29 +73,53 @@ await result.WithMessage("And about physics?")
}

[Fact]
public async Task Should_AnswerGameFromImage_ChatWithVision()
public async Task Should_AnswerGameFromImage_ChatWithImagesWithText()
{
List<string> images = ["./Files/gamex.jpg"];
var expectedAnswer = "call of duty";

var result = await AIHub.Chat()
.WithModel(Models.Local.Llama3_2_3b)
.WithMessage("What is the title of the game? Answer only this question.")
.WithMemoryParams(new MemoryParams
{
AnswerTokens = 1000
})
.WithMessage("What is the title of the game? Answer in 3 words.")
.WithMemoryParams(new MemoryParams { AnswerTokens = 10 })
.WithFiles(images)
.CompleteAsync();

Assert.True(result.Done);
Assert.NotNull(result.Message);
Assert.NotEmpty(result.Message.Content);
var ratio = Fuzz.PartialRatio(expectedAnswer, result.Message.Content.ToLowerInvariant());
Assert.True(ratio > 50,
$"""
Fuzzy match failed!
Expected > 50, but got {ratio}.
Expexted: '{expectedAnswer}'
Actual: '{result.Message.Content}'
""");
}

[Fact]
public async Task Should_AnswerAppleFromImage_ChatWithImagesWithVision()
{
List<string> images = ["./Files/apple.jpg"];
var expectedAnswer = "apple";

var result = await AIHub.Chat()
.WithModel(Models.Local.Gemma3_4b)
.WithMessage("What is this fruit? Answer in one word.")
.WithMemoryParams(new MemoryParams { AnswerTokens = 10 })
.WithFiles(images)
.CompleteAsync();

Assert.True(result.Done);
Assert.NotNull(result.Message);
Assert.NotEmpty(result.Message.Content);
var ratio = Fuzz.PartialRatio("call of duty", result.Message.Content.ToLowerInvariant());
var ratio = Fuzz.PartialRatio(expectedAnswer, result.Message.Content.ToLowerInvariant());
Assert.True(ratio > 50,
$"""
Fuzzy match failed!
Expected > 50, but got {ratio}.
Expexted: 'call of duty'
Expexted: '{expectedAnswer}'
Actual: '{result.Message.Content}'
""");
}
Expand Down Expand Up @@ -113,9 +148,9 @@ public async Task Should_GenerateImage_BasedOnPrompt()
}

[Fact]
public async Task Should_AnswerDifferences_BetweenDocuments_ChatWithFiles_UsingStreams()
public async Task Should_AnswerFileSubject_ChatWithFiles_UsingStreams()
{
List<string> files = ["./Files/Nicolaus_Copernicus.pdf", "./Files/Galileo_Galilei.pdf"];
List<string> files = ["./Files/Nicolaus_Copernicus.pdf"];

var fileStreams = new List<FileStream>();

Expand All @@ -135,14 +170,25 @@ public async Task Should_AnswerDifferences_BetweenDocuments_ChatWithFiles_UsingS
fileStreams.Add(fs);
}

var expectedAnswer = "nicolaus copernicus";

var result = await AIHub.Chat()
.WithModel(Models.Local.Gemma2_2b)
.WithMessage("You have 2 documents in memory. Whats the difference of work between Galileo and Copernicus?. Give answer based on the documents.")
.WithModel(Models.Local.Qwen2_5_0_5b)
.WithMessage("Who is described in the file? Reply with ONLY their full name. No explanation, no punctuation. Example: Isaak Newton")
.WithMemoryParams(new MemoryParams { AnswerTokens = 10 })
.WithFiles(fileStreams)
.CompleteAsync();

Assert.True(result.Done);
Assert.NotNull(result.Message);
Assert.NotEmpty(result.Message.Content);
var ratio = Fuzz.PartialRatio(expectedAnswer, result.Message.Content.ToLowerInvariant());
Assert.True(ratio > 50,
$"""
Fuzzy match failed!
Expected > 50, but got {ratio}.
Expected: '{expectedAnswer}'
Actual: '{result.Message.Content}'
""");
}
}
Binary file added MaIN.Core.E2ETests/Files/apple.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Net.Sockets;

namespace MaIN.Core.IntegrationTests.Helpers;
namespace MaIN.Core.E2ETests.Helpers;

public static class NetworkHelper
{
Expand Down
40 changes: 40 additions & 0 deletions MaIN.Core.E2ETests/IntegrationTestBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace MaIN.Core.E2ETests;

public class IntegrationTestBase : IDisposable
{
protected readonly IHost _host;
protected readonly IServiceProvider _services;

protected IntegrationTestBase()
{
_host = Host.CreateDefaultBuilder()
.ConfigureServices((context, services) =>
{
services.AddMaIN(context.Configuration);
ConfigureServices(services);
})
.Build();

_host.Services.UseMaIN();
_host.Start();

_services = _host.Services;
}

// Allow derived classes to add additional services or override existing ones
protected virtual void ConfigureServices(IServiceCollection services)
{
}

protected T GetService<T>() where T : notnull => _services.GetRequiredService<T>();

public void Dispose()
{
_host.Dispose();
GC.SuppressFinalize(this);
}
}
47 changes: 47 additions & 0 deletions MaIN.Core.E2ETests/MaIN.Core.E2ETests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net8.0;net10.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="FuzzySharp" Version="2.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Xunit.SkippableFact" Version="1.5.61" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\MaIN.Core\MaIN.Core.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="Files\apple.jpg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Files\Books.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Files\Galileo_Galilei.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Files\gamex.jpg">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="Files\Nicolaus_Copernicus.pdf">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
Loading
Loading