Table of Contents

๐Ÿง  Potato RAG Pipeline Quickstart Guide

This step-by-step guide will walk you through creating a Retrieval-Augmented Generation (RAG) API using Potato packages with MongoDB, MinIO (S3-compatible), Qdrant, and Mistral embeddings.


1. ๐ŸŽฏ Create the API Project

Add a new project to the Potato.Examples solution:

dotnet new web -n Potato.Examples.Api.Rag
dotnet sln add Potato.Examples.Api.Rag/Potato.Examples.Api.Rag.csproj

cd Potato.Examples.Api.Rag
dotnet new gitignore

2. ๐Ÿ“ฆ Install Required Packages

dotnet add package Potato

dotnet add package Potato.Mongo

dotnet add package Potato.Files.S3

dotnet add package Potato.Vectors.Qdrant

dotnet add package Potato.DocumentChopper

dotnet add package Potato.Embeddings.Mistral

dotnet add package Potato.RagPipeline

3. ๐Ÿณ Add docker-compose.yml

version: "3.8"
services:
  mongo:
    image: mongo
    ports:
      - "27017:27017"

  qdrant:
    image: qdrant/qdrant
    ports:
      - "6333:6333"
      - "6334:6334"

  minio:
    image: minio/minio
    command: server /data --console-address ":9001"
    ports:
      - "9000:9000"
      - "9001:9001"
    environment:
      MINIO_ROOT_USER: admin
      MINIO_ROOT_PASSWORD: potato123
    volumes:
      - minio_data:/data

volumes:
  minio_data:

Run the services:

docker compose up -d

4. โš™๏ธ Add Configuration to appsettings.json

{
  "ConnectionStrings": {
    "MongoDbConnectionString": "mongodb://localhost:27017"
  },
  "PotatoS3Files": {
    "ServiceUrl": "http://localhost:9000",
    "AccessKey": "admin",
    "SecretKey": "potato123",
    "BucketName": "potato-dev",
    "UseHttp": true
  },
  "PotatoQdrant": {
    "Host": "localhost",
    "Port": 6334
  },
  "Mistral": {
    "ApiKey": "your-mistral-api-key"
  }
}

5. ๐Ÿ›  Register Services in Program.cs

builder.Services
    .AddApplicationLayer(typeof(Program).Assembly)
    .AddMongoStandardGuidRepresentation()
    .AddMongoDb(builder.Configuration, "chunks")
    .AddMongoRepository<PotatoDocumentChunk>()
    .AddMongoFinder<PotatoDocumentChunk>()
    .AddPotatoS3Files(builder.Configuration)
    .AddPotatoDocumentChopper()
    .AddPotatoMarkdownConverter()
    .AddPotatoMistralEmbeddings(builder.Configuration)
    .AddPotatoQdrantVectors(builder.Configuration)
    .AddPotatoRagPipeline();

var app = builder.Build();

await app.EnsureQdrantCollectionExists();
await app.EnsureS3BucketExists();

app.MapPost("/ingest", async (IFormFile file, IPotatoRagPipeline pipeline) =>
{
    var result = await pipeline.Ingest(file, Map<string, string>(), contextId: Guid.NewGuid());
    return result.Match(Results.Ok, f => Results.BadRequest(f.Message));
}).DisableAntiforgery();

app.MapGet("/search", async (string query, IPotatoRagPipeline pipeline) =>
{
    var result = await pipeline.Retrieve(query);
    return result.Match(Results.Ok, f => Results.BadRequest(f.Message));
});

app.MapGet("/message", async (string message, MistralClient client, IPotatoRagPipeline pipeline) =>
{
    var result = await pipeline.Retrieve(message).GetOrThrow();
    var request = new ChatCompletionRequest(
        ModelDefinitions.MistralMedium,
        [new ChatMessage(ChatMessage.RoleEnum.User, PromptBuilder.Build(result.Map(c => c.Text), message))]
    );

    return Results.Ok(new
    {
        Answer = await client.Completions.GetCompletionAsync(request),
        ContextUsed = result,
        SourceFiles = result.Map(r => new { r.FilePath, r.Index })
    });
});

app.Run();

public class PromptBuilder
{
    public static string Build(Lst<string> chunks, string userQuery)
    {
        if (chunks.IsEmpty)
        {
            return userQuery;
        }

        return $"""
        Context information is below.
        ---------------------
        {string.Join("\n\n", chunks)}
        ---------------------
        Given the context information and not prior knowledge, answer the query.
        Query: {userQuery}
        Answer:
        """;
    }
}

6. ๐Ÿงช Test Using .http File

Create a new file named api.http in your project:

@hostname=localhost
@port=5297

### ๐Ÿ“ค Ingest a document (multipart upload)
POST http://{{hostname}}:{{port}}/ingest
Content-Type: multipart/form-data; boundary=---011000010111000001101001

-----011000010111000001101001
Content-Disposition: form-data; name="file"; filename="sample.txt"
Content-Type: text/plain

This is a simple test document. You can say hello and goodbye.

-----011000010111000001101001--

### ๐Ÿ” Search chunks related to a query
GET http://{{hostname}}:{{port}}/search?query=how do i say hello

### ๐Ÿ’ฌ Ask a question and get a contextual answer
GET http://{{hostname}}:{{port}}/message?message=how do i say hello

Use the REST Client extension in VS Code to test these endpoints directly.


โœ… You're Done!

You now have a full RAG pipeline wired with Potato. ๐Ÿš€