├── .github └── workflows │ ├── dotnet-build.yml │ └── dotnet-publish.yml ├── .gitignore ├── LICENSE.md ├── README.md ├── dotnet ├── .dockerignore ├── .gitignore ├── .idea │ ├── .gitignore │ ├── .idea.Codeblaze.SemanticKernel │ │ └── .idea │ │ │ ├── .gitignore │ │ │ ├── .name │ │ │ ├── encodings.xml │ │ │ ├── indexLayout.xml │ │ │ └── vcs.xml │ ├── encodings.xml │ ├── indexLayout.xml │ └── vcs.xml ├── Codeblaze.SemanticKernel.Api │ ├── Codeblaze.SemanticKernel.Api.csproj │ ├── Codeblaze.SemanticKernel.Api.http │ ├── Dockerfile │ ├── Program.cs │ ├── appsettings.Development.json │ └── appsettings.json ├── Codeblaze.SemanticKernel.Connectors.Memory.Neo4j │ ├── Codeblaze.SemanticKernel.Connectors.Memory.Neo4j.csproj │ ├── INeo4jQueryFactory.cs │ ├── Neo4jMemoryBuilderExtension.cs │ ├── Neo4jMemoryStore.cs │ └── README.md ├── Codeblaze.SemanticKernel.Connectors.Ollama │ ├── ChatCompletion │ │ ├── OllamaChatCompletionService.cs │ │ ├── OllamaChatRequestMessage.cs │ │ └── OllamaChatResponseMessage.cs │ ├── Codeblaze.SemanticKernel.Connectors.Ollama.csproj │ ├── Codeblaze.SemanticKernel.Connectors.Ollama.csproj.DotSettings │ ├── EmbeddingGeneration │ │ └── OllamaTextEmbeddingGeneration.cs │ ├── OllamaBase.cs │ ├── OllamaKernelBuilderExtensions.cs │ ├── OllamaMemoryBuilderExtensions.cs │ ├── OllamaPromptExecutionSettings.cs │ ├── OllamaResponseMessage.cs │ ├── README.md │ └── TextGenerationService │ │ └── OllamaTextGenerationService.cs ├── Codeblaze.SemanticKernel.Console │ ├── .gitignore │ ├── Codeblaze.SemanticKernel.Console.csproj │ ├── Dockerfile │ ├── Program.cs │ └── Services │ │ ├── KernelService.cs │ │ ├── NeoKernelService.cs │ │ └── NeoMemoryService.cs ├── Codeblaze.SemanticKernel.Plugins.Neo4j │ ├── Codeblaze.SemanticKernel.Plugins.Neo4j.csproj │ ├── Neo4jCypherGenPlugin.cs │ ├── Neo4jPluginBuilderExtension.cs │ └── README.md ├── Codeblaze.SemanticKernel.sln ├── Codeblaze.SemanticKernel.sln.DotSettings └── global.json ├── java └── .gitkeep └── python └── .gitkeep /.github/workflows/dotnet-build.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | name: dotnet-build 4 | 5 | on: 6 | workflow_dispatch: # Allow running the workflow manually from the GitHub UI 7 | pull_request: 8 | branches: 9 | - 'main' 10 | push: 11 | branches: 12 | - 'main' # Run the workflow when pushing to the main branch 13 | 14 | env: 15 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 16 | DOTNET_NOLOGO: true 17 | NuGetDirectory: ${{ github.workspace }}/dotnet/nuget 18 | 19 | defaults: 20 | run: 21 | working-directory: ./dotnet 22 | shell: pwsh 23 | 24 | jobs: 25 | create_nuget: 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/checkout@v3 29 | with: 30 | fetch-depth: 0 # Get all history to allow automatic versioning using MinVer 31 | 32 | # Install the .NET SDK indicated in the global.json file 33 | - name: Setup .NET 34 | uses: actions/setup-dotnet@v3 35 | 36 | # Create the NuGet package in the folder from the environment variable NuGetDirectory 37 | - run: dotnet pack --configuration Release --output ${{ env.NuGetDirectory }} 38 | 39 | # Publish the NuGet package as an artifact, so they can be used in the following jobs 40 | - uses: actions/upload-artifact@v3 41 | with: 42 | name: nuget 43 | if-no-files-found: error 44 | retention-days: 7 45 | path: ${{ env.NuGetDirectory }}/*.nupkg 46 | 47 | validate_nuget: 48 | runs-on: ubuntu-latest 49 | needs: [ create_nuget ] 50 | steps: 51 | # Install the .NET SDK indicated in the global.json file 52 | - name: Setup .NET 53 | uses: actions/setup-dotnet@v3 54 | 55 | # Download the NuGet package created in the previous job 56 | - uses: actions/download-artifact@v3 57 | with: 58 | name: nuget 59 | path: ${{ env.NuGetDirectory }} 60 | 61 | - name: Install nuget validator 62 | run: dotnet tool update Meziantou.Framework.NuGetPackageValidation.Tool --global 63 | 64 | # Validate metadata and content of the NuGet package 65 | # https://www.nuget.org/packages/Meziantou.Framework.NuGetPackageValidation.Tool#readme-body-tab 66 | # If some rules are not applicable, you can disable them 67 | # using the --excluded-rules or --excluded-rule-ids option 68 | - name: Validate package 69 | run: meziantou.validate-nuget-package (Get-ChildItem "${{ env.NuGetDirectory }}/*.nupkg") --excluded-rules=IconMustBeSet,ProjectUrlMustBeSet,Symbols 70 | 71 | # run_test: 72 | # runs-on: ubuntu-latest 73 | # steps: 74 | # - uses: actions/checkout@v3 75 | # - name: Setup .NET 76 | # uses: actions/setup-dotnet@v3 77 | # - name: Run tests 78 | # run: dotnet test --configuration Release -------------------------------------------------------------------------------- /.github/workflows/dotnet-publish.yml: -------------------------------------------------------------------------------- 1 | # yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json 2 | 3 | name: dotnet-publish 4 | 5 | on: 6 | push: 7 | tags: 8 | - "v*" 9 | 10 | env: 11 | DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 12 | DOTNET_NOLOGO: true 13 | NuGetDirectory: ${{ github.workspace }}/dotnet/nuget 14 | 15 | defaults: 16 | run: 17 | working-directory: ./dotnet 18 | shell: pwsh 19 | 20 | jobs: 21 | create_nuget: 22 | runs-on: ubuntu-latest 23 | steps: 24 | - uses: actions/checkout@v3 25 | with: 26 | fetch-depth: 0 # Get all history to allow automatic versioning using MinVer 27 | 28 | # Install the .NET SDK indicated in the global.json file 29 | - name: Setup .NET 30 | uses: actions/setup-dotnet@v3 31 | 32 | # Create the NuGet package in the folder from the environment variable NuGetDirectory 33 | - run: dotnet pack --configuration Release --output ${{ env.NuGetDirectory }} 34 | 35 | # Publish the NuGet package as an artifact, so they can be used in the following jobs 36 | - uses: actions/upload-artifact@v3 37 | with: 38 | name: nuget 39 | if-no-files-found: error 40 | retention-days: 7 41 | path: ${{ env.NuGetDirectory }}/*.nupkg 42 | 43 | validate_nuget: 44 | runs-on: ubuntu-latest 45 | needs: [ create_nuget ] 46 | steps: 47 | # Install the .NET SDK indicated in the global.json file 48 | - name: Setup .NET 49 | uses: actions/setup-dotnet@v3 50 | 51 | # Download the NuGet package created in the previous job 52 | - uses: actions/download-artifact@v3 53 | with: 54 | name: nuget 55 | path: ${{ env.NuGetDirectory }} 56 | 57 | - name: Install nuget validator 58 | run: dotnet tool update Meziantou.Framework.NuGetPackageValidation.Tool --global 59 | 60 | # Validate metadata and content of the NuGet package 61 | # https://www.nuget.org/packages/Meziantou.Framework.NuGetPackageValidation.Tool#readme-body-tab 62 | # If some rules are not applicable, you can disable them 63 | # using the --excluded-rules or --excluded-rule-ids option 64 | - name: Validate package 65 | run: meziantou.validate-nuget-package (Get-ChildItem "${{ env.NuGetDirectory }}/*.nupkg") --excluded-rules=IconMustBeSet,ProjectUrlMustBeSet,Symbols 66 | 67 | # run_test: 68 | # runs-on: ubuntu-latest 69 | # steps: 70 | # - uses: actions/checkout@v3 71 | # - name: Setup .NET 72 | # uses: actions/setup-dotnet@v3 73 | # - name: Run tests 74 | # run: dotnet test --configuration Release 75 | 76 | deploy: 77 | # Publish only when a tag is pushed 78 | # https://docs.github.com/en/repositories/releasing-projects-on-github/managing-releases-in-a-repository 79 | # You can update this logic if you want to manage releases differently 80 | runs-on: ubuntu-latest 81 | needs: [ validate_nuget ] #run_test 82 | steps: 83 | # Download the NuGet package created in the previous job 84 | - uses: actions/download-artifact@v3 85 | with: 86 | name: nuget 87 | path: ${{ env.NuGetDirectory }} 88 | 89 | # Install the .NET SDK indicated in the global.json file 90 | - name: Setup .NET Core 91 | uses: actions/setup-dotnet@v3 92 | 93 | # Publish all NuGet packages to NuGet.org 94 | # Use --skip-duplicate to prevent errors if a package with the same version already exists. 95 | # If you retry a failed workflow, already published packages will be skipped without error. 96 | - name: Publish NuGet package 97 | run: | 98 | foreach($file in (Get-ChildItem "${{ env.NuGetDirectory }}" -Recurse -Include *.nupkg)) { 99 | dotnet nuget push $file --api-key "${{ secrets.NUGET_APIKEY }}" --source https://api.nuget.org/v3/index.json --skip-duplicate 100 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Mac desktop service store files 2 | .DS_Store -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 Devashish Lal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeBlaze's Semantic Kernel Plugins 2 | 3 |

4 | 5 | GitHub Workflow Status (with event) 6 | 7 | 8 | GitHub 9 | 10 | 11 | YouTube Channel Subscribers 12 | 13 |

