Priest 2.3.0

dotnet add package Priest --version 2.3.0
                    
NuGet\Install-Package Priest -Version 2.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Priest" Version="2.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Priest" Version="2.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Priest" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Priest --version 2.3.0
                    
#r "nuget: Priest, 2.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Priest@2.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Priest&version=2.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Priest&version=2.3.0
                    
Install as a Cake Tool

Priest

C# / .NET SDK for the priest AI orchestration protocol.

.NET 8+ · C# · One dependency (Microsoft.Data.Sqlite for SQLite sessions)


Overview

Priest is a .NET class library that implements the priest protocol spec v2.3.0 natively — no Python server, no FFI. It is designed for .NET backends, Unity games, Godot projects, and any C# host that needs to talk to a local or remote AI provider.

The core API is two methods on PriestEngine:

Method Returns Use when
RunAsync(request) Task<PriestResponse> You need structured metadata (usage, latency, session info)
StreamAsync(request) IAsyncEnumerable<string> You want to yield text as it arrives

Installation

dotnet add package Priest

Quick Start

Single run with Ollama

using Priest.Engine;
using Priest.Profiles;
using Priest.Providers;
using Priest.Schema;

var engine = new PriestEngine(
    profileLoader: new FilesystemProfileLoader("./profiles"),
    adapters: new Dictionary<string, IProviderAdapter>
    {
        ["ollama"] = new OllamaProvider("http://localhost:11434"),
    }
);

var response = await engine.RunAsync(new PriestRequest(
    config: new PriestConfig("ollama", "llama3.2"),
    prompt: "What is the capital of France?"
));

if (response.Ok)
    Console.WriteLine(response.Text);

Streaming

await foreach (var chunk in engine.StreamAsync(new PriestRequest(
    config: new PriestConfig("ollama", "llama3.2"),
    prompt: "Tell me a story."
)))
{
    Console.Write(chunk);
}

Anthropic or OpenAI-compatible providers

var engine = new PriestEngine(
    profileLoader: new FilesystemProfileLoader("./profiles"),
    adapters: new Dictionary<string, IProviderAdapter>
    {
        ["anthropic"] = new AnthropicProvider("sk-ant-..."),
        ["openai"]    = new OpenAICompatProvider("https://api.openai.com", "sk-..."),
    }
);

var response = await engine.RunAsync(new PriestRequest(
    config: new PriestConfig("anthropic", "claude-opus-4-6"),
    prompt: "Summarize the priest protocol in one sentence."
));

Session Continuity

Pass a Session property to persist conversation history across calls.

using Priest.Sessions;

using var store = new SqliteSessionStore("./sessions.db");
store.Open();

var engine = new PriestEngine(
    profileLoader: new FilesystemProfileLoader("./profiles"),
    sessionStore: store,
    adapters: new Dictionary<string, IProviderAdapter>
    {
        ["ollama"] = new OllamaProvider(),
    }
);

var config = new PriestConfig("ollama", "llama3.2");
var sessionId = "user-123-chat";

// First turn — session is created automatically
await engine.RunAsync(new PriestRequest(config, "My name is Alex.")
{
    Session = new SessionRef(sessionId),
});

// Second turn — session is continued
var r = await engine.RunAsync(new PriestRequest(config, "What is my name?")
{
    Session = new SessionRef(sessionId),
});
// r.Text → "Your name is Alex."

SessionRef behavior:

ContinueExisting CreateIfMissing Result
true (default) true (default) Load existing session or create it
true false Load existing or throw SESSION_NOT_FOUND
false Always create a new session

The SQLite store is interoperable with the Python priest SqliteSessionStore and the Swift/TypeScript SDKs — the schema and timestamp format are identical across all implementations.


Profiles

A profile supplies identity, rules, and optional custom and memories that shape the system prompt.

profiles/
├── default.json
└── coder.json
var loader = new FilesystemProfileLoader("./profiles");

Falls back to the built-in default profile when the named file is not found. Use new FilesystemProfileLoader("./profiles", includeMemories: false) when the host app owns memory selection and passes selected memory through PriestRequest.Memory.

Profile format — default.json:

{
  "identity": "You are a helpful assistant.",
  "rules": "Be honest. Do not make things up.\nBe concise unless the user asks for depth.",
  "memories": []
}

Memory and Context

