An open API service indexing awesome lists of open source software.

https://github.com/sauravbhattacharya001/prompt

.NET 8 prompt engineering toolkit โ€” Azure OpenAI client, template engine, prompt chaining, injection detection, bias detector, and more
https://github.com/sauravbhattacharya001/prompt

ai azure-openai chat-completions chatgpt csharp dotnet dotnet-library gpt-4 llm nuget openai prompt-chaining prompt-engineering prompt-template

Last synced: about 1 month ago
JSON representation

.NET 8 prompt engineering toolkit โ€” Azure OpenAI client, template engine, prompt chaining, injection detection, bias detector, and more

Awesome Lists containing this project

README

          

# ๐Ÿค– Prompt

**A comprehensive .NET library for Azure OpenAI prompt engineering**

[![NuGet](https://img.shields.io/nuget/v/prompt-llm-aoi?style=flat-square&logo=nuget&color=004880)](https://www.nuget.org/packages/prompt-llm-aoi)
[![NuGet Downloads](https://img.shields.io/nuget/dt/prompt-llm-aoi?style=flat-square&logo=nuget&color=004880)](https://www.nuget.org/packages/prompt-llm-aoi)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](LICENSE)
[![.NET](https://img.shields.io/badge/.NET-8.0-blueviolet?style=flat-square&logo=dotnet)](https://dotnet.microsoft.com/download/dotnet/8.0)
[![CodeQL](https://img.shields.io/github/actions/workflow/status/sauravbhattacharya001/prompt/codeql.yml?style=flat-square&label=CodeQL&logo=github)](https://github.com/sauravbhattacharya001/prompt/actions/workflows/codeql.yml)
[![CI](https://img.shields.io/github/actions/workflow/status/sauravbhattacharya001/prompt/ci.yml?style=flat-square&label=CI&logo=github)](https://github.com/sauravbhattacharya001/prompt/actions/workflows/ci.yml)
[![Publish](https://img.shields.io/github/actions/workflow/status/sauravbhattacharya001/prompt/nuget-publish.yml?style=flat-square&label=Publish&logo=github)](https://github.com/sauravbhattacharya001/prompt/actions/workflows/nuget-publish.yml)
[![codecov](https://img.shields.io/codecov/c/github/sauravbhattacharya001/prompt?style=flat-square&logo=codecov)](https://codecov.io/gh/sauravbhattacharya001/prompt)
![Tests](https://img.shields.io/badge/tests-1011%20passed-brightgreen?style=flat-square)
[![Docker](https://img.shields.io/github/actions/workflow/status/sauravbhattacharya001/prompt/docker.yml?style=flat-square&label=Docker&logo=docker)](https://github.com/sauravbhattacharya001/prompt/actions/workflows/docker.yml)
[![Pages](https://img.shields.io/github/actions/workflow/status/sauravbhattacharya001/prompt/pages.yml?style=flat-square&label=Docs&logo=github)](https://sauravbhattacharya001.github.io/prompt/)
[![GitHub Release](https://img.shields.io/github/v/release/sauravbhattacharya001/prompt?style=flat-square&logo=github&color=blue)](https://github.com/sauravbhattacharya001/prompt/releases)
[![Last Commit](https://img.shields.io/github/last-commit/sauravbhattacharya001/prompt?style=flat-square&logo=git&color=orange)](https://github.com/sauravbhattacharya001/prompt/commits/main)
[![GitHub Stars](https://img.shields.io/github/stars/sauravbhattacharya001/prompt?style=flat-square&logo=github&color=gold)](https://github.com/sauravbhattacharya001/prompt/stargazers)
[![Open Issues](https://img.shields.io/github/issues/sauravbhattacharya001/prompt?style=flat-square&logo=github&color=red)](https://github.com/sauravbhattacharya001/prompt/issues)
[![Contributors](https://img.shields.io/github/contributors/sauravbhattacharya001/prompt?style=flat-square&logo=github&color=green)](https://github.com/sauravbhattacharya001/prompt/graphs/contributors)
[![Repo Size](https://img.shields.io/github/repo-size/sauravbhattacharya001/prompt?style=flat-square&logo=github&color=lightgrey)](https://github.com/sauravbhattacharya001/prompt)
[![Dependabot](https://img.shields.io/badge/Dependabot-enabled-brightgreen?style=flat-square&logo=dependabot)](https://github.com/sauravbhattacharya001/prompt/security/dependabot)

Send prompts to Azure OpenAI and get responses โ€” with templates, chains, safety guards, token management, version control, and a full prompt engineering toolkit. Zero boilerplate.

[Installation](#installation) ยท [Quick Start](#quick-start) ยท [Full Class Library](#full-class-library) ยท [API Reference](#api-reference) ยท [Docs](https://sauravbhattacharya001.github.io/prompt/) ยท [Changelog](CHANGELOG.md)

---

## ๐Ÿš€ Why Prompt?

Most Azure OpenAI wrappers give you a thin HTTP client and call it a day. **Prompt** gives you the full toolkit:

- **One line to start** โ€” `await Main.GetResponseAsync("...")` handles auth, retries, and connection pooling
- **Templates + Chains** โ€” build reusable, composable prompt pipelines without string concatenation
- **Safety built in** โ€” injection detection, token budgeting, and quality scoring out of the box
- **100+ specialized classes** โ€” from A/B testing to workflow orchestration, every prompt engineering pattern is covered
- **Production-ready** โ€” 1,000+ tests, NuGet package, Docker image, full docs site

If you've ever copy-pasted prompt strings across files, hand-rolled retry logic, or wondered if your prompt is vulnerable to injection โ€” this library exists so you don't have to.

---

## โœจ Features

### Core
- **Single method call** โ€” `GetResponseAsync()` handles everything
- **Multi-turn conversations** โ€” `Conversation` class maintains message history across turns
- **Configurable parameters** โ€” `PromptOptions` class with presets (`ForCodeGeneration()`, `ForCreativeWriting()`, etc.)
- **Automatic retries** โ€” Exponential backoff for 429 rate-limit and 503 errors
- **Cancellation support** โ€” Pass `CancellationToken` to cancel long-running requests
- **Connection pooling** โ€” Thread-safe singleton client with double-check locking

### Prompt Engineering
- **Templates** โ€” `PromptTemplate` with `{{variable}}` placeholders, defaults, validation, and composition
- **Chains** โ€” `PromptChain` pipelines multiple prompts sequentially, each step's output feeding into the next
- **Composer** โ€” `PromptComposer` fluent builder for structured prompts (persona, context, task, constraints, examples, output format)
- **Few-shot builder** โ€” `FewShotBuilder` for structured few-shot prompt construction with 5 formats and token-budget awareness
- **Library** โ€” `PromptLibrary` central template registry with search, categories, tags, and 8 built-in templates
- **Router** โ€” `PromptRouter` intent-based routing with keyword/regex scoring and fallback support

### Safety & Quality
- **Guard** โ€” `PromptGuard` injection detection (10 attack vectors), quality scoring (0โ€“100, Aโ€“F grade), sanitization, and format wrapping
- **Token budget** โ€” `TokenBudget` auto-trims conversations to fit model context windows with 3 trim strategies
- **Test suite** โ€” `PromptTestSuite` automated prompt evaluation framework with 10 assertion types and pluggable response providers

### Management
- **Version manager** โ€” `PromptVersionManager` version history, line-level diffs, and rollback for prompt templates
- **Response parser** โ€” `ResponseParser` extracts structured data (JSON, lists, tables, key-value pairs, code blocks) from LLM responses
- **Serialization** โ€” All classes support JSON round-trip (ToJson/FromJson/SaveToFileAsync/LoadFromFileAsync)

### Infrastructure
- **1,000+ tests** โ€” Comprehensive xUnit test suite
- **Cross-platform** โ€” Environment variable resolution on Windows, Linux, and macOS
- **NuGet ready** โ€” Published as [`prompt-llm-aoi`](https://www.nuget.org/packages/prompt-llm-aoi)

### Full Class Library

๐Ÿ”ง Core Runtime โ€” The essentials for sending prompts and managing conversations

| Class | Description |
|-------|-------------|
| [`Main`](#maingettresponseasync) | Single-call Azure OpenAI completions with retries and cancellation |
| [`Conversation`](#conversation-class) | Multi-turn message history with configurable model parameters |
| `PromptOptions` | Model parameter presets (code generation, creative writing, data extraction, summarization) |
| `PromptRetryPolicy` | Configurable retry with backoff, circuit breaker, and error classification |
| `PromptCache` | Response caching with configurable expiration policies |
| `PromptRateLimiter` | Request rate limiting and throttling |
| `PromptFallbackChain` | Resilient multi-model execution with automatic fallback |
| `PromptLoadBalancer` | Distribute requests across multiple endpoints |
| `ResponseParser` | Extract JSON, lists, tables, key-value pairs, and code blocks from LLM responses |
| `StreamChunk` | Typed chunk model for streaming response parsing |
| `PromptStreamParser` | Real-time streaming content extraction |
| `TokenBudget` | Context window management with 3 trim strategies and 15+ model presets |

โœ๏ธ Prompt Engineering โ€” Templates, chains, composition, and routing

| Class | Description |
|-------|-------------|
| [`PromptTemplate`](#prompttemplate-class) | Reusable `{{variable}}` templates with defaults, validation, and composition |
| [`PromptChain`](#promptchain-class) | Multi-step LLM pipeline with variable forwarding between steps |
| `PromptComposer` | Fluent structured prompt builder with semantic sections and 4 presets |
| `FewShotBuilder` | Structured few-shot prompt construction with 5 formats and token-budget integration |
| `PromptLibrary` | Central template registry with CRUD, search by category/tag, merge, and 8 built-in templates |
| `PromptRouter` | Intent-based prompt routing with keyword/regex scoring and fallback |
| `PromptConditional` | Conditional logic for prompt templates |
| `PromptContextBuilder` | Priority-based prompt context assembly with token budgeting |
| `PromptContextCompressor` | Intelligent conversation context compression with 4 strategies |
| `PromptInheritance` | Block-based template inheritance with `{{super}}` support |
| `PromptInterpolator` | Pipe-based template variable transformations |
| `PromptMerger` | Merge and combine multiple prompt templates |
| `PromptPipeline` | Configurable prompt processing pipeline with middleware |
| `PromptWorkflow` | DAG-based prompt workflow engine |
| `PromptSignature` | Strongly-typed prompt signatures (DSPy-style) |
| `PromptSamplerConfig` | LLM sampling parameter builder |
| `PromptSchemaGenerator` | Fluent structured output schema builder |
| `PromptToolFormatter` | Unified tool/function calling format across LLM providers |
| `PromptSlotFiller` | Structured slot extraction and multi-turn filling |
| `PromptExpander` | Expand compressed prompts into full form |
| `PromptMinifier` | Prompt compression and whitespace optimization |
| `PromptTokenOptimizer` | Token usage optimization and prompt compression |
| `PromptSplitter` | Boundary-aware content chunking for long prompts |

๐Ÿ›ก๏ธ Safety & Quality โ€” Injection detection, validation, compliance

| Class | Description |
|-------|-------------|
| `PromptGuard` | Injection detection (10 attack vectors), quality scoring, sanitization, and output format wrapping |
| `PromptInjectionDetector` | Specialized prompt injection attack detection |
| `PromptRiskAssessor` | Multi-dimensional security risk analysis for prompts |
| `PromptSanitizer` | Prompt cleaning and normalization utility |
| `PromptComplianceChecker` | Policy and regulatory compliance validation for prompts |
| `PromptQualityGate` | Configurable pass/fail gate for prompt validation |
| `PromptOutputValidator` | LLM response validation against configurable rules |
| `PromptGrammarValidator` | Response format validation with 11 rule types (regex, JSON, length, structure) |
| `PromptLinter` | Rule-based static analysis for LLM prompts |
| `PromptBiasDetector` | Detect potential biases in prompts |
| `PromptChecklist` | Pre-flight validation checklists |
| `SerializationGuards` | Input validation and safety checks for deserialization |

๐Ÿงช Testing & Evaluation โ€” Test suites, benchmarks, fuzzing

| Class | Description |
|-------|-------------|
| `PromptTestSuite` | Automated prompt evaluation with 10 assertion types and pluggable response providers |
| `PromptBenchmarkSuite` | Benchmark prompt variants against test scenarios |
| `PromptGoldenTester` | Snapshot testing for prompt outputs |
| `PromptFuzzer` | Robustness testing โ€” generates prompt variants via 7 mutation strategies |
| `PromptABTester` | A/B testing framework for comparing prompt variant performance |
| `PromptMatrix` | Combinatorial template variable testing |
| `PromptResponseEvaluator` | Heuristic quality scoring across 5 dimensions |
| `PromptScorecardBuilder` | Custom evaluation rubrics with weighted scoring |
| `PromptConversationSimulator` | Simulated multi-turn conversations for testing |
| `PromptDatasetBuilder` | Evaluation and fine-tuning dataset builder |
| `PromptVariantGenerator` | Automated prompt variant generation for testing |
| `PromptCoverageAnalyzer` | Library coverage analysis with health scoring |

๐Ÿ“Š Analytics & Observability โ€” Profiling, auditing, reporting

| Class | Description |
|-------|-------------|
| `PromptAnalytics` | Prompt usage analytics and metrics collection |
| `PromptAuditLog` | Immutable hash-chained execution audit trail |
| `PromptCostEstimator` | Token-based cost estimation for prompt execution |
| `PromptCostOptimizer` | Cost optimization recommendations |
| `PromptPerformanceProfiler` | Execution profiling with percentiles, comparison, and reports |
| `PromptUsageReport` | Comprehensive usage reporting with time-bucketed breakdowns and cost analysis |
| `PromptHistory` | Prompt execution history tracking and retrieval |
| `PromptHealthCheck` | Library-wide quality analysis and health scoring |
| `PromptHeatmap` | Visual prompt usage and performance heatmaps |
| `PromptCanary` | Canary deployment monitoring for prompt changes |

๐Ÿ”„ Versioning & Lifecycle โ€” Version control, snapshots, promotion

| Class | Description |
|-------|-------------|
| `PromptVersionManager` | Version history, line-level diffs, and rollback for templates |
| `PromptSnapshotManager` | Point-in-time library snapshots with diff comparison and rollback |
| `PromptPromotionManager` | Lifecycle stage management with approval gates and rollback |
| `PromptDeprecationManager` | Deprecation tracking and migration |
| `PromptChangeImpactAnalyzer` | Blast-radius analysis for prompt template changes |
| `PromptChangelogGenerator` | Formatted changelogs from version history with multiple output formats |
| `PromptDiff` | Line-level diff comparison between prompt versions |
| `PromptDiffEngine` | Advanced diff computation engine |
| `PromptDiffViewer` | Visual diff rendering |
| `PromptMigrationAssistant` | Cross-provider prompt adaptation assistant |
| `PromptFeatureFlag` | Feature-flagged prompt variants |

๐Ÿ” Analysis & Intelligence โ€” Understanding, classifying, and improving prompts

| Class | Description |
|-------|-------------|
| `PromptDebugger` | Deep structural analysis โ€” anti-pattern detection, clarity scoring, suggested fixes |
| `PromptExplainer` | Analyze prompts for techniques, sections, and improvement suggestions |
| `PromptComplexityScorer` | Multi-dimensional prompt complexity analysis |
| `PromptIntentClassifier` | Intent classification for prompt routing |
| `PromptMetadataExtractor` | Structured prompt analysis for routing and analytics |
| `PromptEmotionAnalyzer` | Emotional tone analysis for prompts |
| `PromptSimilarityAnalyzer` | Multi-metric prompt comparison and duplicate detection |
| `PromptSemanticSearch` | Semantic similarity search across prompt libraries |
| `PromptFingerprint` | Content-based prompt fingerprinting for deduplication |
| `PromptDependencyGraph` | DAG analysis for prompt pipelines |
| `PromptCompatibilityChecker` | Cross-provider prompt portability analysis |
| `PromptRefactorer` | Automated prompt refactoring and optimization suggestions |
| `PromptCoEvolver` | Co-evolutionary prompt improvement |
| `PromptStyleTransfer` | Heuristic prompt tone and style rewriting |
| `PromptNegotiator` | Iterative prompt refinement with validation feedback loops |

๐Ÿ“ฆ Import/Export & Tooling โ€” Serialization, formatting, documentation

| Class | Description |
|-------|-------------|
| `PromptAnnotation` | Structured inline comments and metadata for prompts |
| `PromptCatalogExporter` | Export prompt library to HTML, CSV, and JSON formats |
| `PromptMarkdownExporter` | Export and import prompt libraries as Markdown |
| `PromptDocGenerator` | Auto-generate documentation from prompt templates |
| `PromptChainVisualizer` | Mermaid, DOT, and ASCII flowchart generation from prompt chains |
| `PromptFlowChart` | Visual flowchart generation |
| `PromptChatFormatter` | Multi-provider chat message formatting |
| `PromptLocalizer` | Prompt localization and translation management |
| `PromptLocalizationManager` | Localization resource management |
| `PromptAudienceAdapter` | Adapt prompts for different audience levels |
| `PromptDialect` | Provider-specific prompt dialect conversion |
| `PromptGlossary` | Domain-specific terminology management |
| `PromptEnvironmentManager` | Environment-specific prompt configuration management |
| `PromptBatchProcessor` | Batch execution of prompts with concurrency control |
| `PromptReplayRecorder` | VCR-style prompt interaction recording and replay |
| `PromptEnsemble` | Multi-response aggregation (majority vote, best-of-N, consensus) |
| `PromptAutopilot` | Automated prompt improvement loops |
| `PromptErrorRecovery` | Graceful error handling and recovery strategies |
| `PromptArchetypeLibrary` | Pre-built prompt archetypes and patterns |
| `PromptConflictDetector` | Detect conflicting prompt instructions |

## Prerequisites

- [.NET 8.0](https://dotnet.microsoft.com/download/dotnet/8.0) or later
- An [Azure OpenAI](https://azure.microsoft.com/en-us/products/ai-services/openai-service) resource with a deployed model

## Installation

```bash
dotnet add package prompt-llm-aoi
```

## Configuration

Set the following environment variables:

| Variable | Description | Example |
|---|---|---|
| `AZURE_OPENAI_API_URI` | Your Azure OpenAI endpoint URI | `https://myresource.openai.azure.com/` |
| `AZURE_OPENAI_API_KEY` | Your Azure OpenAI API key | `sk-...` |
| `AZURE_OPENAI_API_MODEL` | The deployed model name | `gpt-4` |

> **Note:** On Windows, the library checks Process โ†’ User โ†’ Machine scopes. On Linux/macOS, it reads from the process environment (shell exports, Docker env, systemd, etc.).

## Quick Start

```csharp
using Prompt;

// Simple prompt (single-turn)
string? response = await Main.GetResponseAsync("Explain quantum computing in simple terms.");
Console.WriteLine(response);
```

### With Custom Options

Use `PromptOptions` to customize model behavior for any use case:

```csharp
using Prompt;

// Code generation โ€” low temperature, high token limit
var codeOpts = PromptOptions.ForCodeGeneration();
string? code = await Main.GetResponseAsync(
"Write a merge sort in C#",
options: codeOpts);

// Creative writing โ€” high temperature
var creativeOpts = PromptOptions.ForCreativeWriting();
string? story = await Main.GetResponseAsync(
"Write a short story about a time-traveling cat",
options: creativeOpts);

// Custom configuration
var custom = new PromptOptions
{
Temperature = 0.4f,
MaxTokens = 4000,
TopP = 0.9f,
FrequencyPenalty = 0.3f,
PresencePenalty = 0.1f
};
string? result = await Main.GetResponseAsync("Summarize this article...", options: custom);
```

**Built-in presets:**

| Preset | Temperature | MaxTokens | TopP | Use Case |
|---|---|---|---|---|
| `ForCodeGeneration()` | 0.1 | 4000 | 0.95 | Deterministic code output |
| `ForCreativeWriting()` | 0.9 | 2000 | 0.9 | Stories, poems, creative text |
| `ForDataExtraction()` | 0.0 | 2000 | 1.0 | JSON, structured output |
| `ForSummarization()` | 0.3 | 1000 | 0.9 | Text summarization |

## Multi-Turn Conversations

The `Conversation` class maintains message history so the model has full context:

```csharp
using Prompt;

var conv = new Conversation("You are a helpful math tutor.");

string? r1 = await conv.SendAsync("What is 2+2?");
Console.WriteLine(r1); // "4"

string? r2 = await conv.SendAsync("Now multiply that by 3.");
Console.WriteLine(r2); // "12" โ€” the model remembers the context!

string? r3 = await conv.SendAsync("What was my first question?");
Console.WriteLine(r3); // It knows: "What is 2+2?"
```

### Customizing Parameters

Each conversation can have its own model parameters, either via `PromptOptions` or individual properties:

```csharp
// Using PromptOptions (recommended)
var opts = PromptOptions.ForCreativeWriting();
var conv = new Conversation("You are a creative writer.", opts);

// Or set properties individually
var conv2 = new Conversation("You are a creative writer.")
{
Temperature = 1.2f, // More creative
MaxTokens = 2000, // Longer responses
TopP = 0.9f,
FrequencyPenalty = 0.5f // Less repetition
};

string? story = await conv.SendAsync("Write a short story about a robot.");
```

### Replaying Conversations

Inject prior messages to give the model context from a previous session:

```csharp
var conv = new Conversation("You are a coding assistant.");
conv.AddUserMessage("How do I sort a list in C#?");
conv.AddAssistantMessage("Use list.Sort() for in-place sorting or list.OrderBy() for LINQ.");

// Now continue the conversation with full context
string? response = await conv.SendAsync("Show me the LINQ version with a custom comparer.");
```

### Conversation History

Export the conversation for logging, serialization, or display:

```csharp
var conv = new Conversation("System prompt");
conv.AddUserMessage("Hello");
conv.AddAssistantMessage("Hi there!");

List<(string Role, string Content)> history = conv.GetHistory();
foreach (var (role, content) in history)
Console.WriteLine($"[{role}] {content}");

// [system] System prompt
// [user] Hello
// [assistant] Hi there!
```

### Clearing History

Reset the conversation while preserving the system prompt:

```csharp
var conv = new Conversation("You are helpful.");
conv.AddUserMessage("Hello");
conv.Clear(); // Removes user/assistant messages, keeps system prompt
```

### Save & Load Conversations

Save a conversation to JSON and restore it later โ€” perfect for persisting sessions across app restarts, sharing conversations, or implementing conversation history:

```csharp
var conv = new Conversation("You are a coding tutor.");
await conv.SendAsync("Explain SOLID principles");
await conv.SendAsync("Show me an example of SRP");

// Save to JSON string
string json = conv.SaveToJson();

// Save to file
await conv.SaveToFileAsync("session.json");

// Later... restore from JSON
var restored = Conversation.LoadFromJson(json);

// Or restore from file
var fromFile = await Conversation.LoadFromFileAsync("session.json");

// Continue the conversation with full context
string? response = await fromFile.SendAsync("Now show me OCP");
```

The serialized JSON includes all messages and model parameters (temperature, max tokens, etc.), so the restored conversation is an exact replica:

```json
{
"messages": [
{ "role": "system", "content": "You are a coding tutor." },
{ "role": "user", "content": "Explain SOLID principles" },
{ "role": "assistant", "content": "SOLID stands for..." }
],
"parameters": {
"temperature": 0.7,
"maxTokens": 800,
"topP": 0.95,
"frequencyPenalty": 0,
"presencePenalty": 0,
"maxRetries": 3
}
}
```

## Prompt Templates

The `PromptTemplate` class lets you define reusable prompts with `{{variable}}` placeholders. Set defaults, validate inputs, compose templates together, and serialize them for sharing.

### Basic Usage

```csharp
using Prompt;

var template = new PromptTemplate(
"You are a {{role}} assistant. Help the user with {{topic}}.",
new Dictionary { ["role"] = "helpful" }
);

// Render with variables (role uses default, topic is provided)
string prompt = template.Render(new Dictionary
{
["topic"] = "C# programming"
});
// โ†’ "You are a helpful assistant. Help the user with C# programming."
```

### Variable Introspection

```csharp
var template = new PromptTemplate(
"Translate {{text}} from {{source}} to {{target}}.",
new Dictionary { ["source"] = "English" }
);

HashSet all = template.GetVariables();
// { "text", "source", "target" }

HashSet required = template.GetRequiredVariables();
// { "text", "target" } โ€” source has a default
```

### Strict vs. Non-Strict Rendering

```csharp
var template = new PromptTemplate("Hello {{name}}, you are {{role}}!");

// Strict (default) โ€” throws if variables are missing
template.Render(); // โŒ InvalidOperationException

// Non-strict โ€” leaves unresolved placeholders as-is
string result = template.Render(strict: false);
// โ†’ "Hello {{name}}, you are {{role}}!"
```

### Composing Templates

Chain templates together to build complex prompts from reusable parts:

```csharp
var persona = new PromptTemplate(
"You are a {{role}} with expertise in {{domain}}.",
new Dictionary { ["role"] = "senior developer" }
);

var task = new PromptTemplate(
"Review this code and suggest improvements:\n{{code}}");

var combined = persona.Compose(task);

string prompt = combined.Render(new Dictionary
{
["domain"] = "C#",
["code"] = "public void Foo() { /* ... */ }"
});
```

### Render & Send in One Call

Skip the manual render step โ€” send directly to Azure OpenAI:

```csharp
// Single-turn
var template = new PromptTemplate("Explain {{concept}} in simple terms.");
string? response = await template.RenderAndSendAsync(
new Dictionary { ["concept"] = "recursion" },
systemPrompt: "You are a teacher."
);

// Multi-turn (with existing Conversation)
var conv = new Conversation("You are a coding tutor.");
await template.RenderAndSendAsync(conv,
new Dictionary { ["concept"] = "closures" });
```

### Save & Load Templates

```csharp
var template = new PromptTemplate(
"Summarize {{text}} in {{style}} style.",
new Dictionary { ["style"] = "concise" }
);

// Save to file
await template.SaveToFileAsync("summarizer.json");

// Load from file
var loaded = await PromptTemplate.LoadFromFileAsync("summarizer.json");

// Or use JSON strings directly
string json = template.ToJson();
var restored = PromptTemplate.FromJson(json);
```

## Prompt Chains

The `PromptChain` class lets you build multi-step LLM pipelines where each step's output automatically becomes a variable for subsequent steps. Perfect for summarize-then-translate, extract-then-analyze, or any sequential reasoning pattern.

### Basic Chain

```csharp
using Prompt;

var chain = new PromptChain()
.AddStep("summarize",
new PromptTemplate("Summarize this text in 2 sentences: {{text}}"),
"summary")
.AddStep("translate",
new PromptTemplate("Translate to French: {{summary}}"),
"french")
.AddStep("keywords",
new PromptTemplate("Extract 5 keywords from: {{summary}}"),
"keywords");

var result = await chain.RunAsync(new Dictionary
{
["text"] = "Your long article text here..."
});

Console.WriteLine(result.FinalResponse); // keywords output
Console.WriteLine(result.GetOutput("summary")); // the summary
Console.WriteLine(result.GetOutput("french")); // the French translation
```

### Chain Configuration

```csharp
var chain = new PromptChain()
.WithSystemPrompt("You are a precise analyst.")
.WithMaxRetries(5)
.AddStep("extract",
new PromptTemplate("Extract key facts from: {{document}}"),
"facts")
.AddStep("analyze",
new PromptTemplate("Analyze these facts for trends: {{facts}}"),
"analysis");
```

### Validation

Check that all variables are satisfied before running (no API calls):

```csharp
var chain = new PromptChain()
.AddStep("s1", new PromptTemplate("Process: {{input}}"), "result")
.AddStep("s2", new PromptTemplate("Refine: {{result}}"), "final");

List errors = chain.Validate(
new Dictionary { ["input"] = "test" });

if (errors.Count == 0)
Console.WriteLine("Chain is valid!");
else
errors.ForEach(Console.WriteLine);
```

### Chain Results

Every step is tracked with timing, rendered prompts, and responses:

```csharp
var result = await chain.RunAsync(initialVars);

Console.WriteLine($"Total time: {result.TotalElapsed.TotalSeconds}s");
Console.WriteLine($"Steps: {result.Steps.Count}");

foreach (var step in result.Steps)
{
Console.WriteLine($" [{step.StepName}] {step.Elapsed.TotalMilliseconds}ms");
Console.WriteLine($" Prompt: {step.RenderedPrompt}");
Console.WriteLine($" Response: {step.Response}");
}

// Export results as JSON for logging/analysis
string json = result.ToJson();
```

### Save & Load Chains

```csharp
var chain = new PromptChain()
.WithSystemPrompt("Be helpful")
.AddStep("step1", new PromptTemplate("Summarize: {{text}}"), "summary");

// Save to file
await chain.SaveToFileAsync("my-chain.json");

// Load from file
var loaded = await PromptChain.LoadFromFileAsync("my-chain.json");

// Or use JSON strings
string chainJson = chain.ToJson();
var restored = PromptChain.FromJson(chainJson);
```

## Usage Examples

### System Prompt

Set the assistant's behavior to control the style of responses:

```csharp
string? response = await Main.GetResponseAsync(
"Summarize this text: ...",
systemPrompt: "You are a concise summarizer. Respond in 2-3 sentences max.");
```

### Cancellation

Cancel long-running requests with a timeout:

```csharp
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10));

try
{
string? response = await Main.GetResponseAsync("Hello!", cancellationToken: cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Request timed out.");
}
```

### Custom Retry Policy

Adjust the retry count for transient failures:

```csharp
// Default: 3 retries with exponential backoff
string? response = await Main.GetResponseAsync("Hello!");

// Custom: 5 retries for high-reliability scenarios
string? response = await Main.GetResponseAsync("Hello!", maxRetries: 5);
```

### Reset Client

Force the client to re-read environment variables (useful for runtime config changes):

```csharp
Main.ResetClient();
```

## API Reference

### `Main.GetResponseAsync()`

```csharp
public static async Task GetResponseAsync(
string prompt,
string? systemPrompt = null,
int maxRetries = 3,
CancellationToken cancellationToken = default)
```

| Parameter | Type | Default | Description |
|---|---|---|---|
| `prompt` | `string` | *(required)* | The user prompt to send |
| `systemPrompt` | `string?` | `null` | Optional system prompt for assistant behavior |
| `maxRetries` | `int` | `3` | Maximum retries for transient failures |
| `cancellationToken` | `CancellationToken` | `default` | Token to cancel the operation |

**Returns:** `Task` โ€” The model's response text, or `null` if no response was generated.

**Throws:**
- `ArgumentException` โ€” if `prompt` is null or empty
- `ArgumentOutOfRangeException` โ€” if `maxRetries` is negative
- `InvalidOperationException` โ€” if required environment variables are missing
- `OperationCanceledException` โ€” if cancelled via token

### `Main.ResetClient()`

```csharp
public static void ResetClient()
```

Clears the cached client, forcing re-initialization on the next call. Thread-safe.

### `Conversation` Class

```csharp
public class Conversation
```

Multi-turn conversation manager with full message history and configurable model parameters.

#### Constructor

| Parameter | Type | Default | Description |
|---|---|---|---|
| `systemPrompt` | `string?` | `null` | Optional system prompt for the entire conversation |

#### Methods

| Method | Returns | Description |
|---|---|---|
| `SendAsync(message, cancellationToken)` | `Task` | Sends a message and returns the response. Both are added to history. |
| `AddUserMessage(message)` | `void` | Adds a user message to history without calling the API. |
| `AddAssistantMessage(message)` | `void` | Adds an assistant message to history without calling the API. |
| `Clear()` | `void` | Clears history but preserves the system prompt. |
| `GetHistory()` | `List<(string Role, string Content)>` | Returns a snapshot of the conversation. |
| `SaveToJson(indented)` | `string` | Serializes the conversation (messages + parameters) to a JSON string. |
| `LoadFromJson(json)` | `Conversation` | *Static.* Restores a conversation from a JSON string. |
| `SaveToFileAsync(filePath, indented, cancellationToken)` | `Task` | Saves the conversation to a JSON file. |
| `LoadFromFileAsync(filePath, cancellationToken)` | `Task` | *Static.* Loads a conversation from a JSON file. |

#### Properties

| Property | Type | Default | Range | Description |
|---|---|---|---|---|
| `MessageCount` | `int` | โ€” | โ€” | Number of messages including system prompt |
| `Temperature` | `float` | `0.7` | `0.0โ€“2.0` | Sampling temperature |
| `MaxTokens` | `int` | `800` | `โ‰ฅ 1` | Maximum response tokens |
| `TopP` | `float` | `0.95` | `0.0โ€“1.0` | Nucleus sampling |
| `FrequencyPenalty` | `float` | `0.0` | `-2.0โ€“2.0` | Frequency penalty |
| `PresencePenalty` | `float` | `0.0` | `-2.0โ€“2.0` | Presence penalty |
| `MaxRetries` | `int` | `3` | `โ‰ฅ 0` | Retry count for transient failures |

### `PromptTemplate` Class

```csharp
public class PromptTemplate
```

Reusable prompt template with `{{variable}}` placeholders, default values, and composition.

#### Constructor

| Parameter | Type | Default | Description |
|---|---|---|---|
| `template` | `string` | *(required)* | Template string with `{{variable}}` placeholders |
| `defaults` | `Dictionary?` | `null` | Default values for variables |

#### Methods

| Method | Returns | Description |
|---|---|---|
| `Render(variables, strict)` | `string` | Renders the template by replacing placeholders. Strict mode throws on missing variables. |
| `RenderAndSendAsync(variables, systemPrompt, maxRetries, cancellationToken)` | `Task` | Renders and sends as a single-turn prompt via `Main.GetResponseAsync()`. |
| `RenderAndSendAsync(conversation, variables, cancellationToken)` | `Task` | Renders and sends as a message in an existing `Conversation`. |
| `GetVariables()` | `HashSet` | Returns all variable names found in the template. |
| `GetRequiredVariables()` | `HashSet` | Returns variable names that have no default value. |
| `SetDefault(name, value)` | `void` | Sets or updates a default value for a variable. |
| `RemoveDefault(name)` | `bool` | Removes a default value, making the variable required. |
| `Compose(other, separator)` | `PromptTemplate` | Combines two templates with merged defaults. |
| `ToJson(indented)` | `string` | Serializes the template to JSON. |
| `FromJson(json)` | `PromptTemplate` | *Static.* Deserializes a template from JSON. |
| `SaveToFileAsync(filePath, indented, cancellationToken)` | `Task` | Saves the template to a JSON file. |
| `LoadFromFileAsync(filePath, cancellationToken)` | `Task` | *Static.* Loads a template from a JSON file. |

#### Properties

| Property | Type | Description |
|---|---|---|
| `Template` | `string` | The raw template string |
| `Defaults` | `IReadOnlyDictionary` | Read-only copy of default values |

### `PromptChain` Class

```csharp
public class PromptChain
```

Multi-step prompt pipeline where each step's output feeds into subsequent steps as template variables.

#### Methods

| Method | Returns | Description |
|---|---|---|
| `AddStep(name, template, outputVariable)` | `PromptChain` | Adds a step to the chain (fluent). Output variable must be unique. |
| `WithSystemPrompt(systemPrompt)` | `PromptChain` | Sets the system prompt for all API calls (fluent). |
| `WithMaxRetries(maxRetries)` | `PromptChain` | Sets the retry count for API calls (fluent). |
| `RunAsync(initialVariables, cancellationToken)` | `Task` | Executes all steps sequentially. |
| `Validate(initialVariables)` | `List` | Checks that all required variables are satisfiable (no API calls). |
| `ToJson(indented)` | `string` | Serializes the chain definition to JSON. |
| `FromJson(json)` | `PromptChain` | *Static.* Deserializes a chain from JSON. |
| `SaveToFileAsync(filePath, indented, cancellationToken)` | `Task` | Saves the chain definition to a JSON file. |
| `LoadFromFileAsync(filePath, cancellationToken)` | `Task` | *Static.* Loads a chain from a JSON file. |

#### Properties

| Property | Type | Description |
|---|---|---|
| `StepCount` | `int` | Number of steps in the chain |
| `Steps` | `IReadOnlyList` | Read-only view of chain steps |

### `ChainResult` Class

| Property / Method | Type | Description |
|---|---|---|
| `Steps` | `IReadOnlyList` | Ordered step results |
| `Variables` | `IReadOnlyDictionary` | All accumulated variables |
| `TotalElapsed` | `TimeSpan` | Total execution time |
| `FinalResponse` | `string?` | Last step's response (convenience) |
| `GetOutput(variableName)` | `string?` | Get a specific step's output by variable name |
| `ToJson(indented)` | `string` | Serialize results to JSON for logging |

### Default Model Parameters

| Parameter | Value |
|---|---|
| Temperature | 0.7 |
| Max Tokens | 800 |
| Top P | 0.95 |
| Frequency Penalty | 0 |
| Presence Penalty | 0 |

## Tech Stack

| Component | Technology |
|---|---|
| Language | C# 12 |
| Framework | .NET 8.0 |
| SDK | [Azure.AI.OpenAI](https://www.nuget.org/packages/Azure.AI.OpenAI) 2.1.0 |
| Retry | Azure.Core pipeline (exponential backoff with jitter) |
| Security | [CodeQL](https://github.com/sauravbhattacharya001/prompt/actions/workflows/codeql.yml) |
| Package | [NuGet](https://www.nuget.org/packages/prompt-llm-aoi) |

## Architecture

```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Your Application โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Prompt Library (this) โ”‚
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€ Prompt Engineering โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ PromptTemplate PromptChain PromptComposer โ”‚ โ”‚
โ”‚ โ”‚ FewShotBuilder PromptLibrary PromptRouter โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€ Safety & Quality โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ PromptGuard TokenBudget PromptTestSuite โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€ Management โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ PromptVersionManager ResponseParser โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ”‚ โ”‚
โ”‚ โ”Œโ”€โ”€โ”€ Runtime โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚
โ”‚ โ”‚ Main (Singleton) Conversation PromptOptions โ”‚ โ”‚
โ”‚ โ”‚ Env Config Retry Pipeline โ”‚ โ”‚
โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Azure OpenAI Service โ”‚
โ”‚ Chat Completions API (GPT-4) โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

## Contributing

Contributions are welcome! Please:

1. Fork the repository
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## License

This project is licensed under the MIT License โ€” see the [LICENSE](LICENSE) file for details.

---

Made by [Saurav Bhattacharya](https://github.com/sauravbhattacharya001)