Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Frank/ollama module #1099

Open
wants to merge 11 commits into
base: develop
Choose a base branch
from
14 changes: 14 additions & 0 deletions Testcontainers.sln
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Tests", "tes
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.WebDriver.Tests", "tests\Testcontainers.WebDriver.Tests\Testcontainers.WebDriver.Tests.csproj", "{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Ollama", "src\Testcontainers.Ollama\Testcontainers.Ollama.csproj", "{FDC88529-64F5-4F0A-95BE-8FBF653201C6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Testcontainers.Ollama.Tests", "tests\Testcontainers.Ollama.Tests\Testcontainers.Ollama.Tests.csproj", "{0997BFAD-919D-482F-83E6-5DF9C4A1D313}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -544,6 +548,14 @@ Global
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2}.Release|Any CPU.Build.0 = Release|Any CPU
{FDC88529-64F5-4F0A-95BE-8FBF653201C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FDC88529-64F5-4F0A-95BE-8FBF653201C6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FDC88529-64F5-4F0A-95BE-8FBF653201C6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FDC88529-64F5-4F0A-95BE-8FBF653201C6}.Release|Any CPU.Build.0 = Release|Any CPU
{0997BFAD-919D-482F-83E6-5DF9C4A1D313}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0997BFAD-919D-482F-83E6-5DF9C4A1D313}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0997BFAD-919D-482F-83E6-5DF9C4A1D313}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0997BFAD-919D-482F-83E6-5DF9C4A1D313}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{5365F780-0E6C-41F0-B1B9-7DC34368F80C} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
Expand Down Expand Up @@ -633,5 +645,7 @@ Global
{1A1983E6-5297-435F-B467-E8E1F11277D6} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{27CDB869-A150-4593-958F-6F26E5391E7C} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{EBA72C3B-57D5-43FF-A5B4-3D55B3B6D4C2} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
{FDC88529-64F5-4F0A-95BE-8FBF653201C6} = {673F23AE-7694-4BB9-ABD4-136D6C13634E}
{0997BFAD-919D-482F-83E6-5DF9C4A1D313} = {7164F1FB-7F24-444A-ACD2-2C329C2B3CCF}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions docs/modules/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ await moduleNameContainer.StartAsync();
| MySQL | `mysql:8.0` | [NuGet](https://www.nuget.org/packages/Testcontainers.MySql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.MySql) |
| NATS | `nats:2.9` | [NuGet](https://www.nuget.org/packages/Testcontainers.Nats) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Nats) |
| Neo4j | `neo4j:5.4` | [NuGet](https://www.nuget.org/packages/Testcontainers.Neo4j) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Neo4j) |
| Ollama | `ollama/ollama:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.Ollama) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Ollama) |
| Oracle | `gvenzl/oracle-xe:21.3.0-slim-faststart` | [NuGet](https://www.nuget.org/packages/Testcontainers.Oracle) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Oracle) |
| Papercut | `jijiechen/papercut:latest` | [NuGet](https://www.nuget.org/packages/Testcontainers.Papercut) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.Papercut) |
| PostgreSQL | `postgres:15.1` | [NuGet](https://www.nuget.org/packages/Testcontainers.PostgreSql) | [Source](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/src/Testcontainers.PostgreSql) |
Expand Down
1 change: 1 addition & 0 deletions src/Testcontainers.Ollama/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
root = true
96 changes: 96 additions & 0 deletions src/Testcontainers.Ollama/OllamaBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
namespace Testcontainers.Ollama
{
/// <inheritdoc cref="ContainerBuilder{TBuilderEntity, TContainerEntity, TConfigurationEntity}" />
[PublicAPI]
public sealed class OllamaBuilder : ContainerBuilder<OllamaBuilder, OllamaContainer, OllamaConfiguration>
{
/// <summary>
/// Initializes a new instance of the <see cref="OllamaBuilder" /> class.
/// </summary>
public OllamaBuilder()
: this(new OllamaConfiguration())
{
DockerResourceConfiguration = Init().DockerResourceConfiguration;
}

/// <summary>
/// Initializes a new instance of the <see cref="OllamaBuilder" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
private OllamaBuilder(OllamaConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
DockerResourceConfiguration = resourceConfiguration;
}

/// <inheritdoc />
protected override OllamaConfiguration DockerResourceConfiguration { get; }

/// <summary>
/// Sets the Testcontainers.Ollama config.
/// </summary>
/// <param name="config">The Testcontainers.Ollama config.</param>
/// <returns>A configured instance of <see cref="OllamaBuilder" />.</returns>
public OllamaBuilder OllamaConfig(OllamaConfiguration config)
{
return Merge(DockerResourceConfiguration, config);
}

/// <inheritdoc />
public override OllamaContainer Build()
{
Validate();
return new OllamaContainer(DockerResourceConfiguration, TestcontainersSettings.Logger);
}

/// <inheritdoc />
protected override void Validate()
{
Guard.Argument(DockerResourceConfiguration.ModelName, nameof(DockerResourceConfiguration.ModelName)).NotNull().NotEmpty();
base.Validate();
}

/// <inheritdoc />
protected override OllamaBuilder Init()
{
return base.Init()
.WithImage(new DockerImage(OllamaConfiguration.ImageName))
.WithPortBinding(OllamaConfiguration.DefaultPort, true)
.WithVolumeMount(OllamaConfiguration.DefaultVolumeName, OllamaConfiguration.DefaultVolumePath)
;
}

/// <inheritdoc />
protected override OllamaBuilder Clone(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new OllamaConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override OllamaBuilder Clone(IContainerConfiguration resourceConfiguration)
{
return Merge(DockerResourceConfiguration, new OllamaConfiguration(resourceConfiguration));
}

/// <inheritdoc />
protected override OllamaBuilder Merge(OllamaConfiguration oldValue, OllamaConfiguration newValue)
{
return new OllamaBuilder(new OllamaConfiguration(oldValue, newValue));
}

/// <summary>
/// Sets the name of the model to run.
/// </summary>
/// <param name="name">The name of the model to run.</param>
/// <returns>A configured instance of <see cref="OllamaBuilder" />.</returns>
/// <exception cref="ArgumentNullException">The name of the model to run is <see langword="null" />.</exception>
/// <exception cref="ArgumentException">The name of the model to run is empty.</exception>
/// <remarks>
/// The name of the model to run is required.
/// </remarks>
public OllamaBuilder WithModelName(string name)
{
return Merge(DockerResourceConfiguration, new OllamaConfiguration(DockerResourceConfiguration) {ModelName = name});
}
}
}
85 changes: 85 additions & 0 deletions src/Testcontainers.Ollama/OllamaConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
namespace Testcontainers.Ollama
{
/// <inheritdoc cref="ContainerConfiguration" />
[PublicAPI]
public sealed class OllamaConfiguration : ContainerConfiguration
{
/// <summary>
/// Initializes a new instance of the <see cref="OllamaConfiguration" /> class.
/// </summary>
/// <param name="modelName"></param>
/// <param name="schema"></param>
/// <param name="hostName"></param>
/// <param name="port"></param>
public OllamaConfiguration(string modelName = null, string schema = null, int? port = null)
{
ModelName = modelName;
}

/// <summary>
/// Initializes a new instance of the <see cref="OllamaConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public OllamaConfiguration(IResourceConfiguration<CreateContainerParameters> resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="OllamaConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public OllamaConfiguration(IContainerConfiguration resourceConfiguration)
: base(resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="OllamaConfiguration" /> class.
/// </summary>
/// <param name="resourceConfiguration">The Docker resource configuration.</param>
public OllamaConfiguration(OllamaConfiguration resourceConfiguration)
: this(new OllamaConfiguration(), resourceConfiguration)
{
// Passes the configuration upwards to the base implementations to create an updated immutable copy.
}

/// <summary>
/// Initializes a new instance of the <see cref="OllamaConfiguration" /> class.
/// </summary>
/// <param name="oldValue">The old Docker resource configuration.</param>
/// <param name="newValue">The new Docker resource configuration.</param>
public OllamaConfiguration(OllamaConfiguration oldValue, OllamaConfiguration newValue)
: base(oldValue, newValue)
{
ModelName = BuildConfiguration.Combine(oldValue.ModelName, newValue.ModelName);
}

/// <summary>
/// Name of the model to use.
/// </summary>
public string ModelName { get; set; } = OllamaModels.Llama2;

/// <summary>
/// Gets the default port of the Ollama API.
/// </summary>
public const int DefaultPort = 11434;

/// <summary>
/// Default image name.
/// </summary>
public const string ImageName = "ollama/ollama:latest";
frankhaugen marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Default volume path.
/// </summary>
public const string DefaultVolumePath = "/root/.ollama";

/// <summary>
/// Default volume name.
/// </summary>
public const string DefaultVolumeName = "ollama-volume";
frankhaugen marked this conversation as resolved.
Show resolved Hide resolved
}
}
52 changes: 52 additions & 0 deletions src/Testcontainers.Ollama/OllamaContainer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
namespace Testcontainers.Ollama
{
/// <inheritdoc cref="DockerContainer" />
[PublicAPI]
public sealed class OllamaContainer : DockerContainer
{
/// <summary>
/// Initializes a new instance of the <see cref="OllamaContainer" /> class.
/// </summary>
/// <param name="configuration">The container configuration.</param>
/// <param name="logger">The logger.</param>
public OllamaContainer(OllamaConfiguration configuration, ILogger logger)
: base(configuration, logger)
{
ModelName = configuration.ModelName;
ImageName = OllamaConfiguration.ImageName;
}

/// <summary>
/// Starts the Ollama container.
/// </summary>
public async Task StartOllamaAsync()
{
if (State!= TestcontainersStates.Created && State != TestcontainersStates.Running) {
throw new InvalidOperationException("Cannot start a container that has not been created.");
}
frankhaugen marked this conversation as resolved.
Show resolved Hide resolved
Task.WaitAll(ExecAsync(new List<string>()
{
"ollama", "run", ModelName,
}));

await Task.CompletedTask;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to pass the model name as an argument to the member? Run(string modelName, CancellationToken ct = default)? In addition, please do not block the thread; simply return ExecAsync(["ollama", "run", modelName], ct);.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed and resolved about the return. I added two methods one taking only the ct and one taking name and ct. Have a look if you agree


/// <summary>
/// Gets the base URL of the Ollama API.
/// </summary>
/// <returns>The base URL of the Ollama API.</returns>
/// <example>http://localhost:5000/api</example>
public string GetBaseUrl() => $"http://{Hostname}:{GetMappedPublicPort(OllamaConfiguration.DefaultPort)}/api";

/// <summary>
/// Gets the name of the Docker image to use.
/// </summary>
public string ImageName { get; }

/// <summary>
/// Gets the name of the model to run.
/// </summary>
public string ModelName { get; }
}
}
76 changes: 76 additions & 0 deletions src/Testcontainers.Ollama/OllamaModels.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
namespace Testcontainers.Ollama
{
/// <summary>
/// A selection of OLLAMA models from the readme.
/// </summary>
/// <remarks>
/// See: https://github.com/ollama/ollama?tab=readme-ov-file#model-library
/// </remarks>
public static class OllamaModels
{
/// <summary>
/// Llama 2: 7B parameters, Size: 3.8GB, Command: ollama run llama2
/// </summary>
public const string Llama2 = "llama2";

/// <summary>
/// Mistral: 7B parameters, Size: 4.1GB, Command: ollama run mistral
/// </summary>
public const string Mistral = "mistral";

/// <summary>
/// Dolphin Phi: 2.7B parameters, Size: 1.6GB, Command: ollama run dolphin-phi
/// </summary>
public const string DolphinPhi = "dolphin-phi";

/// <summary>
/// Phi-2: 2.7B parameters, Size: 1.7GB, Command: ollama run phi
/// </summary>
public const string Phi2 = "phi";

/// <summary>
/// Neural Chat: 7B parameters, Size: 4.1GB, Command: ollama run neural-chat
/// </summary>
public const string NeuralChat = "neural-chat";

/// <summary>
/// Starling: 7B parameters, Size: 4.1GB, Command: ollama run starling-lm
/// </summary>
public const string Starling = "starling-lm";

/// <summary>
/// Code Llama: 7B parameters, Size: 3.8GB, Command: ollama run codellama
/// </summary>
public const string CodeLlama = "codellama";

/// <summary>
/// Llama 2 Uncensored: 7B parameters, Size: 3.8GB, Command: ollama run llama2-uncensored
/// </summary>
public const string Llama2Uncensored = "llama2-uncensored";

/// <summary>
/// Llama 2 13B: 13B parameters, Size: 7.3GB, Command: ollama run llama2:13b
/// </summary>
public const string Llama213B = "llama2:13b";

/// <summary>
/// Llama 2 70B: 70B parameters, Size: 39GB, Command: ollama run llama2:70b
/// </summary>
public const string Llama270B = "llama2:70b";

/// <summary>
/// Orca Mini: 3B parameters, Size: 1.9GB, Command: ollama run orca-mini
/// </summary>
public const string OrcaMini = "orca-mini";

/// <summary>
/// Vicuna: 7B parameters, Size: 3.8GB, Command: ollama run vicuna
/// </summary>
public const string Vicuna = "vicuna";

/// <summary>
/// LLaVA: 7B parameters, Size: 4.5GB, Command: ollama run llava
/// </summary>
public const string LLaVA = "llava";
}
}
12 changes: 12 additions & 0 deletions src/Testcontainers.Ollama/Testcontainers.Ollama.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1</TargetFrameworks>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" PrivateAssets="All"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="$(SolutionDir)src/Testcontainers/Testcontainers.csproj"/>
</ItemGroup>
</Project>
11 changes: 11 additions & 0 deletions src/Testcontainers.Ollama/Usings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
global using Docker.DotNet.Models;
global using DotNet.Testcontainers.Builders;
global using DotNet.Testcontainers.Configurations;
global using DotNet.Testcontainers.Containers;
global using DotNet.Testcontainers;
global using JetBrains.Annotations;
global using Microsoft.Extensions.Logging;
global using DotNet.Testcontainers.Images;
global using System;
global using System.Collections.Generic;
global using System.Threading.Tasks;