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
18 changes: 18 additions & 0 deletions Examples/Examples/Chat/ChatExampleOllama.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Examples.Utils;
using MaIN.Core.Hub;

namespace Examples.Chat;

public class ChatExampleOllama : IExample
{
public async Task Start()
{
OllamaExample.Setup(); //We need to provide Ollama API key
Console.WriteLine("(Ollama) ChatExample is running!");

await AIHub.Chat()
.WithModel("qwen3-next:80b")
.WithMessage("Write a short poem about the color green.")
.CompleteAsync(interactive: true);
}
}
2 changes: 2 additions & 0 deletions Examples/Examples/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ static void RegisterExamples(IServiceCollection services)
services.AddTransient<ChatExampleGroqCloud>();
services.AddTransient<ChatExampleAnthropic>();
services.AddTransient<ChatExampleXai>();
services.AddTransient<ChatExampleOllama>();
}

async Task RunSelectedExample(IServiceProvider serviceProvider)
Expand Down Expand Up @@ -174,6 +175,7 @@ public class ExampleRegistry(IServiceProvider serviceProvider)
("\u25a0 GroqCloud Chat", serviceProvider.GetRequiredService<ChatExampleGroqCloud>()),
("\u25a0 Anthropic Chat", serviceProvider.GetRequiredService<ChatExampleAnthropic>()),
("\u25a0 xAI Chat", serviceProvider.GetRequiredService<ChatExampleXai>()),
("\u25a0 Ollama Chat", serviceProvider.GetRequiredService<ChatExampleOllama>()),
("\u25a0 McpClient example", serviceProvider.GetRequiredService<McpExample>()),
("\u25a0 McpAgent example", serviceProvider.GetRequiredService<McpAgentsExample>()),
("\u25a0 Chat with TTS example", serviceProvider.GetRequiredService<ChatWithTextToSpeechExample>()),
Expand Down
16 changes: 16 additions & 0 deletions Examples/Examples/Utils/OllamaExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using MaIN.Core;
using MaIN.Domain.Configuration;

namespace Examples.Utils;

public class OllamaExample
{
public static void Setup()
{
MaINBootstrapper.Initialize(configureSettings: (options) =>
{
options.BackendType = BackendType.Ollama;
options.OllamaKey = "<YOUR_OLLAMA_KEY>";
});
}
}
4 changes: 1 addition & 3 deletions MaIN.Core.IntegrationTests/MaIN.Core.IntegrationTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.22" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<PrivateAssets>all</PrivateAssets>
Expand All @@ -19,7 +17,7 @@
</ItemGroup>

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

<ItemGroup>
Expand Down
3 changes: 3 additions & 0 deletions Releases/0.8.1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# 0.8.1 release

- Add Ollama integration
2 changes: 1 addition & 1 deletion src/MaIN.Core/.nuspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<package>
<metadata>
<id>MaIN.NET</id>
<version>0.8.0</version>
<version>0.8.1</version>
<authors>Wisedev</authors>
<owners>Wisedev</owners>
<icon>favicon.png</icon>
Expand Down
13 changes: 2 additions & 11 deletions src/MaIN.Core/MaIN.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="GTranslate" Version="2.3.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
<PackageReference Include="LLamaSharp" Version="0.25.0" />
<PackageReference Include="LLamaSharp.Backend.Cuda12" Version="0.25.0" />
<PackageReference Include="LLamaSharp.kernel-memory" Version="0.25.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.1" />
<PackageReference Include="Microsoft.KernelMemory" Version="0.98.250508.3" />
<PackageReference Include="Microsoft.KernelMemory.SemanticKernelPlugin" Version="0.98.250508.3" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.6" />
<PackageReference Include="Tesseract" Version="5.2.0" />
<PackageReference Include="Tesseract.Data.English" Version="4.0.0" />
</ItemGroup>

Expand All @@ -33,7 +24,7 @@
</ItemGroup>

<Target Name="link_deps" AfterTargets="AfterBuild" Condition="$([MSBuild]::IsOSPlatform('OSX'))">
<Exec Command="ln -sf /opt/homebrew/lib/libleptonica.dylib $(OutDir)x64/libleptonica-1.82.0.dylib"/>
<Exec Command="ln -sf /opt/homebrew/lib/libtesseract.dylib $(OutDir)x64/libtesseract50.dylib"/>
<Exec Command="ln -sf /opt/homebrew/lib/libleptonica.dylib $(OutDir)x64/libleptonica-1.82.0.dylib" />
<Exec Command="ln -sf /opt/homebrew/lib/libtesseract.dylib $(OutDir)x64/libtesseract50.dylib" />
</Target>
</Project>
2 changes: 2 additions & 0 deletions src/MaIN.Domain/Configuration/MaINSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public class MaINSettings
public string? DeepSeekKey { get; set; }
public string? AnthropicKey { get; set; }
public string? GroqCloudKey { get; set; }
public string? OllamaKey { get; set; }
public string? XaiKey { get; set; }
public MongoDbSettings? MongoDbSettings { get; set; }
public FileSystemSettings? FileSystemSettings { get; set; }
Expand All @@ -30,4 +31,5 @@ public enum BackendType
GroqCloud = 4,
Anthropic = 5,
Xai = 6,
Ollama = 7,
}
4 changes: 0 additions & 4 deletions src/MaIN.InferPage/MaIN.InferPage.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Costura.Fody" Version="6.0.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Markdig" Version="0.44.0" />
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.13.2" />
<PackageReference Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.13.2" />
</ItemGroup>

