Table of Contents

Prompt Templates

The PromptTemplate class lets you create reusable prompts with {{variable}} placeholders. Variables are filled at render time, with support for defaults, strict validation, and template composition.

Creating a Template

using Prompt;

// Simple template
var template = new PromptTemplate(
    "Explain {{topic}} to a {{audience}} in {{style}} style."
);

// With default values
var template = new PromptTemplate(
    "You are a {{role}} assistant. Help the user with {{topic}}.",
    new Dictionary<string, string>
    {
        ["role"] = "helpful",
        ["style"] = "concise"
    }
);

Rendering

Replace placeholders with values:

var template = new PromptTemplate(
    "Translate '{{text}}' from {{source}} to {{target}}.",
    new Dictionary<string, string> { ["source"] = "English" }
);

string prompt = template.Render(new Dictionary<string, string>
{
    ["text"] = "Good morning",
    ["target"] = "Japanese"
});
// → "Translate 'Good morning' from English to Japanese."

Strict vs Non-Strict Mode

By default, rendering throws if a required variable is missing:

// Strict mode (default) — throws InvalidOperationException
template.Render(new Dictionary<string, string>
{
    ["text"] = "Hello"
    // Missing "target" → exception
});

// Non-strict mode — leaves unresolved variables as-is
string result = template.Render(
    new Dictionary<string, string> { ["text"] = "Hello" },
    strict: false
);
// → "Translate 'Hello' from English to {{target}}."

Variable Introspection

Discover what variables a template needs:

var template = new PromptTemplate(
    "Summarize {{text}} in {{language}} for a {{audience}} audience."
);

HashSet<string> allVars = template.GetVariables();
// { "text", "language", "audience" }

HashSet<string> required = template.GetRequiredVariables();
// { "text", "language", "audience" } (none have defaults)

Managing Defaults

var template = new PromptTemplate("Explain {{topic}} in {{style}} style.");

// Add a default
template.SetDefault("style", "concise");

// Remove a default (makes it required again)
template.RemoveDefault("style");

// Read defaults
IReadOnlyDictionary<string, string> defaults = template.Defaults;

Render and Send

Render the template and send directly to Azure OpenAI in one call:

// One-shot: render + send via Main.GetResponseAsync
string? response = await template.RenderAndSendAsync(
    new Dictionary<string, string> { ["topic"] = "recursion" },
    systemPrompt: "You are a CS professor."
);

// With custom options
var opts = PromptOptions.ForCodeGeneration();
string? code = await template.RenderAndSendAsync(
    new Dictionary<string, string> { ["topic"] = "merge sort in C#" },
    options: opts
);

// Within a Conversation
var conv = new Conversation("You are a tutor.");
string? reply = await template.RenderAndSendAsync(
    conv,
    new Dictionary<string, string> { ["topic"] = "closures" }
);

Composition

Combine templates by concatenating them with merged defaults:

var context = new PromptTemplate(
    "Context: The user is working on a {{language}} project.",
    new Dictionary<string, string> { ["language"] = "C#" }
);

var task = new PromptTemplate(
    "Task: {{instruction}}\n\nRespond with clean, tested code."
);

// Compose: context + task
PromptTemplate combined = context.Compose(task);

string prompt = combined.Render(new Dictionary<string, string>
{
    ["instruction"] = "Write a binary search function"
});
// → "Context: The user is working on a C# project.\n\nTask: Write a binary search function\n\nRespond with clean, tested code."

The Compose method concatenates templates with a separator (default: \n\n) and merges defaults — the second template's defaults take precedence on conflicts.

Serialization

Save and load templates as JSON for reuse:

// To JSON string
string json = template.ToJson();
// {
//   "template": "Explain {{topic}} in {{style}} style.",
//   "defaults": { "style": "concise" }
// }

// From JSON string
var loaded = PromptTemplate.FromJson(json);

// File-based
await template.SaveToFileAsync("templates/summarize.json");
var restored = await PromptTemplate.LoadFromFileAsync("templates/summarize.json");

Building a Template Library

A practical pattern for organizing reusable templates:

public static class Templates
{
    public static PromptTemplate Summarize => new(
        "Summarize the following text in {{sentences}} sentences:\n\n{{text}}",
        new Dictionary<string, string> { ["sentences"] = "3" }
    );

    public static PromptTemplate CodeReview => new(
        "Review this {{language}} code for bugs, performance, and style:\n\n```{{language}}\n{{code}}\n```",
        new Dictionary<string, string> { ["language"] = "C#" }
    );

    public static PromptTemplate Translate => new(
        "Translate the following from {{source}} to {{target}}:\n\n{{text}}",
        new Dictionary<string, string> { ["source"] = "English" }
    );
}