From 12051b9ba0380565ee7e98eba5d10c0f6ab3a2a8 Mon Sep 17 00:00:00 2001 From: HavenDV Date: Mon, 19 Aug 2024 16:37:15 +0400 Subject: [PATCH] docs: Added mkdocs. --- .github/workflows/mkdocs.yml | 54 ++++ Ollama.sln | 7 + docs/css/extra.css | 102 +++++++ docs/media/icon128.png | Bin 0 -> 13498 bytes mkdocs.yml | 108 ++++++++ src/helpers/GenerateDocs/GenerateDocs.csproj | 9 + src/helpers/GenerateDocs/Program.cs | 38 +++ .../Ollama.IntegrationTests/Tests.Chat.cs | 19 ++ .../Tests.Embeddings.cs | 31 +++ .../Tests.GetCompletion.cs | 26 ++ .../Tests.GetCompletionWithOptions.cs | 26 ++ .../Ollama.IntegrationTests/Tests.Helpers.cs | 62 ++++- .../Tests.Integration.cs | 257 ------------------ .../Tests.ListModels.cs | 20 ++ .../Tests.PullModel.cs | 20 ++ .../Ollama.IntegrationTests/Tests.Tools.cs | 55 ++++ .../Tests.ToolsInChat.cs | 31 +++ 17 files changed, 607 insertions(+), 258 deletions(-) create mode 100644 .github/workflows/mkdocs.yml create mode 100644 docs/css/extra.css create mode 100644 docs/media/icon128.png create mode 100644 mkdocs.yml create mode 100644 src/helpers/GenerateDocs/GenerateDocs.csproj create mode 100644 src/helpers/GenerateDocs/Program.cs create mode 100755 src/tests/Ollama.IntegrationTests/Tests.Chat.cs create mode 100755 src/tests/Ollama.IntegrationTests/Tests.Embeddings.cs create mode 100755 src/tests/Ollama.IntegrationTests/Tests.GetCompletion.cs create mode 100755 src/tests/Ollama.IntegrationTests/Tests.GetCompletionWithOptions.cs delete mode 100755 src/tests/Ollama.IntegrationTests/Tests.Integration.cs create mode 100755 src/tests/Ollama.IntegrationTests/Tests.ListModels.cs create mode 100755 src/tests/Ollama.IntegrationTests/Tests.PullModel.cs create mode 100755 src/tests/Ollama.IntegrationTests/Tests.Tools.cs create mode 100755 src/tests/Ollama.IntegrationTests/Tests.ToolsInChat.cs diff --git a/.github/workflows/mkdocs.yml b/.github/workflows/mkdocs.yml new file mode 100644 index 0000000..5c80b98 --- /dev/null +++ b/.github/workflows/mkdocs.yml @@ -0,0 +1,54 @@ +name: MKDocs Deploy +on: + push: + branches: + - main + paths: + - 'docs/**' + - 'mkdocs.yml' + - 'examples/**' + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Pages + uses: actions/configure-pages@v5 + + - name: Install dependencies + run: pip install mkdocs-material + + - name: Generate docs + run: dotnet run --project src/helpers/GenerateDocs/GenerateDocs.csproj . + + - name: Build with MkDocs + run: mkdocs build -d ./_site + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/Ollama.sln b/Ollama.sln index 939b39a..2071ba7 100755 --- a/Ollama.sln +++ b/Ollama.sln @@ -43,6 +43,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{ .github\dependabot.yml = .github\dependabot.yml EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GenerateDocs", "src\helpers\GenerateDocs\GenerateDocs.csproj", "{E1D4D0DA-5A4C-4253-B690-C581FB23EA03}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -77,6 +79,10 @@ Global {8198FC7A-4DCA-4E18-B812-BE953985BF7E}.Debug|Any CPU.Build.0 = Debug|Any CPU {8198FC7A-4DCA-4E18-B812-BE953985BF7E}.Release|Any CPU.ActiveCfg = Release|Any CPU {8198FC7A-4DCA-4E18-B812-BE953985BF7E}.Release|Any CPU.Build.0 = Release|Any CPU + {E1D4D0DA-5A4C-4253-B690-C581FB23EA03}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1D4D0DA-5A4C-4253-B690-C581FB23EA03}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E1D4D0DA-5A4C-4253-B690-C581FB23EA03}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1D4D0DA-5A4C-4253-B690-C581FB23EA03}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -90,6 +96,7 @@ Global {824E0F5F-0272-4EAE-BCC3-CB6F4E88EC12} = {61E7E11E-4558-434C-ACE8-06316A3097B3} {8198FC7A-4DCA-4E18-B812-BE953985BF7E} = {AAA11B78-2764-4520-A97E-46AA7089A588} {95FEBA37-963B-4E42-A49C-33905341480C} = {E793AF18-4371-4EBD-96FC-195EB1798855} + {E1D4D0DA-5A4C-4253-B690-C581FB23EA03} = {C612F166-61F7-4552-A52B-2494F45DB431} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {CED9A020-DBA5-4BE6-8096-75E528648EC1} diff --git a/docs/css/extra.css b/docs/css/extra.css new file mode 100644 index 0000000..f217483 --- /dev/null +++ b/docs/css/extra.css @@ -0,0 +1,102 @@ +/* Logo title */ +.md-header__topic:first-child { + font-weight: initial !important; +} + +/* Code font size in
*/ +details .linenos, details code { + font-size: inherit !important; +} + +/* Code block / tab in details */ +details > summary + .highlight:last-child, details > summary + .tabbed-set:last-child { margin: 0 -0.6rem !important; } +details > summary + .highlight:last-child > .highlighttable { margin: 0 !important; } + +/* Table full width */ +.md-typeset__table { display: block !important; } +.md-typeset table:not(.highlighttable) { display: table !important; } + +.md-typeset table:not([class]) th { + min-width: 0rem; +} + +.headerlink { transform: translateY(-2.5px); } + +.md-nav__link[for=__toc] .md-icon { margin-left: auto !important; } + +blockquote.page-time { + margin: 20px 0 !important; + border-left-color: #64b5f6 !important; /* Just change the color value and that's it*/ +} +blockquote.page-copyright { + margin: 20px 0 !important; + border-left-color: #ff1700 !important; /* Just change the color value and that's it*/ +} +blockquote.page-copyright i.md-icon { + display: inline-block; + margin-right: 5px; + transform: translateY(3.5px); + width: 18px; +} + +#myBtn { + display: none; + position: fixed; + bottom: 100px; + right: 16px; + z-index: 99; + border: none; + outline: none; + color: #8590a6; + cursor: pointer; + padding: .7rem; + border-radius: .4rem; +} + +#myBtn:hover { + background-color: #d3d3d3; +} + +#color-button > button { + cursor: pointer; + transition: opacity .25s; + display: inline-block; + width: 6.5rem; + margin-bottom: 0.2rem; + padding: 1.2rem 0.4rem 0.2rem; + font-size: 0.64rem; + text-align: left; +} + +#color-button > button[data-md-color-primary] { + background-color: var(--md-primary-fg-color); + color: var(--md-primary-bg-color); +} +#color-button > button[data-md-color-primary=white] { + box-shadow: inset 0 0 0.05rem rgb(0 0 0 / 54%); +} + +#color-button > button[data-md-color-accent] { + background-color: var(--md-accent-fg-color); + color: var(--md-code-bg-color); +} + +mjx-container > img { + width: 0; + height: 0; +} + +[data-md-color-scheme="slate"] { + --md-primary-fg-color: #2e303e; + --md-accent-fg-color: #00bda4; + --md-typeset-a-color: #526cfe; +} + +[data-md-color-scheme="slate"] .md-typeset img { + background: white; + filter: brightness(0.9); +} + +[data-md-color-scheme="slate"] .md-typeset img[src$=".svg"] { + border: 4px solid white; +} \ No newline at end of file diff --git a/docs/media/icon128.png b/docs/media/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..05d2b17ae9551641c26b916e6e211d9e6e6ff466 GIT binary patch literal 13498 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 literal 0 HcmV?d00001 diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..3797080 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,108 @@ +site_name: OpenAI .NET Documentation +nav: +- Overview: index.md +# EXAMPLES # + +# - Quick Start: QuickStart.md +# - Architecture: Architecture.md +# - FAQ: FAQ.md +# - Contributing Guide: ContributingGuide.md +# - Tutorials: +# - Configure the native library loading: Tutorials/NativeLibraryConfig.md +# - Use executors: Tutorials/Executors.md +# - Use ChatSession: Tutorials/ChatSession.md +# - Understand LLamaContext: Tutorials/UnderstandLLamaContext.md +# - Get embeddings: Tutorials/GetEmbeddings.md +# - Quantize the model: Tutorials/Quantization.md +# +# - Advanced Tutorials: +# - Customize the native library loading: AdvancedTutorials/CustomizeNativeLibraryLoading.md +# +# - Integrations: +# - semantic-kernel integration: Integrations/semantic-kernel.md +# - kernel-memory integration: Integrations/kernel-memory.md +# - BotSharp integration: Integrations/BotSharp.md +# - Langchain integration: Integrations/Langchain.md +# +# - Examples: +# - Bacthed executor - multi-output to one input: Examples/BatchedExecutorFork.md +# - Batched executor - basic guidance: Examples/BatchedExecutorGuidance.md +# - Batched executor - rewinding to an earlier state: Examples/BatchedExecutorRewind.md + +theme: + name: material + static_templates: + - 404.html + language: 'en' + palette: + # Palette toggle for light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: white + accent: red + toggle: + icon: material/weather-sunny + name: Switch to dark mode + + # Palette toggle for dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: blue + accent: blue + toggle: + icon: material/weather-night + name: Switch to light mode + include_search_page: false + search_index_only: true + favicon: 'media/icon128.png' + icon: + logo: 'material/file-document' + features: + - content.action.edit + - navigation.instant + font: + text: 'Fira Sans' + code: 'Fira Mono' + +extra: + version: + provider: mike + +extra_css: + - 'css/extra.css?v=14' + +markdown_extensions: + - admonition + - def_list + - footnotes + - meta + - toc: + permalink: "" + slugify: !!python/name:pymdownx.slugs.uslugify + - pymdownx.arithmatex: + generic: true + - pymdownx.caret + - pymdownx.critic + - pymdownx.details + - pymdownx.emoji: + emoji_generator: !!python/name:pymdownx.emoji.to_svg + - pymdownx.highlight: + linenums: true + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.magiclink + - pymdownx.mark + - pymdownx.snippets: + check_paths: true + - pymdownx.progressbar + - pymdownx.smartsymbols + - pymdownx.superfences: + custom_fences: + - name: math + class: arithmatex + format: !!python/name:pymdownx.arithmatex.fence_mathjax_format + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + - pymdownx.tabbed: + alternate_style: true \ No newline at end of file diff --git a/src/helpers/GenerateDocs/GenerateDocs.csproj b/src/helpers/GenerateDocs/GenerateDocs.csproj new file mode 100644 index 0000000..f67f521 --- /dev/null +++ b/src/helpers/GenerateDocs/GenerateDocs.csproj @@ -0,0 +1,9 @@ + + + + Exe + net8.0 + enable + + + diff --git a/src/helpers/GenerateDocs/Program.cs b/src/helpers/GenerateDocs/Program.cs new file mode 100644 index 0000000..7ae8efa --- /dev/null +++ b/src/helpers/GenerateDocs/Program.cs @@ -0,0 +1,38 @@ +var solutionDirectory = args.ElementAtOrDefault(0) ?? Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), "../../../../../..")); +var sampleDirectory = Path.Combine(solutionDirectory, "src", "tests", "Ollama.IntegrationTests"); +var mkDocsPath = Path.Combine(solutionDirectory, "mkdocs.yml"); + +var newDir = Path.Combine(solutionDirectory, "docs", "samples"); +Directory.CreateDirectory(newDir); + +File.Copy( + Path.Combine(solutionDirectory, "README.md"), + Path.Combine(solutionDirectory, "docs", "index.md"), + overwrite: true); + +Console.WriteLine($"Generating samples from {sampleDirectory}..."); +foreach (var path in Directory.EnumerateFiles(sampleDirectory, "Tests.*.cs", SearchOption.AllDirectories)) +{ + var code = await File.ReadAllTextAsync(path); + + var start = code.IndexOf("\n {", StringComparison.Ordinal); + var end = code.IndexOf("\n }", StringComparison.Ordinal); + code = code.Substring(start + 4, end - start + 4); + + var lines = code.Split('\n')[1..^2]; + code = string.Join('\n', lines.Select(x => x.Length > 8 ? x[8..] : string.Empty)); + + var newPath = Path.Combine(newDir, $"{Path.GetExtension(Path.GetFileNameWithoutExtension(path)).TrimStart('.')}.md"); + await File.WriteAllTextAsync(newPath, $@"```csharp +{code} +```"); +} + +var mkDocs = await File.ReadAllTextAsync(mkDocsPath); +var newMkDocs = mkDocs.Replace( + "# EXAMPLES #", + $"- Examples:{string.Concat(Directory.EnumerateFiles(Path.Combine(solutionDirectory, "docs", "samples"), "*.md") + .Select(x => $@" + - {Path.GetFileNameWithoutExtension(x)}: samples/{Path.GetFileNameWithoutExtension(x)}.md"))}"); +await File.WriteAllTextAsync(mkDocsPath, newMkDocs); + diff --git a/src/tests/Ollama.IntegrationTests/Tests.Chat.cs b/src/tests/Ollama.IntegrationTests/Tests.Chat.cs new file mode 100755 index 0000000..a561d51 --- /dev/null +++ b/src/tests/Ollama.IntegrationTests/Tests.Chat.cs @@ -0,0 +1,19 @@ +namespace Ollama.IntegrationTests; + +public partial class Tests +{ + [TestMethod] + public async Task Chat() + { +#if DEBUG + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3"); +#else + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3"); +#endif + + var chat = container.ApiClient.Chat("llama3"); + var message = await chat.SendAsync("answer 5 random words"); + + Console.WriteLine(message.Content); + } +} \ No newline at end of file diff --git a/src/tests/Ollama.IntegrationTests/Tests.Embeddings.cs b/src/tests/Ollama.IntegrationTests/Tests.Embeddings.cs new file mode 100755 index 0000000..ac6c96d --- /dev/null +++ b/src/tests/Ollama.IntegrationTests/Tests.Embeddings.cs @@ -0,0 +1,31 @@ +namespace Ollama.IntegrationTests; + +public partial class Tests +{ + [TestMethod] + public async Task Embeddings() + { + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container); + + if (container.Type == EnvironmentType.Local) + { + var models = await container.ApiClient.Models.ListRunningModelsAsync(); + models.Models.Should().BeNullOrEmpty(); + } + + await container.ApiClient.Models.PullModelAndEnsureSuccessAsync("all-minilm"); + + var embeddingResponse = await container.ApiClient.Embeddings.GenerateEmbeddingAsync( + model:"all-minilm", + prompt: "hello"); + + embeddingResponse.Embedding.Should().NotBeNullOrEmpty(); + + if (container.Type == EnvironmentType.Local) + { + var models2 = await container.ApiClient.Models.ListRunningModelsAsync(); + models2.Models.Should().NotBeNullOrEmpty(); + } + + } +} \ No newline at end of file diff --git a/src/tests/Ollama.IntegrationTests/Tests.GetCompletion.cs b/src/tests/Ollama.IntegrationTests/Tests.GetCompletion.cs new file mode 100755 index 0000000..846fe76 --- /dev/null +++ b/src/tests/Ollama.IntegrationTests/Tests.GetCompletion.cs @@ -0,0 +1,26 @@ +namespace Ollama.IntegrationTests; + +public partial class Tests +{ + [TestMethod] + public async Task GetCompletion() + { +#if DEBUG + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3"); +#else + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3"); +#endif + + IList? context = null; + var enumerable = container.ApiClient.Completions.GenerateCompletionAsync("llama3", "answer 5 random words", stream: true); + await foreach (var response in enumerable) + { + Console.WriteLine($"> {response.Response}"); + + context = response.Context; + } + + var lastResponse = await container.ApiClient.Completions.GenerateCompletionAsync("llama3", "answer 123", stream: false, context: context).WaitAsync(); + Console.WriteLine(lastResponse.Response); + } +} \ No newline at end of file diff --git a/src/tests/Ollama.IntegrationTests/Tests.GetCompletionWithOptions.cs b/src/tests/Ollama.IntegrationTests/Tests.GetCompletionWithOptions.cs new file mode 100755 index 0000000..6e48717 --- /dev/null +++ b/src/tests/Ollama.IntegrationTests/Tests.GetCompletionWithOptions.cs @@ -0,0 +1,26 @@ +namespace Ollama.IntegrationTests; + +public partial class Tests +{ + [TestMethod] + public async Task GetCompletionWithOptions() + { +#if DEBUG + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3"); +#else + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3"); +#endif + + var response = await container.ApiClient.Completions.GenerateCompletionAsync(new GenerateCompletionRequest + { + Model = "llama3", + Prompt = "answer me just \"123\"", + Stream = true, + Options = new RequestOptions + { + Temperature = 0, + }, + }).WaitAsync(); + Console.WriteLine(response.Response); + } +} \ No newline at end of file diff --git a/src/tests/Ollama.IntegrationTests/Tests.Helpers.cs b/src/tests/Ollama.IntegrationTests/Tests.Helpers.cs index b3f940a..2caced4 100755 --- a/src/tests/Ollama.IntegrationTests/Tests.Helpers.cs +++ b/src/tests/Ollama.IntegrationTests/Tests.Helpers.cs @@ -1,4 +1,64 @@ +using DotNet.Testcontainers.Builders; + namespace Ollama.IntegrationTests; [TestClass] -public partial class Tests; +public partial class Tests +{ + + private static async Task PrepareEnvironmentAsync(EnvironmentType environmentType, string model = "") + { + switch (environmentType) + { + case EnvironmentType.Local: + { + // set OLLAMA_HOST=10.10.0.125:11434 + // ollama serve + var apiClient = new OllamaApiClient( + httpClient: new HttpClient + { + Timeout = TimeSpan.FromMinutes(10), + }, + baseUri: new Uri("http://10.10.0.125:11434/api")); + + if (!string.IsNullOrEmpty(model)) + { + await apiClient.Models.PullModelAndEnsureSuccessAsync(model); + } + + return new Environment + { + Type = environmentType, + ApiClient = apiClient, + }; + } + case EnvironmentType.Container: + { + var container = new ContainerBuilder() + .WithImage("ollama/ollama") + .WithPortBinding(hostPort: 11434, containerPort: 11434) + .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(11434)) + .Build(); + + await container.StartAsync(); + + var apiClient = new OllamaApiClient(); + if (!string.IsNullOrEmpty(model)) + { + await apiClient.Models.PullModelAndEnsureSuccessAsync(model); + } + + return new Environment + { + Type = environmentType, + Container = container, + ApiClient = apiClient, + }; + } + default: + throw new ArgumentOutOfRangeException(nameof(environmentType), environmentType, null); + } + } + +} + diff --git a/src/tests/Ollama.IntegrationTests/Tests.Integration.cs b/src/tests/Ollama.IntegrationTests/Tests.Integration.cs deleted file mode 100755 index 5354210..0000000 --- a/src/tests/Ollama.IntegrationTests/Tests.Integration.cs +++ /dev/null @@ -1,257 +0,0 @@ -using DotNet.Testcontainers.Builders; - -namespace Ollama.IntegrationTests; - -public partial class Tests -{ - private static async Task PrepareEnvironmentAsync(EnvironmentType environmentType, string model = "") - { - switch (environmentType) - { - case EnvironmentType.Local: - { - // set OLLAMA_HOST=10.10.0.125:11434 - // ollama serve - var apiClient = new OllamaApiClient( - httpClient: new HttpClient - { - Timeout = TimeSpan.FromMinutes(10), - }, - baseUri: new Uri("http://10.10.0.125:11434/api")); - - if (!string.IsNullOrEmpty(model)) - { - await apiClient.Models.PullModelAndEnsureSuccessAsync(model); - } - - return new Environment - { - Type = environmentType, - ApiClient = apiClient, - }; - } - case EnvironmentType.Container: - { - var container = new ContainerBuilder() - .WithImage("ollama/ollama") - .WithPortBinding(hostPort: 11434, containerPort: 11434) - .WithWaitStrategy(Wait.ForUnixContainer().UntilPortIsAvailable(11434)) - .Build(); - - await container.StartAsync(); - - var apiClient = new OllamaApiClient(); - if (!string.IsNullOrEmpty(model)) - { - await apiClient.Models.PullModelAndEnsureSuccessAsync(model); - } - - return new Environment - { - Type = environmentType, - Container = container, - ApiClient = apiClient, - }; - } - default: - throw new ArgumentOutOfRangeException(nameof(environmentType), environmentType, null); - } - } - - [TestMethod] - public async Task ListModels() - { - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container); - - var models = await container.ApiClient.Models.ListModelsAsync(); - models.Models.Should().BeNullOrEmpty(); - - await container.ApiClient.Models.PullModelAndEnsureSuccessAsync("all-minilm"); - - models = await container.ApiClient.Models.ListModelsAsync(); - models.Models.Should().NotBeNull(); - models.Models.Should().HaveCount(1); - models.Models![0].Model1.Should().Be("all-minilm:latest"); - } - - [TestMethod] - public async Task PullModel() - { - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container); - - await foreach (var response in container.ApiClient.Models.PullModelAsync("all-minilm", stream: true)) - { - Console.WriteLine($"{response.Status?.Object}. Progress: {response.Completed}/{response.Total}"); - } - - var response2 = await container.ApiClient.Models.PullModelAsync("all-minilm").WaitAsync(); - response2.EnsureSuccess(); - - await container.ApiClient.Models.PullModelAndEnsureSuccessAsync("all-minilm"); - } - - [TestMethod] - public async Task Embeddings() - { - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container); - - if (container.Type == EnvironmentType.Local) - { - var models = await container.ApiClient.Models.ListRunningModelsAsync(); - models.Models.Should().BeNullOrEmpty(); - } - - await container.ApiClient.Models.PullModelAndEnsureSuccessAsync("all-minilm"); - - var embeddingResponse = await container.ApiClient.Embeddings.GenerateEmbeddingAsync( - model:"all-minilm", - prompt: "hello"); - - embeddingResponse.Embedding.Should().NotBeNullOrEmpty(); - - if (container.Type == EnvironmentType.Local) - { - var models2 = await container.ApiClient.Models.ListRunningModelsAsync(); - models2.Models.Should().NotBeNullOrEmpty(); - } - - } - - [TestMethod] - public async Task GetCompletionWithOptions() - { -#if DEBUG - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3"); -#else - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3"); -#endif - - var response = await container.ApiClient.Completions.GenerateCompletionAsync(new GenerateCompletionRequest - { - Model = "llama3", - Prompt = "answer me just \"123\"", - Stream = true, - Options = new RequestOptions - { - Temperature = 0, - }, - }).WaitAsync(); - Console.WriteLine(response.Response); - } - - [TestMethod] - public async Task GetCompletion() - { -#if DEBUG - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3"); -#else - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3"); -#endif - - IList? context = null; - var enumerable = container.ApiClient.Completions.GenerateCompletionAsync("llama3", "answer 5 random words", stream: true); - await foreach (var response in enumerable) - { - Console.WriteLine($"> {response.Response}"); - - context = response.Context; - } - - var lastResponse = await container.ApiClient.Completions.GenerateCompletionAsync("llama3", "answer 123", stream: false, context: context).WaitAsync(); - Console.WriteLine(lastResponse.Response); - } - - [TestMethod] - public async Task GetChat() - { -#if DEBUG - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3"); -#else - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3"); -#endif - - var chat = container.ApiClient.Chat("llama3"); - var message = await chat.SendAsync("answer 5 random words"); - - Console.WriteLine(message.Content); - } - - [TestMethod] - public async Task Tools() - { -#if DEBUG - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3.1"); -#else - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3.1"); -#endif - - var messages = new List - { - "You are a helpful weather assistant.".AsSystemMessage(), - "What is the current temperature in Dubai, UAE in Celsius?".AsUserMessage(), - }; - const string model = "llama3.1"; - - try - { - var service = new WeatherService(); - var tools = service.AsTools(); - var response = await container.ApiClient.Chat.GenerateChatCompletionAsync( - model, - messages, - stream: false, - tools: tools).WaitAsync(); - - messages.Add(response.Message); - - response.Message.ToolCalls.Should().NotBeNullOrEmpty(because: "Expected a function call."); - - foreach (var call in response.Message.ToolCalls!) - { - var json = await service.CallAsync( - functionName: call.Function?.Name ?? string.Empty, - argumentsAsJson: call.Function?.Arguments.AsJson() ?? string.Empty); - messages.Add(json.AsToolMessage()); - } - - response = await container.ApiClient.Chat.GenerateChatCompletionAsync( - model, - messages, - stream: false, - tools: tools).WaitAsync(); - messages.Add(response.Message); - } - finally - { - Console.WriteLine(Chat.PrintMessages(messages)); - } - } - - - [TestMethod] - public async Task ToolsInChat() - { -#if DEBUG - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3.1"); -#else - await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3.1"); -#endif - - var chat = container.ApiClient.Chat( - model: "llama3.1", - systemMessage: "You are a helpful weather assistant.", - autoCallTools: true); - - var service = new WeatherService(); - chat.AddToolService(service.AsTools(), service.AsCalls()); - - try - { - _ = await chat.SendAsync("What is the current temperature in Dubai, UAE in Celsius?"); - } - finally - { - Console.WriteLine(chat.PrintMessages()); - } - } -} \ No newline at end of file diff --git a/src/tests/Ollama.IntegrationTests/Tests.ListModels.cs b/src/tests/Ollama.IntegrationTests/Tests.ListModels.cs new file mode 100755 index 0000000..87b2743 --- /dev/null +++ b/src/tests/Ollama.IntegrationTests/Tests.ListModels.cs @@ -0,0 +1,20 @@ +namespace Ollama.IntegrationTests; + +public partial class Tests +{ + [TestMethod] + public async Task ListModels() + { + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container); + + var models = await container.ApiClient.Models.ListModelsAsync(); + models.Models.Should().BeNullOrEmpty(); + + await container.ApiClient.Models.PullModelAndEnsureSuccessAsync("all-minilm"); + + models = await container.ApiClient.Models.ListModelsAsync(); + models.Models.Should().NotBeNull(); + models.Models.Should().HaveCount(1); + models.Models![0].Model1.Should().Be("all-minilm:latest"); + } +} \ No newline at end of file diff --git a/src/tests/Ollama.IntegrationTests/Tests.PullModel.cs b/src/tests/Ollama.IntegrationTests/Tests.PullModel.cs new file mode 100755 index 0000000..94c8195 --- /dev/null +++ b/src/tests/Ollama.IntegrationTests/Tests.PullModel.cs @@ -0,0 +1,20 @@ +namespace Ollama.IntegrationTests; + +public partial class Tests +{ + [TestMethod] + public async Task PullModel() + { + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container); + + await foreach (var response in container.ApiClient.Models.PullModelAsync("all-minilm", stream: true)) + { + Console.WriteLine($"{response.Status?.Object}. Progress: {response.Completed}/{response.Total}"); + } + + var response2 = await container.ApiClient.Models.PullModelAsync("all-minilm").WaitAsync(); + response2.EnsureSuccess(); + + await container.ApiClient.Models.PullModelAndEnsureSuccessAsync("all-minilm"); + } +} \ No newline at end of file diff --git a/src/tests/Ollama.IntegrationTests/Tests.Tools.cs b/src/tests/Ollama.IntegrationTests/Tests.Tools.cs new file mode 100755 index 0000000..8f1aeaf --- /dev/null +++ b/src/tests/Ollama.IntegrationTests/Tests.Tools.cs @@ -0,0 +1,55 @@ +namespace Ollama.IntegrationTests; + +public partial class Tests +{ + [TestMethod] + public async Task Tools() + { +#if DEBUG + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3.1"); +#else + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3.1"); +#endif + + var messages = new List + { + "You are a helpful weather assistant.".AsSystemMessage(), + "What is the current temperature in Dubai, UAE in Celsius?".AsUserMessage(), + }; + const string model = "llama3.1"; + + try + { + var service = new WeatherService(); + var tools = service.AsTools(); + var response = await container.ApiClient.Chat.GenerateChatCompletionAsync( + model, + messages, + stream: false, + tools: tools).WaitAsync(); + + messages.Add(response.Message); + + response.Message.ToolCalls.Should().NotBeNullOrEmpty(because: "Expected a function call."); + + foreach (var call in response.Message.ToolCalls!) + { + var json = await service.CallAsync( + functionName: call.Function?.Name ?? string.Empty, + argumentsAsJson: call.Function?.Arguments.AsJson() ?? string.Empty); + messages.Add(json.AsToolMessage()); + } + + response = await container.ApiClient.Chat.GenerateChatCompletionAsync( + model, + messages, + stream: false, + tools: tools).WaitAsync(); + messages.Add(response.Message); + } + finally + { + Console.WriteLine(Ollama.Chat.PrintMessages(messages)); + } + } +} \ No newline at end of file diff --git a/src/tests/Ollama.IntegrationTests/Tests.ToolsInChat.cs b/src/tests/Ollama.IntegrationTests/Tests.ToolsInChat.cs new file mode 100755 index 0000000..545eceb --- /dev/null +++ b/src/tests/Ollama.IntegrationTests/Tests.ToolsInChat.cs @@ -0,0 +1,31 @@ +namespace Ollama.IntegrationTests; + +public partial class Tests +{ + [TestMethod] + public async Task ToolsInChat() + { +#if DEBUG + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Local, "llama3.1"); +#else + await using var container = await PrepareEnvironmentAsync(EnvironmentType.Container, "llama3.1"); +#endif + + var chat = container.ApiClient.Chat( + model: "llama3.1", + systemMessage: "You are a helpful weather assistant.", + autoCallTools: true); + + var service = new WeatherService(); + chat.AddToolService(service.AsTools(), service.AsCalls()); + + try + { + _ = await chat.SendAsync("What is the current temperature in Dubai, UAE in Celsius?"); + } + finally + { + Console.WriteLine(chat.PrintMessages()); + } + } +} \ No newline at end of file