var response = await engine.RunAsync(new PriestRequest(config, "What should I work on today?")
{
    // Raw system context — injected first, never trimmed or deduped
    Context = ["Today is Monday. App: ProjectManager"],

    // Dynamic memory — deduped against profile memories and each other
    Memory = ["User prefers bullet points.", "Active sprint: v3.0"],

    // Per-turn user context — appended to the user message
    UserContext = ["Recent tasks: [fix login bug, update README]"],
});

When MaxSystemChars is set on the config, the engine trims Memory entries tail-first, then profile.Memories tail-first. Context, rules, identity, custom, and format instructions are never trimmed.

var response = await engine.RunAsync(new PriestRequest(
    new PriestConfig("ollama", "llama3.2") { MaxSystemChars = 4096 },
    "Summarize my notes."
) { Memory = longMemoryList });

Output Format Hints

var response = await engine.RunAsync(new PriestRequest(config, "List three planets as JSON.")
{
    Output = new OutputSpec
    {
        ProviderFormat = ProviderFormat.Json,
        PromptFormat   = PromptFormat.Json,
    },
});

ProviderFormat activates the provider's native JSON mode. PromptFormat injects a natural-language instruction into the system prompt.

For strict schema compliance, use JsonSchema instead:

using System.Text.Json.Nodes;

var response = await engine.RunAsync(new PriestRequest(config, "Give me a person object.")
{
    Output = new OutputSpec
    {
        JsonSchema = JsonNode.Parse("""
            {
              "type": "object",
              "properties": { "name": { "type": "string" }, "age": { "type": "integer" } },
              "required": ["name", "age"]
            }
            """),
        JsonSchemaName   = "person",  // optional, defaults to "response"
        JsonSchemaStrict = false,     // true requires additionalProperties:false on all objects
    },
});

JsonSchema maps to response_format:{type:"json_schema"} for OpenAI-compat, format:<schema> for Ollama (v0.5+), and system message injection for Anthropic. It takes precedence over ProviderFormat when both are set.

response.Text is always the raw string. Priest never parses the output.


Error Handling

Two errors are always thrown and never captured into response.Error:

  • PROVIDER_NOT_REGISTERED — no adapter found for the requested provider key.
  • SESSION_NOT_FOUND — session lookup failed and CreateIfMissing is false.

All other provider errors (network failures, rate limits, timeouts) are caught and placed into response.Error. Check response.Ok before reading response.Text.

using Priest.Errors;

try
{
    var response = await engine.RunAsync(request);
    if (response.Ok)
        Console.WriteLine(response.Text);
    else
        Console.Error.WriteLine($"Provider error: {response.Error!.Message}");
}
catch (PriestException ex)
{
    // PROVIDER_NOT_REGISTERED or SESSION_NOT_FOUND
    Console.Error.WriteLine($"Fatal: {ex.Code} — {ex.Message}");
}

Providers

Key Class Notes
any OllamaProvider NDJSON streaming; local by default (http://localhost:11434)
any AnthropicProvider SSE streaming; requires API key
any OpenAICompatProvider SSE streaming; works with any OpenAI-compatible endpoint

Provider keys are arbitrary strings — the key you register in the adapters dictionary must match the Provider field in PriestConfig.


Custom Providers

Implement IProviderAdapter to add your own backend:

using Priest.Providers;

public class MyProvider : IProviderAdapter
{
    public Task<AdapterResult> CompleteAsync(
        IList<ChatMessage> messages, PriestConfig config,
        OutputSpec? outputSpec = null, CancellationToken ct = default)
    {
        return Task.FromResult(new AdapterResult("Hello from MyProvider", "stop"));
    }

    public async IAsyncEnumerable<string> StreamAsync(
        IList<ChatMessage> messages, PriestConfig config,
        OutputSpec? outputSpec = null,
        [EnumeratorCancellation] CancellationToken ct = default)
    {
        yield return "Hello ";
        yield return "from MyProvider";
        await Task.CompletedTask;
    }
}

Spec

Priest targets priest protocol spec v2.3.0. The spec lives in the priest repository under spec/.

PriestEngine.SpecVersion  // "2.3.0"

Requirements

  • .NET 8+
  • Microsoft.Data.Sqlite is the only runtime dependency (required for SqliteSessionStore)
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.3.0 96 5/8/2026
2.2.0 103 4/26/2026
2.0.0 91 4/20/2026
1.0.0 94 4/12/2026