Expand Down
26 changes: 26 additions & 0 deletions src/MaIN.InferPage/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,18 @@
apiKeyVariable = "ANTHROPIC_API_KEY";
apiName = "Anthropic";
break;

case "xai":
Utils.Xai = true;
apiKeyVariable = "XAI_API_KEY";
apiName = "Xai";
break;

case "ollama":
Utils.Ollama = true;
apiKeyVariable = "OLLAMA_API_KEY";
apiName = "Ollama";
break;
}

var key = Environment.GetEnvironmentVariable(apiKeyVariable);
Expand Down Expand Up @@ -128,6 +140,20 @@
settings.BackendType = BackendType.Anthropic;
});
}
else if (Utils.Xai)
{
builder.Services.AddMaIN(builder.Configuration, settings =>
{
settings.BackendType = BackendType.Xai;
});
}
else if (Utils.Ollama)
{
builder.Services.AddMaIN(builder.Configuration, settings =>
{
settings.BackendType = BackendType.Ollama;
});
}
else
{
if (Utils.Path == null && !KnownModels.IsModelSupported(Utils.Model!))
Expand Down
2 changes: 2 additions & 0 deletions src/MaIN.InferPage/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public static class Utils
public static bool DeepSeek { get; set; }
public static bool GroqCloud { get; set; }
public static bool Anthropic { get; set; }
public static bool Xai { get; set; }
public static bool Ollama { get; set; }
public static string? Path { get; set; }
public static bool Reason { get; set; }
}
Expand Down
2 changes: 0 additions & 2 deletions src/MaIN.Infrastructure/MaIN.Infrastructure.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
<PackageReference Include="Microsoft.Data.Sqlite" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.1" />
<PackageReference Include="Microsoft.Extensions.Options" Version="10.0.1" />
<PackageReference Include="MongoDB.Driver" Version="3.2.1" />
<PackageReference Include="MongoDB.Driver.Core" Version="2.30.0" />
</ItemGroup>
Expand Down
4 changes: 4 additions & 0 deletions src/MaIN.Services/Constants/ServiceConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public static class HttpClients
public const string GroqCloudClient = "GroqCloudClient";
public const string AnthropicClient = "AnthropicClient";
public const string XaiClient = "XaiClient";
public const string OllamaClient = "OllamaClient";
public const string ImageDownloadClient = "ImageDownloadClient";
public const string ModelContextDownloadClient = "ModelContextDownloadClient";
}
Expand Down Expand Up @@ -41,6 +42,9 @@ public static class ApiUrls
public const string XaiImageGenerations = "https://api.x.ai/v1/images/generations";
public const string XaiOpenAiChatCompletions = "https://api.x.ai/v1/chat/completions";
public const string XaiModels = "https://api.x.ai/v1/models";

public const string OllamaOpenAiChatCompletions = "https://ollama.com/v1/chat/completions";
public const string OllamaModels = "https://ollama.com/v1/models";
}

public static class Messages
Expand Down
5 changes: 0 additions & 5 deletions src/MaIN.Services/MaIN.Services.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,12 @@
<ItemGroup>
<PackageReference Include="GTranslate" Version="2.3.1" />
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
<PackageReference Include="LLamaSharp" Version="0.25.0" />
<PackageReference Include="llamasharp.backend.cpu" Version="0.25.0" />
<PackageReference Include="LLamaSharp.Backend.Cuda12" Version="0.25.0" />
<PackageReference Include="LLamaSharp.kernel-memory" Version="0.25.0" />
<PackageReference Include="LLamaSharp.semantic-kernel" Version="0.25.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="10.0.1" />
<PackageReference Include="Microsoft.KernelMemory" Version="0.98.250508.3" />
<PackageReference Include="Microsoft.KernelMemory.SemanticKernelPlugin" Version="0.98.250508.3" />
<PackageReference Include="Microsoft.SemanticKernel" Version="1.68.0" />
<PackageReference Include="Microsoft.SemanticKernel.Connectors.Google" Version="1.64.0-alpha" />
<PackageReference Include="Microsoft.SemanticKernel.Plugins.Memory" Version="1.64.0-alpha" />
<PackageReference Include="ModelContextProtocol.AspNetCore" Version="0.2.0-preview.1" />
<PackageReference Include="NAudio" Version="2.2.1" />
<PackageReference Include="NumSharp" Version="0.30.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
using System;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using MaIN.Domain.Configuration;
using MaIN.Domain.Entities;
using MaIN.Services.Constants;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class ImageGenServiceFactory(IServiceProvider serviceProvider) : IImageGe
BackendType.Anthropic => null,
BackendType.Xai => new XaiImageGenService(serviceProvider.GetRequiredService<IHttpClientFactory>(),
serviceProvider.GetRequiredService<MaINSettings>()),
BackendType.Ollama => null,
BackendType.Self => new ImageGenService(serviceProvider.GetRequiredService<IHttpClientFactory>(),
serviceProvider.GetRequiredService<MaINSettings>()),

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ public ILLMService CreateService(BackendType backendType)
serviceProvider.GetRequiredService<IMemoryFactory>(),
serviceProvider.GetRequiredService<IMemoryService>()),