14 | 15 | | Plugin | Dotnet | Python | Java | 16 | |---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------|------| 17 | | Ollama Connector | Nuget | | | 18 | | Neo4J Memory | Nuget | | | 19 | | Neo4j Cypher Plugin | Nuget | | | 20 | -------------------------------------------------------------------------------- /dotnet/.dockerignore: -------------------------------------------------------------------------------- 1 | **/.dockerignore 2 | **/.env 3 | **/.git 4 | **/.gitignore 5 | **/.project 6 | **/.settings 7 | **/.toolstarget 8 | **/.vs 9 | **/.vscode 10 | **/.idea 11 | **/*.*proj.user 12 | **/*.dbmdl 13 | **/*.jfm 14 | **/azds.yaml 15 | **/bin 16 | **/charts 17 | **/docker-compose* 18 | **/Dockerfile* 19 | **/node_modules 20 | **/npm-debug.log 21 | **/obj 22 | **/secrets.dev.yaml 23 | **/values.dev.yaml 24 | LICENSE 25 | README.md -------------------------------------------------------------------------------- /dotnet/.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | .vs/ 9 | *.VC.db 10 | 11 | # Build results 12 | [Aa]rtifacts/ 13 | [Dd]ebug/ 14 | [Rr]elease/ 15 | x64/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | .dotnet/ 19 | .tools/ 20 | src/Templates/**/ModifiedTemplates/ 21 | .packages/ 22 | 23 | # Per-user project properties 24 | launchSettings.json 25 | 26 | *_i.c 27 | *_p.c 28 | *.ilk 29 | *.meta 30 | *.obj 31 | *.pch 32 | *.pdb 33 | *.pgc 34 | *.pgd 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.wrn 43 | *.vspscc 44 | *.vssscc 45 | .builds 46 | *.pidb 47 | *.log 48 | *.scc 49 | 50 | # Visual C++ cache files 51 | ipch/ 52 | *.aps 53 | *.ncb 54 | *.opensdf 55 | *.sdf 56 | *.cachefile 57 | *.VC.opendb 58 | 59 | # Visual Studio profiler 60 | *.psess 61 | *.vsp 62 | *.vspx 63 | 64 | # Guidance Automation Toolkit 65 | *.gpState 66 | 67 | # ReSharper is a .NET coding add-in 68 | _ReSharper*/ 69 | *.[Rr]e[Ss]harper 70 | 71 | # TeamCity is a build add-in 72 | _TeamCity* 73 | 74 | # DotCover is a Code Coverage Tool 75 | *.dotCover 76 | 77 | # NCrunch 78 | *.ncrunch* 79 | .*crunch*.local.xml 80 | 81 | # Others 82 | sql/ 83 | *.Cache 84 | ClientBin/ 85 | [Ss]tyle[Cc]op.* 86 | ~$* 87 | *~ 88 | *.dbmdl 89 | *.[Pp]ublish.xml 90 | *.pfx 91 | *.publishsettings 92 | 93 | # SQL Server files 94 | App_Data/*.mdf 95 | App_Data/*.ldf 96 | 97 | # ========================= 98 | # Windows detritus 99 | # ========================= 100 | 101 | # Windows image file caches 102 | Thumbs.db 103 | ehthumbs.db 104 | 105 | # Folder config file 106 | Desktop.ini 107 | 108 | # Recycle Bin used on file shares 109 | $RECYCLE.BIN/ 110 | 111 | # Mac desktop service store files 112 | .DS_Store -------------------------------------------------------------------------------- /dotnet/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /modules.xml 6 | /contentModel.xml 7 | /projectSettingsUpdater.xml 8 | /.idea.Codeblaze.SemanticKernel.iml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /dotnet/.idea/.idea.Codeblaze.SemanticKernel/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /projectSettingsUpdater.xml 6 | /modules.xml 7 | /.idea.Codeblaze.SemanticKernel.iml 8 | /contentModel.xml 9 | # Editor-based HTTP Client requests 10 | /httpRequests/ 11 | # Datasource local storage ignored files 12 | /dataSources/ 13 | /dataSources.local.xml 14 | -------------------------------------------------------------------------------- /dotnet/.idea/.idea.Codeblaze.SemanticKernel/.idea/.name: -------------------------------------------------------------------------------- 1 | Codeblaze.SemanticKernel -------------------------------------------------------------------------------- /dotnet/.idea/.idea.Codeblaze.SemanticKernel/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /dotnet/.idea/.idea.Codeblaze.SemanticKernel/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /dotnet/.idea/.idea.Codeblaze.SemanticKernel/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /dotnet/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /dotnet/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /dotnet/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Api/Codeblaze.SemanticKernel.Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0 5 | enable 6 | enable 7 | true 8 | Linux 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | .dockerignore 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Api/Codeblaze.SemanticKernel.Api.http: -------------------------------------------------------------------------------- 1 | @Codeblaze.SemanticKernel.Api_HostAddress = http://localhost:5144 2 | 3 | GET {{Codeblaze.SemanticKernel.Api_HostAddress}}/weatherforecast/ 4 | Accept: application/json 5 | 6 | ### 7 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Api/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base 2 | USER $APP_UID 3 | WORKDIR /app 4 | EXPOSE 8080 5 | EXPOSE 8081 6 | 7 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build 8 | ARG BUILD_CONFIGURATION=Release 9 | WORKDIR /src 10 | COPY ["Codeblaze.SemanticKernel.Api/Codeblaze.SemanticKernel.Api.csproj", "Codeblaze.SemanticKernel.Api/"] 11 | RUN dotnet restore "Codeblaze.SemanticKernel.Api/Codeblaze.SemanticKernel.Api.csproj" 12 | COPY . . 13 | WORKDIR "/src/Codeblaze.SemanticKernel.Api" 14 | RUN dotnet build "Codeblaze.SemanticKernel.Api.csproj" -c $BUILD_CONFIGURATION -o /app/build 15 | 16 | FROM build AS publish 17 | ARG BUILD_CONFIGURATION=Release 18 | RUN dotnet publish "Codeblaze.SemanticKernel.Api.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false 19 | 20 | FROM base AS final 21 | WORKDIR /app 22 | COPY --from=publish /app/publish . 23 | ENTRYPOINT ["dotnet", "Codeblaze.SemanticKernel.Api.dll"] 24 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Api/Program.cs: -------------------------------------------------------------------------------- 1 | var builder = WebApplication.CreateBuilder(args); 2 | 3 | // Add services to the container. 4 | // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle 5 | builder.Services.AddEndpointsApiExplorer(); 6 | builder.Services.AddSwaggerGen(); 7 | 8 | var app = builder.Build(); 9 | 10 | // Configure the HTTP request pipeline. 11 | if (app.Environment.IsDevelopment()) 12 | { 13 | app.UseSwagger(); 14 | app.UseSwaggerUI(); 15 | } 16 | 17 | app.UseHttpsRedirection(); 18 | 19 | var summaries = new[] 20 | { 21 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 22 | }; 23 | 24 | app.MapGet("/weatherforecast", () => 25 | { 26 | var forecast = Enumerable.Range(1, 5).Select(index => 27 | new WeatherForecast 28 | ( 29 | DateOnly.FromDateTime(DateTime.Now.AddDays(index)), 30 | Random.Shared.Next(-20, 55), 31 | summaries[Random.Shared.Next(summaries.Length)] 32 | )) 33 | .ToArray(); 34 | return forecast; 35 | }) 36 | .WithName("GetWeatherForecast") 37 | .WithOpenApi(); 38 | 39 | app.Run(); 40 | 41 | record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) 42 | { 43 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); 44 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Api/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Warning" 6 | } 7 | }, 8 | "AllowedHosts": "*" 9 | } 10 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Memory.Neo4j/Codeblaze.SemanticKernel.Connectors.Memory.Neo4j.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | Latest 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | 23 | 24 | 25 | Codeblaze.SemanticKernel.Connectors.Memory.Neo4j 26 | Codeblaze (Devashish Lal) 27 | SemanticKernel;Neo4j;Memory;Embeddings;AI 28 | 29 | This package provides a Neo4j Memory store for semantic kernel capable of storing and retriving vector embeddings 30 | 31 | https://github.com/BLaZeKiLL/Codeblaze.SemanticKernel 32 | git 33 | README.md 34 | LICENSE.md 35 | v 36 | 37 | 38 | 39 | true 40 | true 41 | 42 | 43 | 44 | $(NoWarn);1591 45 | bin/$(Configuration)/$(TargetFramework)/$(AssemblyName).xml 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Memory.Neo4j/INeo4jQueryFactory.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SemanticKernel; 2 | 3 | namespace Codeblaze.SemanticKernel.Connectors.Memory.Neo4j; 4 | 5 | public interface INeo4jQueryFactory 6 | { 7 | public IDictionary DynamicProperties { get; } 8 | public string IndexName { get; } 9 | public string NodeName { get; } 10 | public string TextProperty { get; } 11 | public string IndexProperty { get; } 12 | public int Dimensions { get; } 13 | (string, object) ListIndexQuery(); 14 | (string, object) CreateIndexQuery(string collectionName); 15 | (string, object) DropIndexQuery(string collectionName); 16 | (string, object) UpsertQuery(string collectionName, int key, ReadOnlyMemory embedding); 17 | (string, object) UpsertBatchQuery(string collectionName, IEnumerable keys, 18 | IEnumerable> embeddings); 19 | (string, object) GetQuery(string collectionName, int keys); 20 | (string, object) GetBatchQuery(string collectionName, IEnumerable keys); 21 | (string, object) RemoveQuery(string collectionName, int keys); 22 | (string, object) RemoveBatchQuery(string collectionName, IEnumerable keys); 23 | (string, object) GetNearestMatchQuery(string collectionName, ReadOnlyMemory embedding, 24 | double minRelevanceScore, int limit = 1); 25 | } 26 | 27 | public abstract class Neo4jVectorQueries 28 | { 29 | public const string ListIndexQuery = "SHOW VECTOR INDEXES YIELD name"; 30 | 31 | public const string CreateIndexQuery = """ 32 | CREATE VECTOR INDEX `$name` 33 | FOR (n:$node) ON (n.$indexProperty) 34 | OPTIONS {indexConfig: { 35 | `vector.dimensions`: $dimensions, 36 | `vector.similarity_function`: 'cosine' 37 | } 38 | """; 39 | 40 | public const string DropIndexQuery = "DROP INDEX $name"; 41 | 42 | public const string GetNearestMatchQuery = """ 43 | CALL db.index.vector.queryNodes($name, $limit, $embedding) 44 | YIELD node, score 45 | WHERE score >= $minRelevanceScore 46 | RETURN node.id AS id, score 47 | """; 48 | 49 | public const string UpsertQuery = """ 50 | MATCH (n {id: $id}) 51 | CALL db.create.setNodeVectorProperty(n, $indexProperty, $embedding) 52 | RETURN node.id AS id 53 | """; 54 | 55 | public const string UpsertBatchQuery = """ 56 | UNWIND $updates AS update, 57 | MATCH (n {id: update.id}) 58 | CALL db.create.setNodeVectorProperty(n, $indexProperty, update.embedding) 59 | RETURN node.id AS id 60 | """; 61 | 62 | public const string GetQuery = """ 63 | MATCH (n {id: $id}) 64 | RETURN n 65 | """; 66 | 67 | public const string GetBatchQuery = """ 68 | UNWIND $ids AS id 69 | MATCH (n {id: id}) 70 | RETURN n 71 | """; 72 | 73 | public const string RemoveQuery = """ 74 | MATCH (n {id: $id}) 75 | DELETE n 76 | """; 77 | 78 | public const string RemoveBatchQuery = """ 79 | UNWIND $ids AS id 80 | MATCH (n {id: id}) 81 | DELETE n 82 | """; 83 | } 84 | 85 | /// 86 | /// Neo4j 5.15 or above is supported by this query factory, vector functionality was introduced in an earlier version of neo4j 87 | /// If you are using an old version you may need to implement some of the queries below using old cypher, refer neo4j docs 88 | /// 89 | /// Index name 90 | /// Node on which index is created 91 | /// Property of the node on which index is created 92 | /// Vector index dimensions 93 | public class Neo4jVectorIndexQueryFactory(string name, string node, string indexProperty, string textProperty, int dimensions) 94 | : INeo4jQueryFactory 95 | { 96 | /// 97 | /// This can be updated at runtime to pass different values to the query factory 98 | /// To use dynamic properties the default query methods need to be overriden 99 | /// and the dynamic property must be returned along with the new query 100 | /// 101 | public IDictionary DynamicProperties { get; } = new Dictionary(); 102 | public string IndexName => name; 103 | public string NodeName => node; 104 | public string TextProperty => textProperty; 105 | public string IndexProperty => indexProperty; 106 | public int Dimensions => dimensions; 107 | 108 | public virtual (string, object) ListIndexQuery() 109 | { 110 | return (Neo4jVectorQueries.ListIndexQuery, null); 111 | } 112 | 113 | public virtual (string, object) CreateIndexQuery(string collectionName) 114 | { 115 | if (name != collectionName) 116 | throw new KernelException( 117 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 118 | 119 | return (Neo4jVectorQueries.CreateIndexQuery, new 120 | { 121 | name, node, indexProperty, dimensions 122 | } 123 | ); 124 | } 125 | 126 | public virtual (string, object) DropIndexQuery(string collectionName) 127 | { 128 | if (name != collectionName) 129 | throw new KernelException( 130 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 131 | 132 | return (Neo4jVectorQueries.DropIndexQuery, new { name }); 133 | } 134 | 135 | public (string, object) UpsertQuery(string collectionName, int key, ReadOnlyMemory embedding) 136 | { 137 | if (name != collectionName) 138 | throw new KernelException( 139 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 140 | 141 | return (Neo4jVectorQueries.UpsertQuery, new { id = key, embedding }); 142 | } 143 | 144 | public (string, object) UpsertBatchQuery(string collectionName, IEnumerable keys, IEnumerable> embeddings) 145 | { 146 | if (name != collectionName) 147 | throw new KernelException( 148 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 149 | 150 | var updates = keys.Zip(embeddings, (id, embedding) => new { id, embedding }).ToArray(); 151 | 152 | return (Neo4jVectorQueries.UpsertBatchQuery, new { updates }); 153 | } 154 | 155 | public virtual (string, object) GetQuery(string collectionName, int key) 156 | { 157 | if (name != collectionName) 158 | throw new KernelException( 159 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 160 | 161 | return (Neo4jVectorQueries.GetQuery, new { id = key }); 162 | } 163 | 164 | public virtual (string, object) GetBatchQuery(string collectionName, IEnumerable keys) 165 | { 166 | if (name != collectionName) 167 | throw new KernelException( 168 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 169 | 170 | return (Neo4jVectorQueries.GetBatchQuery, new { ids = keys.ToArray() }); 171 | } 172 | 173 | public virtual (string, object) RemoveQuery(string collectionName, int key) 174 | { 175 | if (name != collectionName) 176 | throw new KernelException( 177 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 178 | 179 | return (Neo4jVectorQueries.RemoveQuery, new { id = key }); 180 | } 181 | 182 | public virtual (string, object) RemoveBatchQuery(string collectionName, IEnumerable keys) 183 | { 184 | if (name != collectionName) 185 | throw new KernelException( 186 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 187 | 188 | return (Neo4jVectorQueries.RemoveBatchQuery, new { ids = keys }); 189 | } 190 | 191 | public virtual (string, object) GetNearestMatchQuery(string collectionName, ReadOnlyMemory embedding, 192 | double minRelevanceScore, int limit = 1) 193 | { 194 | if (name != collectionName) 195 | throw new KernelException( 196 | $"Kernel passed {collectionName} index but query factory is configured with {name} index"); 197 | 198 | return (Neo4jVectorQueries.GetNearestMatchQuery, new 199 | { 200 | name, 201 | limit, 202 | embedding = embedding.ToArray(), 203 | minRelevanceScore 204 | }); 205 | } 206 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Memory.Neo4j/Neo4jMemoryBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SemanticKernel.Memory; 2 | 3 | namespace Codeblaze.SemanticKernel.Connectors.Memory.Neo4j; 4 | 5 | #pragma warning disable SKEXP0003 6 | public static class Neo4jMemoryBuilderExtension 7 | { 8 | public static MemoryBuilder WithNeo4jMemoryStore(this MemoryBuilder builder, string url, string username, string password, INeo4jQueryFactory queryFactory) 9 | { 10 | builder.WithMemoryStore(_ => new Neo4jMemoryStore(url, username, password, queryFactory)); 11 | 12 | return builder; 13 | } 14 | } 15 | #pragma warning enable SKEXP0003 -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Memory.Neo4j/Neo4jMemoryStore.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SemanticKernel.Memory; 2 | using Neo4j.Driver; 3 | 4 | namespace Codeblaze.SemanticKernel.Connectors.Memory.Neo4j; 5 | 6 | /// 7 | /// Collection in Neo4j context refers to index 8 | /// 9 | #pragma warning disable SKEXP0003 10 | public class Neo4jMemoryStore : IMemoryStore, IDisposable 11 | { 12 | private readonly IDriver _driver; 13 | private readonly INeo4jQueryFactory _queryFactory; 14 | 15 | public Neo4jMemoryStore(string url, string username, string password, INeo4jQueryFactory queryFactory) 16 | { 17 | _driver = GraphDatabase.Driver(url, AuthTokens.Basic(username, password)); 18 | _queryFactory = queryFactory; 19 | } 20 | 21 | public async Task CreateCollectionAsync(string collectionName, 22 | CancellationToken cancellationToken = new CancellationToken()) 23 | { 24 | if (await DoesCollectionExistAsync(collectionName, cancellationToken)) return; 25 | 26 | var (query, props) = _queryFactory.CreateIndexQuery(collectionName); 27 | 28 | await using var session = _driver.AsyncSession(); 29 | 30 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 31 | 32 | await cursor.ConsumeAsync().ConfigureAwait(false); 33 | } 34 | 35 | public async IAsyncEnumerable GetCollectionsAsync( 36 | CancellationToken cancellationToken = new CancellationToken()) 37 | { 38 | var indexes = await GetIndexesAsync().ConfigureAwait(false); 39 | 40 | foreach (var record in indexes) 41 | { 42 | yield return record.As(); 43 | } 44 | } 45 | 46 | public async Task DoesCollectionExistAsync(string collectionName, 47 | CancellationToken cancellationToken = new CancellationToken()) 48 | { 49 | var indexes = await GetIndexesAsync().ConfigureAwait(false); 50 | 51 | return indexes.Contains(collectionName); 52 | } 53 | 54 | public async Task DeleteCollectionAsync(string collectionName, 55 | CancellationToken cancellationToken = new CancellationToken()) 56 | { 57 | if (await DoesCollectionExistAsync(collectionName, cancellationToken)) return; 58 | 59 | var (query, props) = _queryFactory.DropIndexQuery(collectionName); 60 | 61 | await using var session = _driver.AsyncSession(); 62 | 63 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 64 | 65 | await cursor.ConsumeAsync().ConfigureAwait(false); 66 | } 67 | 68 | public async Task UpsertAsync(string collectionName, MemoryRecord record, 69 | CancellationToken cancellationToken = new CancellationToken()) 70 | { 71 | var (query, props) = _queryFactory.UpsertQuery(collectionName, int.Parse(record.Key), record.Embedding); 72 | 73 | await using var session = _driver.AsyncSession(); 74 | 75 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 76 | 77 | var result = await cursor.SingleAsync().ConfigureAwait(false); 78 | 79 | return result["id"].As(); 80 | } 81 | 82 | public async IAsyncEnumerable UpsertBatchAsync(string collectionName, IEnumerable records, 83 | CancellationToken cancellationToken = new CancellationToken()) 84 | { 85 | var (query, props) = _queryFactory.UpsertBatchQuery( 86 | collectionName, 87 | records.Select(x => int.Parse(x.Key)), 88 | records.Select(x => x.Embedding) 89 | ); 90 | 91 | await using var session = _driver.AsyncSession(); 92 | 93 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 94 | 95 | var results = await cursor.ToListAsync(cancellationToken: cancellationToken).ConfigureAwait(false); 96 | 97 | foreach (var result in results) 98 | { 99 | yield return result["id"].As(); 100 | } 101 | } 102 | 103 | public async Task GetAsync(string collectionName, string key, bool withEmbedding = false, 104 | CancellationToken cancellationToken = new CancellationToken()) 105 | { 106 | var (query, props) = _queryFactory.GetQuery(collectionName, int.Parse(key)); 107 | 108 | await using var session = _driver.AsyncSession(); 109 | 110 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 111 | 112 | var record = await cursor.SingleAsync().ConfigureAwait(false); 113 | 114 | var node = record["n"].As(); 115 | 116 | var id = node["id"].As(); 117 | var embedding = withEmbedding 118 | ? new ReadOnlyMemory(node[_queryFactory.IndexProperty].As()) 119 | : ReadOnlyMemory.Empty; 120 | 121 | return new MemoryRecord( 122 | new MemoryRecordMetadata( 123 | true, 124 | id, 125 | node[_queryFactory.TextProperty].As(), 126 | "", "neo4j", 127 | "" 128 | ), 129 | embedding, 130 | id 131 | ); 132 | } 133 | 134 | public async IAsyncEnumerable GetBatchAsync(string collectionName, IEnumerable keys, 135 | bool withEmbeddings = false, 136 | CancellationToken cancellationToken = new CancellationToken()) 137 | { 138 | var (query, props) = _queryFactory.GetBatchQuery(collectionName, keys.Select(int.Parse)); 139 | 140 | await using var session = _driver.AsyncSession(); 141 | 142 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 143 | 144 | var result = await cursor.ToListAsync(cancellationToken: cancellationToken).ConfigureAwait(false); 145 | 146 | foreach (var record in result) 147 | { 148 | var node = record["n"].As(); 149 | 150 | var id = node["id"].As(); 151 | var embedding = withEmbeddings 152 | ? new ReadOnlyMemory(node[_queryFactory.IndexProperty].As()) 153 | : ReadOnlyMemory.Empty; 154 | 155 | yield return new MemoryRecord( 156 | new MemoryRecordMetadata( 157 | true, 158 | id, 159 | node[_queryFactory.TextProperty].As(), 160 | "", "neo4j", 161 | "" 162 | ), 163 | embedding, 164 | id 165 | ); 166 | } 167 | } 168 | 169 | public async Task RemoveAsync(string collectionName, string key, 170 | CancellationToken cancellationToken = new CancellationToken()) 171 | { 172 | var (query, props) = _queryFactory.RemoveQuery(collectionName, int.Parse(key)); 173 | 174 | await using var session = _driver.AsyncSession(); 175 | 176 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 177 | 178 | await cursor.ConsumeAsync(); 179 | } 180 | 181 | public async Task RemoveBatchAsync(string collectionName, IEnumerable keys, 182 | CancellationToken cancellationToken = new CancellationToken()) 183 | { 184 | var (query, props) = _queryFactory.RemoveBatchQuery(collectionName, keys.Select(int.Parse)); 185 | 186 | await using var session = _driver.AsyncSession(); 187 | 188 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 189 | 190 | await cursor.ConsumeAsync(); 191 | } 192 | 193 | public async IAsyncEnumerable<(MemoryRecord, double)> GetNearestMatchesAsync(string collectionName, 194 | ReadOnlyMemory embedding, int limit, 195 | double minRelevanceScore = 0, bool withEmbeddings = false, 196 | CancellationToken cancellationToken = new CancellationToken()) 197 | { 198 | var (query, props) = _queryFactory.GetNearestMatchQuery(collectionName, embedding, minRelevanceScore, limit); 199 | 200 | await using var session = _driver.AsyncSession(); 201 | 202 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 203 | 204 | // Should use Async Linq and stream 205 | var result = await cursor.ToListAsync(cancellationToken: cancellationToken).ConfigureAwait(false); 206 | 207 | foreach (var record in result) 208 | { 209 | var score = record["score"].As(); 210 | var id = record["id"].As(); 211 | 212 | yield return ( 213 | new MemoryRecord( 214 | new MemoryRecordMetadata(true, id, "", "", "neo4j", ""), embedding, id 215 | ), 216 | score); 217 | } 218 | } 219 | 220 | public async Task<(MemoryRecord, double)?> GetNearestMatchAsync(string collectionName, 221 | ReadOnlyMemory embedding, 222 | double minRelevanceScore = 0, 223 | bool withEmbedding = false, CancellationToken cancellationToken = new CancellationToken()) 224 | { 225 | var (query, props) = _queryFactory.GetNearestMatchQuery(collectionName, embedding, minRelevanceScore); 226 | 227 | await using var session = _driver.AsyncSession(); 228 | 229 | var cursor = await session.RunAsync(query, props).ConfigureAwait(false); 230 | 231 | var result = await cursor.SingleAsync().ConfigureAwait(false); 232 | 233 | var score = result["score"].As(); 234 | var id = result["id"].As(); 235 | 236 | return (new MemoryRecord( 237 | new MemoryRecordMetadata( 238 | true, id, "", "", "neo4j", ""), 239 | embedding, 240 | id), 241 | score 242 | ); 243 | } 244 | 245 | public void Dispose() 246 | { 247 | _driver.Dispose(); 248 | } 249 | 250 | private async Task> GetIndexesAsync() 251 | { 252 | var (query, _) = _queryFactory.ListIndexQuery(); 253 | 254 | await using var session = _driver.AsyncSession(); 255 | 256 | var cursor = await session.RunAsync(query).ConfigureAwait(false); 257 | 258 | return await cursor.ToListAsync(x => x.As()).ConfigureAwait(false); 259 | } 260 | } 261 | #pragma warning enable SKEXP0003 -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Memory.Neo4j/README.md: -------------------------------------------------------------------------------- 1 | # Neo4j Memory Store 2 | Supports 3 | - Customizable queries 4 | - Runtime properties 5 | - Vector embedding Update, Get, Remove and Search 6 | - TextMemoryPlugin support see [example 15](https://github.com/microsoft/semantic-kernel/blob/main/dotnet/samples/KernelSyntaxExamples/Example15_TextMemoryPlugin.cs) of Semantic Kernel 7 | 8 | > :warning: **Kernel Memory**: Is experimental in the semantic kernel. 9 | 10 | ### Quick Start 11 | - Install from [nuget](https://www.nuget.org/packages/Codeblaze.SemanticKernel.Connectors.Memory.Neo4j) 12 | ``` 13 | dotnet add package Codeblaze.SemanticKernel.Connectors.Memory.Neo4j 14 | ``` 15 | - Configure the memory store 16 | ```csharp 17 | var builder = new MemoryBuilder(); 18 | 19 | builder.WithHttpClient(new HttpClient()); 20 | builder.WithOllamaTextEmbeddingGeneration(config["Ollama:Model"], config["Ollama:BaseUrlEmbeddings"]); 21 | builder.WithNeo4jMemoryStore(config["Neo4j:Url"], config["Neo4j:Username"], config["Neo4j:Password"], 22 | new Neo4jVectorIndexQueryFactory( 23 | "", 24 | "", 25 | "", 26 | "", 27 | 384 // Dimensions 28 | ) 29 | ); 30 | 31 | memory = builder.Build(); 32 | ``` 33 | - Store embedding 34 | ```csharp 35 | memory.SaveReferenceAsync( 36 | collection: "", 37 | externalSourceName: "", 38 | externalId: "", 39 | description: "not used", 40 | text: "" 41 | ); 42 | ``` 43 | - Search embedding 44 | ```csharp 45 | var results = memory.SearchAsync("", "", limit: 3); 46 | ``` 47 | - Retrieve node 48 | ```csharp 49 | var results = await memory.GetAsync("", ""); 50 | ``` 51 | 52 | ### Query Factory 53 | You can implement you own `INeo4jQueryFactory` or Extend `Neo4jVectorIndexQueryFactory` to modify 54 | any of the queries (Default queries found in `Neo4jVectorQueries`). -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/ChatCompletion/OllamaChatCompletionService.cs: -------------------------------------------------------------------------------- 1 | using Codeblaze.SemanticKernel.Connectors.Ollama.ChatCompletion; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.SemanticKernel; 4 | using Microsoft.SemanticKernel.ChatCompletion; 5 | using System.Net.Http.Json; 6 | using System.Runtime.CompilerServices; 7 | using System.Text.Json; 8 | 9 | namespace Codeblaze.SemanticKernel.Connectors.Ollama; 10 | 11 | public class OllamaChatCompletionService( 12 | string modelId, 13 | string baseUrl, 14 | HttpClient http, 15 | ILoggerFactory? loggerFactory) 16 | : OllamaBase(modelId, baseUrl, http, loggerFactory), IChatCompletionService 17 | { 18 | public async Task> GetChatMessageContentsAsync(ChatHistory chatHistory, PromptExecutionSettings? executionSettings = null, 19 | Kernel? kernel = null, CancellationToken cancellationToken = new()) 20 | { 21 | var chatExecutionSettings = OllamaPromptExecutionSettings.FromExecutionSettings(executionSettings); 22 | 23 | var data = new 24 | { 25 | model = Attributes["model_id"] as string, 26 | messages = GetChatMessages(chatHistory), 27 | stream = false, 28 | options = chatExecutionSettings 29 | }; 30 | 31 | var response = await Http.PostAsJsonAsync($"{Attributes["base_url"]}/api/chat", data, cancellationToken).ConfigureAwait(false); 32 | 33 | ValidateOllamaResponse(response); 34 | string jsonResponse = await response.Content.ReadAsStringAsync().ConfigureAwait(false); 35 | 36 | var chatResponseMessage = JsonSerializer.Deserialize(jsonResponse); 37 | 38 | return new List { chatResponseMessage!.ToChatMessageContent() }; 39 | } 40 | 41 | public async IAsyncEnumerable GetStreamingChatMessageContentsAsync(ChatHistory chatHistory, 42 | PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, 43 | [EnumeratorCancellation] CancellationToken cancellationToken = new()) 44 | { 45 | var chatExecutionSettings = OllamaPromptExecutionSettings.FromExecutionSettings(executionSettings); 46 | 47 | var data = new 48 | { 49 | model = Attributes["model_id"] as string, 50 | messages = GetChatMessages(chatHistory), 51 | stream = true, 52 | options = chatExecutionSettings 53 | }; 54 | 55 | var request = new HttpRequestMessage(HttpMethod.Post, $"{Attributes["base_url"]}/api/chat"); 56 | request.Content = JsonContent.Create(data); 57 | using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); 58 | 59 | ValidateOllamaResponse(response); 60 | 61 | using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); 62 | 63 | using var reader = new StreamReader(stream); 64 | 65 | var done = false; 66 | 67 | while (!done) 68 | { 69 | string jsonResponse = await reader.ReadLineAsync(); 70 | 71 | var chatResponseMessage = JsonSerializer.Deserialize(jsonResponse); 72 | done = chatResponseMessage!.Done; 73 | 74 | yield return chatResponseMessage!.ToStreamingChatMessageContent(); 75 | } 76 | } 77 | 78 | private IEnumerable GetChatMessages(ChatHistory chat) 79 | { 80 | foreach (var item in chat) 81 | { 82 | if (item.Role == AuthorRole.System) 83 | yield return new OllamaChatRequestSystemMessage(item.Content!); 84 | else if (item.Role == AuthorRole.User) 85 | yield return new OllamaChatRequestUserMessage(item.Content!); 86 | else if (item.Role == AuthorRole.Assistant) 87 | yield return new OllamaChatRequestAssistantMessage(item.Content!); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/ChatCompletion/OllamaChatRequestMessage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SemanticKernel; 2 | using Microsoft.SemanticKernel.ChatCompletion; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace Codeblaze.SemanticKernel.Connectors.Ollama.ChatCompletion 6 | { 7 | internal abstract class OllamaChatRequestMessage 8 | { 9 | /// 10 | /// The role of the author of the message either "system", "user", or "assistant". 11 | /// 12 | [JsonPropertyName("role")] 13 | public string Role { get; protected set; } = null!; 14 | 15 | /// 16 | ///The content of the message 17 | /// 18 | [JsonPropertyName("content")] 19 | public string Content { get; set; } = null!; 20 | 21 | internal ChatMessageContent ToChatMessageContent(string modelId = null!) 22 | { 23 | return new ChatMessageContent(new AuthorRole(Role), Content, modelId: modelId); 24 | } 25 | } 26 | 27 | internal sealed class OllamaChatRequestSystemMessage : OllamaChatRequestMessage 28 | { 29 | public OllamaChatRequestSystemMessage(string content) 30 | { 31 | Role = "system"; 32 | Content = content; 33 | } 34 | } 35 | 36 | internal sealed class OllamaChatRequestUserMessage : OllamaChatRequestMessage 37 | { 38 | public OllamaChatRequestUserMessage(string content) 39 | { 40 | Role = "user"; 41 | Content = content; 42 | } 43 | } 44 | 45 | internal sealed class OllamaChatRequestAssistantMessage : OllamaChatRequestMessage 46 | { 47 | public OllamaChatRequestAssistantMessage(string content) 48 | { 49 | Role = "assistant"; 50 | Content = content; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/ChatCompletion/OllamaChatResponseMessage.cs: -------------------------------------------------------------------------------- 1 | using Azure.AI.OpenAI; 2 | using Microsoft.SemanticKernel; 3 | using Microsoft.SemanticKernel.ChatCompletion; 4 | using System.Text.Json.Serialization; 5 | 6 | namespace Codeblaze.SemanticKernel.Connectors.Ollama.ChatCompletion 7 | { 8 | internal class OllamaChatResponseMessageContent 9 | { 10 | /// 11 | /// The role of the author of the message either "system", "user", or "assistant". 12 | /// 13 | [JsonPropertyName("role")] 14 | public string Role { get; set; } = null!; 15 | 16 | /// 17 | /// The message content 18 | /// 19 | [JsonPropertyName("content")] 20 | public string Content { get; set; } = null!; 21 | 22 | } 23 | internal class OllamaChatResponseMessage : OllamaResponseMessage 24 | { 25 | /// 26 | /// The message generated by the chat model. 27 | /// 28 | [JsonPropertyName("message")] 29 | public OllamaChatResponseMessageContent Message { get; set; } = null!; 30 | 31 | internal ChatMessageContent ToChatMessageContent() 32 | { 33 | var metadata = new Dictionary() { 34 | { "total_duration", TotalDuration }, 35 | { "load_duration", LoadDuration }, 36 | { "prompt_eval_count", PromptEvalCount }, 37 | { "prompt_eval_duration", PromptEvalDuration }, 38 | { "eval_count", EvalCount }, 39 | { "eval_duration", EvalDuration} 40 | }; 41 | 42 | return new ChatMessageContent( 43 | role: new AuthorRole(Message.Role), 44 | content: Message.Content, 45 | modelId: Model, 46 | metadata: metadata!); 47 | } 48 | 49 | internal StreamingChatMessageContent ToStreamingChatMessageContent() 50 | { 51 | var metadata = new Dictionary() { 52 | { "total_duration", TotalDuration }, 53 | { "load_duration", LoadDuration }, 54 | { "prompt_eval_count", PromptEvalCount }, 55 | { "prompt_eval_duration", PromptEvalDuration }, 56 | { "eval_count", EvalCount }, 57 | { "eval_duration", EvalDuration} 58 | }; 59 | 60 | return new StreamingChatMessageContent( 61 | role: new AuthorRole(Message.Role), 62 | content: Message.Content, 63 | modelId: Model, 64 | metadata: metadata!); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/Codeblaze.SemanticKernel.Connectors.Ollama.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | Latest 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | all 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | Codeblaze.SemanticKernel.Connectors.Ollama 27 | Codeblaze (Devashish Lal) 28 | SemanticKernel;Ollama;Chat;Embeddings;AI 29 | 30 | This package provides a Ollama AI connector for semantic kernel capable of text generation, chat completion and embedding generation with streaming support 31 | 32 | https://github.com/BLaZeKiLL/Codeblaze.SemanticKernel 33 | git 34 | README.md 35 | LICENSE.md 36 | v 37 | 38 | 39 | 40 | true 41 | true 42 | 43 | 44 | 45 | $(NoWarn);1591 46 | bin/$(Configuration)/$(TargetFramework)/$(AssemblyName).xml 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/Codeblaze.SemanticKernel.Connectors.Ollama.csproj.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True 4 | True -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/EmbeddingGeneration/OllamaTextEmbeddingGeneration.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | using System.Text.Json; 3 | using System.Text.Json.Nodes; 4 | using Microsoft.Extensions.Logging; 5 | using Microsoft.SemanticKernel; 6 | using Microsoft.SemanticKernel.Embeddings; 7 | 8 | namespace Codeblaze.SemanticKernel.Connectors.Ollama; 9 | 10 | #pragma warning disable SKEXP0001 11 | public class OllamaTextEmbeddingGeneration(string modelId, string baseUrl, HttpClient http, ILoggerFactory? loggerFactory) 12 | : OllamaBase(modelId, baseUrl, http, loggerFactory), 13 | ITextEmbeddingGenerationService 14 | #pragma warning restore SKEXP0001 15 | { 16 | public async Task>> GenerateEmbeddingsAsync(IList data, Kernel? kernel = null, 17 | CancellationToken cancellationToken = new()) 18 | { 19 | var result = new List>(data.Count); 20 | 21 | foreach (var text in data) 22 | { 23 | var request = new 24 | { 25 | model = Attributes["model_id"], 26 | prompt = text 27 | }; 28 | 29 | var response = await Http.PostAsJsonAsync($"{Attributes["base_url"]}/api/embeddings", request, cancellationToken).ConfigureAwait(false); 30 | 31 | ValidateOllamaResponse(response); 32 | 33 | var json = JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); 34 | 35 | var embedding = new ReadOnlyMemory(json!["embedding"]?.AsArray().GetValues().ToArray()); 36 | 37 | result.Add(embedding); 38 | } 39 | 40 | return result; 41 | } 42 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/OllamaBase.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Logging.Abstractions; 4 | 5 | namespace Codeblaze.SemanticKernel.Connectors.Ollama; 6 | 7 | public interface IOllamaBase 8 | { 9 | Task PingOllamaAsync(CancellationToken cancellationToken = new()); 10 | } 11 | 12 | public abstract class OllamaBase : IOllamaBase where T : OllamaBase 13 | { 14 | public IReadOnlyDictionary Attributes => _attributes; 15 | 16 | private readonly Dictionary _attributes = new(); 17 | 18 | protected readonly HttpClient Http; 19 | protected readonly ILogger Logger; 20 | 21 | protected OllamaBase(string modelId, string baseUrl, HttpClient http, ILoggerFactory? loggerFactory) 22 | { 23 | _attributes.Add("model_id", modelId); 24 | _attributes.Add("base_url", baseUrl); 25 | 26 | Http = http; 27 | Logger = loggerFactory is not null ? loggerFactory.CreateLogger() : NullLogger.Instance; 28 | } 29 | 30 | /// 31 | /// Ping Ollama instance to check if the required llm model is available at the instance 32 | /// 33 | /// 34 | public async Task PingOllamaAsync(CancellationToken cancellationToken = new()) 35 | { 36 | var data = new 37 | { 38 | name = Attributes["model_id"] 39 | }; 40 | 41 | var response = await Http.PostAsJsonAsync($"{Attributes["base_url"]}/api/show", data, cancellationToken).ConfigureAwait(false); 42 | 43 | ValidateOllamaResponse(response); 44 | 45 | Logger.LogInformation("Connected to Ollama at {url} with model {model}", Attributes["base_url"], Attributes["model_id"]); 46 | } 47 | 48 | protected void ValidateOllamaResponse(HttpResponseMessage? response) 49 | { 50 | try 51 | { 52 | response.EnsureSuccessStatusCode(); 53 | } 54 | catch (HttpRequestException) 55 | { 56 | Logger.LogError("Unable to connect to ollama at {url} with model {model}", Attributes["base_url"], Attributes["model_id"]); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/OllamaKernelBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.DependencyInjection; 2 | using Microsoft.Extensions.Logging; 3 | 4 | using Microsoft.SemanticKernel; 5 | using Microsoft.SemanticKernel.ChatCompletion; 6 | using Microsoft.SemanticKernel.Embeddings; 7 | using Microsoft.SemanticKernel.TextGeneration; 8 | 9 | namespace Codeblaze.SemanticKernel.Connectors.Ollama; 10 | 11 | public static class OllamaKernelBuilderExtensions 12 | { 13 | /// 14 | /// Adds Ollama as the text generation llm backend for semantic kernel 15 | /// 16 | /// kernel builder 17 | /// Ollama model ID to use 18 | /// Ollama base url 19 | /// 20 | /// 21 | public static IKernelBuilder AddOllamaTextGeneration( 22 | this IKernelBuilder builder, 23 | string modelId, 24 | string baseUrl, 25 | string? serviceId = null 26 | ) 27 | { 28 | var factory = (IServiceProvider provider, object? _) => new OllamaTextGenerationService( 29 | modelId, 30 | baseUrl, 31 | provider.GetRequiredService(), 32 | provider.GetService() 33 | ); 34 | 35 | builder.Services.AddKeyedSingleton(serviceId, factory); 36 | 37 | return builder; 38 | } 39 | 40 | /// 41 | /// Adds Ollama as the text generation llm backend for semantic kernel 42 | /// 43 | /// kernel builder 44 | /// Ollama model ID to use 45 | /// Ollama base url 46 | /// 47 | /// 48 | public static IKernelBuilder AddOllamaTextGeneration( 49 | this IKernelBuilder builder, 50 | string modelId, 51 | Uri baseUrl, 52 | string? serviceId = null 53 | ) 54 | { 55 | var factory = (IServiceProvider provider, object? _) => new OllamaTextGenerationService( 56 | modelId, 57 | baseUrl.AbsoluteUri, 58 | provider.GetRequiredService(), 59 | provider.GetService() 60 | ); 61 | 62 | builder.Services.AddKeyedSingleton(serviceId, factory); 63 | 64 | return builder; 65 | } 66 | 67 | /// 68 | /// Adds Ollama as the chat completion llm backend for semantic kernel 69 | /// 70 | /// kernel builder 71 | /// Ollama model ID to use 72 | /// Ollama base url 73 | /// 74 | /// 75 | public static IKernelBuilder AddOllamaChatCompletion( 76 | this IKernelBuilder builder, 77 | string modelId, 78 | string baseUrl, 79 | string? serviceId = null 80 | ) 81 | { 82 | var factory = (IServiceProvider provider, object? _) => new OllamaChatCompletionService( 83 | modelId, 84 | baseUrl, 85 | provider.GetRequiredService(), 86 | provider.GetService() 87 | ); 88 | 89 | builder.Services.AddKeyedSingleton(serviceId, factory); 90 | 91 | return builder; 92 | } 93 | 94 | /// 95 | /// Adds Ollama as the chat completion llm backend for semantic kernel 96 | /// 97 | /// kernel builder 98 | /// Ollama model ID to use 99 | /// Ollama base url 100 | /// 101 | /// 102 | public static IKernelBuilder AddOllamaChatCompletion( 103 | this IKernelBuilder builder, 104 | string modelId, 105 | Uri baseUrl, 106 | string? serviceId = null 107 | ) 108 | { 109 | var factory = (IServiceProvider provider, object? _) => new OllamaChatCompletionService( 110 | modelId, 111 | baseUrl.AbsoluteUri, 112 | provider.GetRequiredService(), 113 | provider.GetService() 114 | ); 115 | 116 | builder.Services.AddKeyedSingleton(serviceId, factory); 117 | 118 | return builder; 119 | } 120 | 121 | /// 122 | /// Adds Ollama as the text embedding generation backend for semantic kernel 123 | /// 124 | /// kernel builder 125 | /// Ollama model ID to use 126 | /// Ollama base url 127 | /// 128 | /// 129 | public static IKernelBuilder AddOllamaTextEmbeddingGeneration( 130 | this IKernelBuilder builder, 131 | string modelId, 132 | string baseUrl, 133 | string? serviceId = null 134 | ) 135 | { 136 | var factory = (IServiceProvider provider, object? _) => new OllamaTextEmbeddingGeneration( 137 | modelId, 138 | baseUrl, 139 | provider.GetRequiredService(), 140 | provider.GetService() 141 | ); 142 | 143 | #pragma warning disable SKEXP0001 144 | builder.Services.AddKeyedSingleton>(serviceId, factory); 145 | #pragma warning restore SKEXP0001 146 | return builder; 147 | } 148 | 149 | /// 150 | /// Adds Ollama as the text embedding generation backend for semantic kernel 151 | /// 152 | /// kernel builder 153 | /// Ollama model ID to use 154 | /// Ollama base url 155 | /// 156 | /// 157 | public static IKernelBuilder AddOllamaTextEmbeddingGeneration( 158 | this IKernelBuilder builder, 159 | string modelId, 160 | Uri baseUrl, 161 | string? serviceId = null 162 | ) 163 | { 164 | var factory = (IServiceProvider provider, object? _) => new OllamaTextEmbeddingGeneration( 165 | modelId, 166 | baseUrl.AbsoluteUri, 167 | provider.GetRequiredService(), 168 | provider.GetService() 169 | ); 170 | 171 | #pragma warning disable SKEXP0001 172 | builder.Services.AddKeyedSingleton>(serviceId, factory); 173 | #pragma warning restore SKEXP0001 174 | return builder; 175 | } 176 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/OllamaMemoryBuilderExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SemanticKernel.Memory; 2 | 3 | namespace Codeblaze.SemanticKernel.Connectors.Ollama; 4 | 5 | #pragma warning disable SKEXP0001 6 | public static class OllamaMemoryBuilderExtensions 7 | { 8 | /// 9 | /// Adds Ollama as the text embedding generation backend for semantic memory 10 | /// 11 | /// kernel builder 12 | /// Ollama model ID to use 13 | /// Ollama base url 14 | /// 15 | public static MemoryBuilder WithOllamaTextEmbeddingGeneration( 16 | this MemoryBuilder builder, 17 | string modelId, 18 | string baseUrl 19 | ) 20 | { 21 | builder.WithTextEmbeddingGeneration((logger, http) => new OllamaTextEmbeddingGeneration( 22 | modelId, 23 | baseUrl, 24 | http, 25 | logger 26 | )); 27 | 28 | return builder; 29 | } 30 | 31 | /// 32 | /// Adds Ollama as the text embedding generation backend for semantic memory 33 | /// 34 | /// kernel builder 35 | /// Ollama model ID to use 36 | /// Ollama base url 37 | /// 38 | public static MemoryBuilder WithOllamaTextEmbeddingGeneration( 39 | this MemoryBuilder builder, 40 | string modelId, 41 | Uri baseUrl 42 | ) 43 | { 44 | builder.WithTextEmbeddingGeneration((logger, http) => new OllamaTextEmbeddingGeneration( 45 | modelId, 46 | baseUrl.AbsoluteUri, 47 | http, 48 | logger 49 | )); 50 | 51 | return builder; 52 | } 53 | } 54 | #pragma warning enable SKEXP0001 -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/OllamaPromptExecutionSettings.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SemanticKernel; 2 | using System.Text.Json; 3 | using System.Text.Json.Serialization; 4 | 5 | namespace Codeblaze.SemanticKernel.Connectors.Ollama 6 | { 7 | [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)] 8 | public sealed class OllamaPromptExecutionSettings 9 | { 10 | /// 11 | /// Temperature controls the randomness of the completion. 12 | /// The higher the temperature, the more random the completion. 13 | /// Default is 1.0. 14 | /// 15 | [JsonPropertyName("temperature")] 16 | public double Temperature { get; set; } = .8; 17 | 18 | /// 19 | /// Works together with top-k. A higher value (e.g., 0.95) will lead to more diverse text, while a lower value (e.g., 0.5) will generate more focused and conservative text. 20 | /// Default: 0.9 21 | /// 22 | [JsonPropertyName("top_p")] 23 | public double TopP { get; set; } = .9; 24 | 25 | /// 26 | /// Reduces the probability of generating nonsense. A higher value (e.g. 100) will give more diverse answers, while a lower value (e.g. 10) will be more conservative. 27 | /// Default: 40 28 | /// 29 | [JsonPropertyName("top_k")] 30 | public int TopK { get; set; } = 40; 31 | 32 | /// 33 | /// Maximum number of tokens to predict when generating text. (Default: 128, -1 = infinite generation, -2 = fill context) 34 | /// 35 | [JsonPropertyName("num_predict")] 36 | public int? MaxTokens { get; set; } 37 | 38 | 39 | internal static OllamaPromptExecutionSettings FromExecutionSettings(PromptExecutionSettings? executionSettings, int? defaultMaxTokens = null) 40 | { 41 | if (executionSettings is null) 42 | { 43 | return new OllamaPromptExecutionSettings() 44 | { 45 | MaxTokens = defaultMaxTokens 46 | }; 47 | } 48 | 49 | var json = JsonSerializer.Serialize(executionSettings); 50 | 51 | var ollamaExecutionSettings = JsonSerializer.Deserialize(json, new JsonSerializerOptions 52 | { 53 | AllowTrailingCommas = true, 54 | PropertyNameCaseInsensitive = true, 55 | ReadCommentHandling = JsonCommentHandling.Skip 56 | }); 57 | 58 | if (ollamaExecutionSettings is not null) 59 | { 60 | return ollamaExecutionSettings; 61 | } 62 | 63 | throw new ArgumentException($"Invalid execution settings, cannot convert to {nameof(OllamaPromptExecutionSettings)}", nameof(executionSettings)); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/OllamaResponseMessage.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json.Serialization; 2 | 3 | namespace Codeblaze.SemanticKernel.Connectors.Ollama 4 | { 5 | internal class OllamaResponseMessage 6 | { 7 | /// 8 | /// The model used to generate the response. 9 | /// 10 | [JsonPropertyName("model")] 11 | public string Model { get; set; } = null!; 12 | 13 | /// 14 | /// The message created date. 15 | /// 16 | [JsonPropertyName("created_at")] 17 | public DateTime CreatedAt { get; set; } 18 | 19 | /// 20 | /// Value indicating whether the message is done. 21 | /// 22 | [JsonPropertyName("done")] 23 | public bool Done { get; set; } = false!; 24 | 25 | /// 26 | /// The time spent generating the response. 27 | /// 28 | [JsonPropertyName("total_duration")] 29 | public UInt64 TotalDuration { get; set; } = 0; 30 | 31 | /// 32 | /// The time spent in nanoseconds loading the model. 33 | /// 34 | [JsonPropertyName("load_duration")] 35 | public UInt64 LoadDuration { get; set; } = 0; 36 | 37 | /// 38 | /// The number of tokens in the prompt. 39 | /// 40 | [JsonPropertyName("generate_duration")] 41 | public int PromptEvalCount { get; set; } = 0; 42 | 43 | /// 44 | /// The time spent in nanoseconds evaluating the prompt. 45 | /// 46 | [JsonPropertyName("prompt_eval_count")] 47 | public UInt64 PromptEvalDuration { get; set; } = 0; 48 | 49 | /// 50 | /// The number of tokens the response. 51 | /// 52 | [JsonPropertyName("eval_count")] 53 | public int EvalCount { get; set; } = 0; 54 | 55 | /// 56 | /// The time in nanoseconds spent generating the response. 57 | /// 58 | [JsonPropertyName("eval_duration")] 59 | public UInt64 EvalDuration { get; set; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/README.md: -------------------------------------------------------------------------------- 1 | # Ollama Connector 2 | Supports 3 | - text generation 4 | - chat completion 5 | - embedding generation 6 | 7 | > :warning: **Embedding generation**: Is experimental in the semantic kernel. 8 | 9 | ### Quick Start 10 | - Install from [nuget](https://www.nuget.org/packages/Codeblaze.SemanticKernel.Connectors.Ollama) 11 | ``` 12 | dotnet add package Codeblaze.SemanticKernel.Connectors.Ollama 13 | ``` 14 | - Text Generation 15 | 16 | Configure the kernel 17 | ```csharp 18 | var builder = Kernel.CreateBuilder(); 19 | 20 | // provide the HTTP client used to interact with Ollama API 21 | builder.Services.AddTransient(); 22 | 23 | builder.AddOllamaTextGeneration( 24 | config["Ollama:Model"], // Ollama model Id 25 | config["Ollama:BaseUrlGeneration"] // Ollama endpoint 26 | ); 27 | 28 | var kernel = builder.Build(); 29 | ``` 30 | 31 | Usage 32 | ```csharp 33 | const string prompt = """ 34 | Bot: How can I help you? 35 | User: {{$input}} 36 | 37 | --------------------------------------------- 38 | 39 | The intent of the user in 5 words or less: 40 | """; 41 | 42 | var result = await kernel.InvokePromptAsync(prompt, new KernelArguments 43 | { 44 | {"input", input} 45 | }); 46 | 47 | System.Console.WriteLine(result.GetValue()); 48 | ``` 49 | 50 | - Chat Completion 51 | 52 | Configure the kernel 53 | ```csharp 54 | var builder = new KernelBuilder(); 55 | 56 | // provide the HTTP client used to interact with Ollama API 57 | builder.Services.AddTransient(); 58 | 59 | builder.AddOllamaChatCompletion( 60 | config["Ollama:Model"], // Ollama model Id 61 | config["Ollama:BaseUrlGeneration"] // Ollama endpoint 62 | ); 63 | 64 | var kernel = builder.Build(); 65 | ``` 66 | 67 | Usage 68 | ```csharp 69 | var chat = _Kernel.GetRequiredService(); 70 | 71 | var history = new ChatHistory(); 72 | 73 | // add messages to current chat history as required 74 | history.AddSystemMessage("..."); 75 | history.AddAssistantMessage("..."); 76 | history.AddUserMessage(input); 77 | 78 | // result is a list of all chat messages 79 | // including the output of current prompt 80 | var result = await chat.GetChatMessageContentsAsync(history); 81 | 82 | // Print the last message 83 | System.Console.WriteLine(result[^1].Content); 84 | ``` 85 | 86 | - Embedding Generation (Experimental) 87 | 88 | Configure the kernel 89 | ```csharp 90 | var builder = new KernelBuilder(); 91 | 92 | // provide the HTTP client used to interact with Ollama API 93 | builder.Services.AddTransient(); 94 | 95 | builder.AddOllamaTextEmbeddingGeneration( 96 | config["Ollama:Model"], // Ollama model Id 97 | config["Ollama:BaseUrlGeneration"] // Ollama endpoint 98 | ); 99 | 100 | // Configure memory backend (e.g Azure Cognitive Search) 101 | 102 | var kernel = builder.Build(); 103 | ``` 104 | 105 | Usage 106 | ```csharp 107 | var memory = _Kernel.GetRequiredService(); 108 | 109 | // This will internally call Ollama embedding service to generate embeddings 110 | memory.SaveReferenceAsync( 111 | collection: "collection", 112 | externalSourceName: "ext-collection", 113 | externalId: id, // reference id (database entity id) 114 | description: input, 115 | text: input 116 | ); 117 | ``` -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Connectors.Ollama/TextGenerationService/OllamaTextGenerationService.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http.Json; 2 | using System.Runtime.CompilerServices; 3 | using System.Text.Json; 4 | using System.Text.Json.Nodes; 5 | using Microsoft.Extensions.Logging; 6 | using Microsoft.SemanticKernel; 7 | using Microsoft.SemanticKernel.TextGeneration; 8 | 9 | namespace Codeblaze.SemanticKernel.Connectors.Ollama; 10 | 11 | public class OllamaTextGenerationService(string modelId, string baseUrl, HttpClient http, ILoggerFactory? loggerFactory) 12 | : OllamaBase(modelId, baseUrl, http, loggerFactory), ITextGenerationService 13 | { 14 | public async Task> GetTextContentsAsync(string prompt, 15 | PromptExecutionSettings? executionSettings = null, Kernel? kernel = null, 16 | CancellationToken cancellationToken = new()) 17 | { 18 | var data = new 19 | { 20 | model = Attributes["model_id"], 21 | prompt, 22 | stream = false, 23 | options = executionSettings?.ExtensionData, 24 | }; 25 | 26 | var response = await Http.PostAsJsonAsync($"{Attributes["base_url"]}/api/generate", data, cancellationToken).ConfigureAwait(false); 27 | 28 | ValidateOllamaResponse(response); 29 | 30 | var json = JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync().ConfigureAwait(false)); 31 | 32 | return new List { new(json!["response"]!.GetValue()) }; 33 | } 34 | 35 | public async IAsyncEnumerable GetStreamingTextContentsAsync(string prompt, 36 | PromptExecutionSettings? executionSettings = null, 37 | Kernel? kernel = null, [EnumeratorCancellation] CancellationToken cancellationToken = new()) 38 | { 39 | var data = new 40 | { 41 | model = Attributes["model_id"], 42 | prompt, 43 | stream = true, 44 | options = executionSettings?.ExtensionData, 45 | }; 46 | 47 | var response = await Http.PostAsJsonAsync($"{Attributes["base_url"]}/api/generate", data, cancellationToken).ConfigureAwait(false); 48 | 49 | ValidateOllamaResponse(response); 50 | 51 | using var stream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); 52 | 53 | using var reader = new StreamReader(stream); 54 | 55 | var done = false; 56 | 57 | while (!done) 58 | { 59 | var json = JsonSerializer.Deserialize( 60 | await response.Content.ReadAsStringAsync().ConfigureAwait(false) 61 | ); 62 | 63 | done = json!["done"]!.GetValue(); 64 | 65 | yield return new StreamingTextContent(json["response"]!.GetValue()); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Console/.gitignore: -------------------------------------------------------------------------------- 1 | appsettings.json 2 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Console/Codeblaze.SemanticKernel.Console.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | Linux 9 | false 10 | 11 | 12 | 13 | 14 | .dockerignore 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Console/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base 2 | USER $APP_UID 3 | WORKDIR /app 4 | 5 | FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build 6 | ARG BUILD_CONFIGURATION=Release 7 | WORKDIR /src 8 | COPY ["Codeblaze.SemanticKernel.Console/Codeblaze.SemanticKernel.Console.csproj", "Codeblaze.SemanticKernel.Console/"] 9 | RUN dotnet restore "Codeblaze.SemanticKernel.Console/Codeblaze.SemanticKernel.Console.csproj" 10 | COPY . . 11 | WORKDIR "/src/Codeblaze.SemanticKernel.Console" 12 | RUN dotnet build "Codeblaze.SemanticKernel.Console.csproj" -c $BUILD_CONFIGURATION -o /app/build 13 | 14 | FROM build AS publish 15 | ARG BUILD_CONFIGURATION=Release 16 | RUN dotnet publish "Codeblaze.SemanticKernel.Console.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false 17 | 18 | FROM base AS final 19 | WORKDIR /app 20 | COPY --from=publish /app/publish . 21 | ENTRYPOINT ["dotnet", "Codeblaze.SemanticKernel.Console.dll"] 22 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Console/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Text.Json; 2 | using Codeblaze.SemanticKernel.Console.Services; 3 | using Codeblaze.SemanticKernel.Plugins.Neo4j; 4 | using Microsoft.Extensions.Configuration; 5 | using Microsoft.SemanticKernel.Memory; 6 | using Spectre.Console; 7 | using Spectre.Console.Json; 8 | 9 | var config = new ConfigurationBuilder() 10 | .AddJsonFile("appsettings.json") 11 | .Build(); 12 | 13 | AnsiConsole.Write(new FigletText($"{config["Name"]!}").Color(Color.Green)); 14 | AnsiConsole.WriteLine(""); 15 | 16 | const string prompt = "1.\tPrompt kernel"; 17 | const string memory = "2.\tMemory search"; 18 | const string exit = "2.\tExit"; 19 | 20 | Run(); 21 | 22 | return; 23 | 24 | void Run() 25 | { 26 | while (true) 27 | { 28 | var option = AnsiConsole.Prompt( 29 | new SelectionPrompt() 30 | .Title("Select an option") 31 | .PageSize(10) 32 | .MoreChoicesText("[grey](Move up and down to reveal more options)[/]") 33 | .AddChoices(prompt, memory, exit) 34 | ); 35 | 36 | switch (option) 37 | { 38 | case prompt: 39 | Prompt().GetAwaiter().GetResult(); 40 | break; 41 | case memory: 42 | Memory().GetAwaiter().GetResult(); 43 | break; 44 | case exit: 45 | return; 46 | } 47 | } 48 | } 49 | 50 | async Task Prompt() 51 | { 52 | KernelService kernel = null; 53 | 54 | AnsiConsole.Status().Start("Initializing...", ctx => 55 | { 56 | ctx.Spinner(Spinner.Known.Star); 57 | ctx.SpinnerStyle(Style.Parse("green")); 58 | 59 | kernel = new KernelService(config); 60 | 61 | ctx.Status("Initialized"); 62 | }); 63 | 64 | var prompt = AnsiConsole.Prompt(new TextPrompt("What are you looking to do today?\n").PromptStyle("teal")); 65 | 66 | string result = null; 67 | 68 | await AnsiConsole.Status().StartAsync("Processing...", async ctx => 69 | { 70 | ctx.Spinner(Spinner.Known.Star); 71 | ctx.SpinnerStyle(Style.Parse("green")); 72 | 73 | ctx.Status($"Processing input"); 74 | result = await kernel.BasicPrompt(prompt); 75 | }); 76 | 77 | AnsiConsole.Write(new Rule("[cyan][/]") { Justification = Justify.Center }); 78 | AnsiConsole.WriteLine($"Result: {result}"); 79 | AnsiConsole.Write(new Rule("[cyan][/]") { Justification = Justify.Center }); 80 | } 81 | 82 | #pragma warning disable SKEXP0003 83 | async Task Memory() 84 | { 85 | NeoMemoryService memory = null; 86 | 87 | AnsiConsole.Status().Start("Initializing...", ctx => 88 | { 89 | ctx.Spinner(Spinner.Known.Star); 90 | ctx.SpinnerStyle(Style.Parse("green")); 91 | 92 | memory = new NeoMemoryService(config); 93 | 94 | ctx.Status("Initialized"); 95 | }); 96 | 97 | var prompt = AnsiConsole.Prompt(new TextPrompt("What are you looking to do today?\n").PromptStyle("teal")); 98 | 99 | #pragma warning disable SKEXP0001 100 | IAsyncEnumerable result = null; 101 | #pragma warning enable SKEXP0001 102 | await AnsiConsole.Status().StartAsync("Processing...", async ctx => 103 | { 104 | ctx.Spinner(Spinner.Known.Star); 105 | ctx.SpinnerStyle(Style.Parse("green")); 106 | 107 | ctx.Status($"Processing input to generate cypher"); 108 | result = memory.Run(prompt); 109 | }); 110 | 111 | await foreach (var record in result) 112 | { 113 | AnsiConsole.Write(new Rule("[cyan][/]") { Justification = Justify.Center }); 114 | AnsiConsole.WriteLine($"Relevance : {record.Relevance}"); 115 | AnsiConsole.WriteLine($"Node ID: {record.Metadata.Id}"); 116 | AnsiConsole.WriteLine($"Node text: {record.Metadata.Text}"); 117 | } 118 | 119 | AnsiConsole.Write(new Rule("[cyan][/]") { Justification = Justify.Center }); 120 | } 121 | #pragma warning enable SKEXP0003 -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Console/Services/KernelService.cs: -------------------------------------------------------------------------------- 1 | using Codeblaze.SemanticKernel.Connectors.Ollama; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.SemanticKernel; 5 | using Microsoft.SemanticKernel.ChatCompletion; 6 | 7 | namespace Codeblaze.SemanticKernel.Console.Services; 8 | 9 | public class KernelService 10 | { 11 | private readonly Kernel _Kernel; 12 | 13 | public KernelService(IConfiguration config) 14 | { 15 | var builder = Kernel.CreateBuilder(); 16 | 17 | builder.Services.AddTransient(); 18 | 19 | // builder.AddOllamaChatCompletion(config["Ollama:Model"], config["Ollama:BaseUrlGeneration"]); 20 | builder.AddOllamaTextGeneration(config["Ollama:Model"], config["Ollama:BaseUrlGeneration"]); 21 | 22 | _Kernel = builder.Build(); 23 | } 24 | 25 | public async Task BasicPrompt(string input) 26 | { 27 | const string prompt = """ 28 | Bot: How can I help you? 29 | User: {{$input}} 30 | 31 | --------------------------------------------- 32 | 33 | The intent of the user in 5 words or less: 34 | """; 35 | 36 | var result = await _Kernel.InvokePromptAsync(prompt, new KernelArguments 37 | { 38 | {"input", input} 39 | }); 40 | 41 | return result.GetValue(); 42 | } 43 | 44 | public async Task BasicChat(string input) 45 | { 46 | var chat = _Kernel.GetRequiredService(); 47 | 48 | var history = new ChatHistory(); 49 | 50 | history.AddSystemMessage("..."); 51 | history.AddAssistantMessage("..."); 52 | history.AddUserMessage(input); 53 | 54 | var result = await chat.GetChatMessageContentsAsync(history); 55 | 56 | return result[^1].Content; 57 | } 58 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Console/Services/NeoKernelService.cs: -------------------------------------------------------------------------------- 1 | using Codeblaze.SemanticKernel.Plugins.Neo4j; 2 | using Microsoft.Extensions.Configuration; 3 | using Microsoft.SemanticKernel; 4 | 5 | namespace Codeblaze.SemanticKernel.Console.Services; 6 | 7 | public class NeoKernelService 8 | { 9 | private readonly Kernel _Kernel; 10 | 11 | public NeoKernelService(IConfiguration config) 12 | { 13 | var builder = Kernel.CreateBuilder(); 14 | 15 | // builder.Services.AddTransient(); 16 | 17 | builder.AddOpenAIChatCompletion(config["OpenAI:Model"], config["OpenAI:Key"]); 18 | // builder.AddOllamaChatCompletion(config["Ollama:Model"], config["Ollama:BaseUrlGeneration"]); 19 | 20 | _Kernel = builder.Build(); 21 | 22 | _Kernel.AddNeo4jCypherGenPlugin(config["Neo4j:Url"], config["Neo4j:Username"], config["Neo4j:Password"]); 23 | } 24 | 25 | public Task Run(string prompt) 26 | { 27 | return _Kernel.InvokeAsync( 28 | nameof(Neo4jCypherGenPlugin), "Query", 29 | new KernelArguments 30 | { 31 | { "prompt", prompt } 32 | } 33 | ); 34 | } 35 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Console/Services/NeoMemoryService.cs: -------------------------------------------------------------------------------- 1 | using Codeblaze.SemanticKernel.Connectors.Memory.Neo4j; 2 | using Codeblaze.SemanticKernel.Connectors.Ollama; 3 | using Microsoft.Extensions.Configuration; 4 | using Microsoft.SemanticKernel.Memory; 5 | 6 | namespace Codeblaze.SemanticKernel.Console.Services; 7 | 8 | #pragma warning disable SKEXP0001 9 | public class NeoMemoryService 10 | { 11 | private readonly ISemanticTextMemory _memory; 12 | 13 | public NeoMemoryService(IConfiguration config) 14 | { 15 | var builder = new MemoryBuilder(); 16 | 17 | builder.WithHttpClient(new HttpClient()); 18 | builder.WithOllamaTextEmbeddingGeneration(config["Ollama:Model"], config["Ollama:BaseUrlEmbeddings"]); 19 | builder.WithNeo4jMemoryStore(config["Neo4j:Url"], config["Neo4j:Username"], config["Neo4j:Password"], 20 | new Neo4jVectorIndexQueryFactory( 21 | "top_questions", 22 | "Question", 23 | "embedding", 24 | "title", 25 | 384 26 | ) 27 | ); 28 | 29 | _memory = builder.Build(); 30 | } 31 | 32 | public async IAsyncEnumerable Run(string prompt) 33 | { 34 | var results = _memory.SearchAsync("top_questions", prompt, limit: 3); 35 | 36 | await foreach (var result in results) 37 | { 38 | yield return new MemoryQueryResult( 39 | (await _memory.GetAsync("top_questions", result.Metadata.Id)).Metadata, 40 | result.Relevance, 41 | result.Embedding 42 | ); 43 | } 44 | } 45 | } 46 | #pragma warning enable SKEXP0001 -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Plugins.Neo4j/Codeblaze.SemanticKernel.Plugins.Neo4j.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | Latest 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | all 20 | 21 | 22 | 23 | 24 | 25 | Codeblaze.SemanticKernel.Plugins.Neo4j 26 | Codeblaze (Devashish Lal) 27 | SemanticKernel;Neo4j;Cypher;Codegen;Knowledge Graphs 28 | 29 | This package provides semantic kernel plugin for Neo4j cyher codegen support, Enabling talking to knowledge graphs 30 | 31 | https://github.com/BLaZeKiLL/Codeblaze.SemanticKernel 32 | git 33 | README.md 34 | LICENSE.md 35 | v 36 | 37 | 38 | 39 | true 40 | true 41 | 42 | 43 | 44 | $(NoWarn);1591 45 | bin/$(Configuration)/$(TargetFramework)/$(AssemblyName).xml 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Plugins.Neo4j/Neo4jCypherGenPlugin.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Text.Json; 3 | using Microsoft.SemanticKernel; 4 | using Microsoft.SemanticKernel.ChatCompletion; 5 | using Neo4j.Driver; 6 | 7 | namespace Codeblaze.SemanticKernel.Plugins.Neo4j; 8 | 9 | /// 10 | /// Cypher gen query execution result 11 | /// 12 | public class Neo4jResult 13 | { 14 | public bool Success { get; set; } 15 | public string? Cypher { get; set; } 16 | public List? Result { get; set; } 17 | } 18 | 19 | /// 20 | /// Cypher code gen plugin 21 | /// 22 | public class Neo4jCypherGenPlugin : IDisposable 23 | { 24 | private readonly IDriver _driver; 25 | private readonly IChatCompletionService _chat; 26 | 27 | /// 28 | /// Text bases representation of schema for the current database 29 | /// 30 | public string Schema { get; } 31 | 32 | private const string NODE_PROPS_QUERY = """ 33 | CALL apoc.meta.data() 34 | YIELD label, other, elementType, type, property 35 | WHERE NOT type = "RELATIONSHIP" AND elementType = "node" 36 | WITH label AS nodeLabels, collect(property) AS properties 37 | RETURN {labels: nodeLabels, properties: properties} AS output 38 | """; 39 | 40 | private const string REL_PROPS_QUERY = """ 41 | CALL apoc.meta.data() 42 | YIELD label, other, elementType, type, property 43 | WHERE NOT type = "RELATIONSHIP" AND elementType = "relationship" 44 | WITH label AS nodeLabels, collect(property) AS properties 45 | RETURN {type: nodeLabels, properties: properties} AS output 46 | """; 47 | 48 | private const string REL_QUERY = """ 49 | CALL apoc.meta.data() 50 | YIELD label, other, elementType, type, property 51 | WHERE type = "RELATIONSHIP" AND elementType = "node" 52 | RETURN {source: label, relationship: property, target: other} AS output 53 | """; 54 | 55 | /// 56 | /// Creates a neo4j cypher gen plugin instance 57 | /// 58 | /// Chat service from semantic kernel to be used as generation backend 59 | /// Neo4j url, used by the neo4j driver 60 | /// Neo4j database username, used by the neo4j driver 61 | /// Neo4j database password, used by the neo4j driver 62 | public Neo4jCypherGenPlugin(IChatCompletionService chat, string url, string username, string password) 63 | { 64 | _driver = GraphDatabase.Driver(url, AuthTokens.Basic(username, password)); 65 | _chat = chat; 66 | 67 | Schema = GetSchema().GetAwaiter().GetResult(); 68 | } 69 | 70 | /// 71 | /// Creates a neo4j cypher gen plugin instance 72 | /// 73 | /// Chat service from semantic kernel to be used as generation backend 74 | /// Neo4j driver to be used for executing cypher 75 | public Neo4jCypherGenPlugin(IChatCompletionService chat, IDriver driver) 76 | { 77 | _driver = driver; 78 | _chat = chat; 79 | 80 | Schema = GetSchema().GetAwaiter().GetResult(); 81 | } 82 | 83 | /// 84 | /// SK Function to generate cypher, execute it and return the result 85 | /// 86 | /// prompt against which cypher is to be generated 87 | /// Result containing, cypher and cypher execution result 88 | [KernelFunction, Description("Generates cypher code based on prompt and queries the database")] 89 | public async Task Query(string prompt) 90 | { 91 | var cypher = await GenerateCypher(prompt); 92 | 93 | try 94 | { 95 | var result = await NeoQuery(cypher); 96 | 97 | return new Neo4jResult 98 | { 99 | Success = true, 100 | Cypher = cypher, 101 | Result = result 102 | }; 103 | } 104 | catch 105 | { 106 | return new Neo4jResult { Success = false, Cypher = cypher }; 107 | } 108 | } 109 | 110 | /// 111 | /// SK Function to generate cypher based on the prompt 112 | /// 113 | /// prompt against which cypher is to be generated 114 | /// Generated cypher 115 | [KernelFunction, Description("Generates cypher code based on prompt")] 116 | public async Task GenerateCypher(string prompt) 117 | { 118 | var system = $""" 119 | Task: Generate Cypher queries to query a Neo4j graph database based on the provided schema definition. 120 | Instructions: 121 | Use only the provided relationship types and properties. 122 | Do not use any other relationship types or properties that are not provided. 123 | If you cannot generate a Cypher statement based on the provided schema, explain the reason to the user. 124 | Schema: 125 | {Schema} 126 | 127 | Note: Do not include any explanations or apologies in your responses. 128 | """; 129 | 130 | var history = new ChatHistory 131 | { 132 | new(AuthorRole.System, system), 133 | new(AuthorRole.User, prompt) 134 | }; 135 | 136 | var result = await _chat.GetChatMessageContentsAsync(history); 137 | 138 | return result[0].Content; 139 | } 140 | 141 | private async Task GetSchema() 142 | { 143 | var nodeProps = JsonSerializer.Serialize((await NeoQuery(NODE_PROPS_QUERY)).Select(x => x.Values["output"]).ToList()); 144 | var relationProps = JsonSerializer.Serialize((await NeoQuery(REL_PROPS_QUERY)).Select(x => x.Values["output"]).ToList()); 145 | var relations = JsonSerializer.Serialize((await NeoQuery(REL_QUERY)).Select(x => x.Values["output"]).ToList()); 146 | 147 | return $""" 148 | This is the schema representation of the Neo4j database. 149 | Node properties are the following: 150 | {nodeProps} 151 | Relationship properties are the following: 152 | {relationProps} 153 | Relationship point from source to target nodes 154 | {relations} 155 | Make sure to respect relationship types and directions 156 | """; 157 | } 158 | 159 | private async Task> NeoQuery(string query) 160 | { 161 | await using var session = _driver.AsyncSession(o => o.WithDatabase("neo4j")); 162 | 163 | return await session.ExecuteReadAsync(async transaction => 164 | { 165 | var cursor = await transaction.RunAsync(query); 166 | 167 | return await cursor.ToListAsync(); 168 | }); 169 | } 170 | 171 | public async void Dispose() 172 | { 173 | await _driver.DisposeAsync(); 174 | } 175 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Plugins.Neo4j/Neo4jPluginBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.SemanticKernel; 2 | using Microsoft.SemanticKernel.ChatCompletion; 3 | using Neo4j.Driver; 4 | 5 | namespace Codeblaze.SemanticKernel.Plugins.Neo4j; 6 | 7 | public static class Neo4jPluginBuilderExtension 8 | { 9 | public static void AddNeo4jCypherGenPlugin(this Kernel kernel, string url, string username, string password) 10 | { 11 | var chat = kernel.GetRequiredService(); 12 | 13 | var plugin = new Neo4jCypherGenPlugin(chat, url, username, password); 14 | 15 | kernel.ImportPluginFromObject(plugin, nameof(Neo4jCypherGenPlugin)); 16 | } 17 | 18 | public static void AddNeo4jCypherGenPlugin(this Kernel kernel, IDriver driver) 19 | { 20 | var chat = kernel.GetRequiredService(); 21 | 22 | var plugin = new Neo4jCypherGenPlugin(chat, driver); 23 | 24 | kernel.ImportPluginFromObject(plugin, nameof(Neo4jCypherGenPlugin)); 25 | } 26 | } -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.Plugins.Neo4j/README.md: -------------------------------------------------------------------------------- 1 | # Neo4j Cypher Codegen 2 | This package provides semantic kernel plugin for Neo4j cyher codegen support, Enabling talking to knowledge graphs respecting it's schema 3 | 4 | ### Important 5 | - Plugin has been tested with gpt-3.5-turbo, other models may require tweaks to the prompts 6 | - Make sure your neo4j instance has the [apoc plugin](https://neo4j.com/docs/apoc/current/) 7 | - On initialization, the plugin queries the database to generate a text representation of schema 8 | 9 | ### Quick Start 10 | - Install from [nuget](https://www.nuget.org/packages/Codeblaze.SemanticKernel.Plugins.Neo4j) 11 | ``` 12 | dotnet add package Codeblaze.SemanticKernel.Plugins.Neo4j 13 | ``` 14 | - Configure the kernel 15 | ```csharp 16 | var builder = Kernel.CreateBuilder(); 17 | 18 | builder.AddOpenAIChatCompletion( 19 | config["OpenAI:Model"], 20 | config["OpenAI:Key"] 21 | ); 22 | 23 | var kernel = builder.Build(); 24 | 25 | kernel.AddNeo4jCypherGenPlugin( 26 | config["Neo4j:Url"], 27 | config["Neo4j:Username"], 28 | config["Neo4j:Password"] 29 | ); 30 | ``` 31 | - Query the database using prompts 32 | ```csharp 33 | var prompt = "Which movies came out in 1990?"; 34 | 35 | var result = await _Kernel.InvokeAsync( 36 | nameof(Neo4jCypherGenPlugin), "Query", 37 | new KernelArguments 38 | { 39 | { "prompt", prompt } 40 | } 41 | ); 42 | 43 | result.Cypher; // Generated cypher (string) 44 | result.Result; // Neo4j query execution result (List) 45 | ``` 46 | 47 | ### Semantic Kernel Functions 48 | - Query 49 | - **description** 50 | 51 | Generates cypher code based on the provided prompt, executes it against the configured neo4j instance 52 | returns both cypher and the prompt 53 | - **usage** 54 | ```csharp 55 | _Kernel.InvokeAsync( 56 | nameof(Neo4jCypherGenPlugin), "Query", 57 | new KernelArguments 58 | { 59 | { "prompt", prompt } 60 | } 61 | ); 62 | ``` 63 | 64 | - GenerateCypher 65 | - **description** 66 | 67 | Generates cypher code based on the provided prompt 68 | - **usage** 69 | ```csharp 70 | _Kernel.InvokeAsync( 71 | nameof(Neo4jCypherGenPlugin), "GenerateCypher", 72 | new KernelArguments 73 | { 74 | { "prompt", prompt } 75 | } 76 | ); 77 | ``` 78 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codeblaze.SemanticKernel.Connectors.Ollama", "Codeblaze.SemanticKernel.Connectors.Ollama\Codeblaze.SemanticKernel.Connectors.Ollama.csproj", "{D9D1DB9F-F4FF-494C-A470-7E29BA5CCC49}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codeblaze.SemanticKernel.Api", "Codeblaze.SemanticKernel.Api\Codeblaze.SemanticKernel.Api.csproj", "{51E5DA79-8F4E-4D8D-95ED-96C724741E6F}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codeblaze.SemanticKernel.Console", "Codeblaze.SemanticKernel.Console\Codeblaze.SemanticKernel.Console.csproj", "{AC32C115-684E-408C-9320-8D915FF7E3A1}" 8 | EndProject 9 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codeblaze.SemanticKernel.Plugins.Neo4j", "Codeblaze.SemanticKernel.Plugins.Neo4j\Codeblaze.SemanticKernel.Plugins.Neo4j.csproj", "{59813D42-7792-4750-AD89-0548812BAD9E}" 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codeblaze.SemanticKernel.Connectors.Memory.Neo4j", "Codeblaze.SemanticKernel.Connectors.Memory.Neo4j\Codeblaze.SemanticKernel.Connectors.Memory.Neo4j.csproj", "{3EAC6B51-3F7E-47CD-98B3-02915B18CE39}" 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | Debug|Any CPU = Debug|Any CPU 16 | Release|Any CPU = Release|Any CPU 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {D9D1DB9F-F4FF-494C-A470-7E29BA5CCC49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {D9D1DB9F-F4FF-494C-A470-7E29BA5CCC49}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {D9D1DB9F-F4FF-494C-A470-7E29BA5CCC49}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {D9D1DB9F-F4FF-494C-A470-7E29BA5CCC49}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {51E5DA79-8F4E-4D8D-95ED-96C724741E6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {51E5DA79-8F4E-4D8D-95ED-96C724741E6F}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {51E5DA79-8F4E-4D8D-95ED-96C724741E6F}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {51E5DA79-8F4E-4D8D-95ED-96C724741E6F}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {AC32C115-684E-408C-9320-8D915FF7E3A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 28 | {AC32C115-684E-408C-9320-8D915FF7E3A1}.Debug|Any CPU.Build.0 = Debug|Any CPU 29 | {AC32C115-684E-408C-9320-8D915FF7E3A1}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {AC32C115-684E-408C-9320-8D915FF7E3A1}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {59813D42-7792-4750-AD89-0548812BAD9E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {59813D42-7792-4750-AD89-0548812BAD9E}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {59813D42-7792-4750-AD89-0548812BAD9E}.Release|Any CPU.ActiveCfg = Release|Any CPU 34 | {59813D42-7792-4750-AD89-0548812BAD9E}.Release|Any CPU.Build.0 = Release|Any CPU 35 | {3EAC6B51-3F7E-47CD-98B3-02915B18CE39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 36 | {3EAC6B51-3F7E-47CD-98B3-02915B18CE39}.Debug|Any CPU.Build.0 = Debug|Any CPU 37 | {3EAC6B51-3F7E-47CD-98B3-02915B18CE39}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {3EAC6B51-3F7E-47CD-98B3-02915B18CE39}.Release|Any CPU.Build.0 = Release|Any CPU 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /dotnet/Codeblaze.SemanticKernel.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | True 3 | True -------------------------------------------------------------------------------- /dotnet/global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "rollForward": "feature", 4 | "version": "8.0.100" 5 | } 6 | } -------------------------------------------------------------------------------- /java/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BLaZeKiLL/Codeblaze.SemanticKernel/aa77487ef6753bd2937f90050493ce48b948436c/java/.gitkeep -------------------------------------------------------------------------------- /python/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BLaZeKiLL/Codeblaze.SemanticKernel/aa77487ef6753bd2937f90050493ce48b948436c/python/.gitkeep --------------------------------------------------------------------------------