BackendType.Ollama => new OllamaService(
serviceProvider.GetRequiredService<MaINSettings>(),
serviceProvider.GetRequiredService<INotificationService>(),
serviceProvider.GetRequiredService<IHttpClientFactory>(),
serviceProvider.GetRequiredService<IMemoryFactory>(),
serviceProvider.GetRequiredService<IMemoryService>()),

BackendType.Anthropic => new AnthropicService(
serviceProvider.GetRequiredService<MaINSettings>(),
serviceProvider.GetRequiredService<INotificationService>(),
Expand Down
78 changes: 78 additions & 0 deletions src/MaIN.Services/Services/LLMService/OllamaService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using System.Text;
using MaIN.Domain.Configuration;
using MaIN.Domain.Entities;
using MaIN.Services.Constants;
using MaIN.Services.Services.Abstract;
using MaIN.Services.Services.LLMService.Memory;
using MaIN.Services.Services.Models;
using Microsoft.Extensions.Logging;

namespace MaIN.Services.Services.LLMService;

public sealed class OllamaService(
MaINSettings settings,
INotificationService notificationService,
IHttpClientFactory httpClientFactory,
IMemoryFactory memoryFactory,
IMemoryService memoryService,
ILogger<OpenAiService>? logger = null)
: OpenAiCompatibleService(notificationService, httpClientFactory, memoryFactory, memoryService, logger)
{
private readonly MaINSettings _settings = settings ?? throw new ArgumentNullException(nameof(settings));

protected override string HttpClientName => ServiceConstants.HttpClients.OllamaClient;
protected override string ChatCompletionsUrl => ServiceConstants.ApiUrls.OllamaOpenAiChatCompletions;
protected override string ModelsUrl => ServiceConstants.ApiUrls.OllamaModels;

protected override string GetApiKey()
{
return _settings.OllamaKey ?? Environment.GetEnvironmentVariable("OLLAMA_API_KEY") ??
throw new InvalidOperationException("Olama Key not configured");
}

protected override void ValidateApiKey()
{
if (string.IsNullOrEmpty(_settings.OllamaKey) && string.IsNullOrEmpty(Environment.GetEnvironmentVariable("OLLAMA_API_KEY")))
{
throw new InvalidOperationException("Ollama Key not configured");
}
}

public override async Task<ChatResult?> AskMemory(
Chat chat,
ChatMemoryOptions memoryOptions,
ChatRequestOptions requestOptions,
CancellationToken cancellationToken = default)
{
var lastMsg = chat.Messages.Last();
var filePaths = await DocumentProcessor.ConvertToFilesContent(memoryOptions);
var message = new Message()
{
Role = ServiceConstants.Roles.User,
Content = ComposeMessage(lastMsg, filePaths),
Type = MessageType.CloudLLM
};

chat.Messages.Last().Content = message.Content;
chat.Messages.Last().Files = [];
var result = await Send(chat, new ChatRequestOptions(), cancellationToken);
chat.Messages.Last().Content = lastMsg.Content;
return result;
}

private string ComposeMessage(Message lastMsg, string[] filePaths)
{
var stringBuilder = new StringBuilder();
stringBuilder.AppendLine($"== FILES IN MEMORY");
foreach (var path in filePaths)
{
var doc = DocumentProcessor.ProcessDocument(path);
stringBuilder.Append(doc);
stringBuilder.AppendLine();
}
stringBuilder.AppendLine($"== END OF FILES");
stringBuilder.AppendLine();
stringBuilder.Append(lastMsg.Content);
return stringBuilder.ToString();
}
}
3 changes: 3 additions & 0 deletions src/MaIN.Services/Services/McpService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,9 @@ private PromptExecutionSettings InitializeChatCompletions(IKernelBuilder kernelB
FunctionChoiceBehavior = FunctionChoiceBehavior.Auto(options: new() { RetainArgumentTypes = true })
};

case BackendType.Ollama:
throw new NotSupportedException("Ollama models does not support MCP integration.");

case BackendType.Self:
throw new NotSupportedException("Self backend (local models) does not support MCP integration.");

Expand Down
Loading