Metadata-Version: 2.4
Name: prompt-template-manager
Version: 1.0.1
Summary: A lightweight and extensible Python library for managing, versioning, and composing reusable prompt templates from YAML or text files.
Home-page: https://promptmanager.davman.dev
Author: Dave Manufor
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Linguistic
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Operating System :: OS Independent
Description-Content-Type: text/markdown
Requires-Dist: PyYAML
Dynamic: author
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: requires-dist
Dynamic: summary

# Prompt Manager

A robust **Python utility** for loading, versioning, and managing prompt templates from YAML files or flat file directories.

Prompt Manager is designed to solve the problem of managing a **large, complex, and evolving library of prompts**. It allows you to compose prompts from reusable parts, manage versions (e.g., for A/B testing or model-specific tuning), and safely inject variables at runtime.

---

## 📚 Table of Contents

* [Core Features](#-core-features)
* [Installation](#-installation)
* [Quick Start](#-quick-start)
* [YAML Configuration Schema](#-yaml-configuration-schema)

  * [Versioned Prompts (Recommended)](#1-versioned-prompts-recommended)
  * [Simple Prompts (Non-versioned)](#2-simple-prompts-non-versioned)
  * [Include Directives – {include prompt_name}](#3-include-directives--include-prompt_name)
  * [Variable Placeholders – {variable_name}](#4-variable-placeholders--variable_name)
* [Loading from a Directory](#-loading-from-a-directory)
* [API Reference](#-api-reference)

  * [Class: PromptManager](#class-promptmanager)
  * [Class: Prompt](#class-prompt)
* [Advanced Usage Examples](#-advanced-usage-examples)

  * [Example 1: Version A/B Testing](#example-1-version-ab-testing)
  * [Example 2: Partial Formatting (Chain-of-Thought)](#example-2-partial-formatting-chain-of-thought)
* [Validation Rules](#-validation-rules)
* [Testing](#-testing)
* [License](#-license)

---

## 🚀 Core Features

* **Centralized Prompt Management:** Load all prompts from a single structured YAML file.
* **Fallback Directory Loading:** Optionally load simple prompts from a directory of `.txt` files.
* **Prompt Versioning:** Native support for versions (e.g., `v1`, `v2`) with a `_default` key.
* **Recursive Composition:** Build complex prompts from smaller, reusable components using `{include ...}` directives.
* **Safe Variable Substitution:** Format prompts with `{variable}` placeholders using a strict or non-strict API.
* **Partial Formatting:** Fill variables incrementally for chain-of-thought or multi-step prompt sequences.
* **Configuration Validation:** Validate prompt configurations at load time to catch errors early.

---

## 💾 Installation

```bash
pip install prompt_template_manager
```

---

## ⚡ Quick Start

### 1. Create your `prompts.yaml` file

```yaml
# prompts.yaml

system_persona:
  _default: v2
  _meta:
    description: "The standard AI persona."
  v1: "You are a helpful AI."
  v2: "You are a helpful, concise, and polite AI assistant."

summarize_task:
  _default: v1
  v1: |
      {include system_persona}
      
      Your task is to summarize the following document into {num_sentences} sentences.
      Document: {text}
      
      Your summary:
```

### 2. Use `PromptManager` in your Python code

```python
from prompt_template_manager import PromptManager

try:
    manager = PromptManager('prompts.yaml')
    prompt = manager.get('summarize_task')

    my_data = {
        "num_sentences": 3,
        "text": "The quick brown fox jumps over the lazy dog."
    }

    formatted_prompt = prompt.format(my_data)
    print(formatted_prompt)

except FileNotFoundError:
    print("Error: prompts.yaml not found.")
except (ValueError, KeyError) as e:
    print(f"Error loading or getting prompt: {e}")
```

**Output:**

```
You are a helpful, concise, and polite AI assistant.

Your task is to summarize the following document into 3 sentences.
Document: The quick brown fox jumps over the lazy dog.

Your summary:
```

---

## 🧩 YAML Configuration Schema

The `PromptManager` supports two main structures for defining prompts.

### 1. Versioned Prompts (Recommended)

A “versioned prompt” is a dictionary containing:

* `_default` (Required): Default version key (e.g., `v1`).
* `v{number}` (Required): Version key(s) with prompt text.
* `_meta` (Optional): Arbitrary metadata.

**Example:**

```yaml
system_persona_default:
  _default: v2
  _meta:
    description: "The standard AI persona."
    author: "Admin"
  v1: "You are a helpful AI."
  v2: "You are a helpful, concise, and polite AI assistant. You always answer the user's question directly."
```

---

### 2. Simple Prompts (Non-versioned)

For simple, static prompts:

```yaml
simple_greeting: "Hello, {name}. This is a simple, non-versioned prompt."
```

> ⚠️ Not recommended for complex systems — lacks versioning, metadata, and include support.

---

### 3. Include Directives – `{include prompt_name}`

Compose prompts from other prompts:

```yaml
system_persona:
  _default: v1
  v1: "You are a helpful AI."
  
output_format_json:
  _default: v1
  v1: "Your response MUST be a single, valid JSON object."

generate_user_profile:
  _default: v1
  _meta:
    description: "Generates a JSON profile from a user's bio."
  v1: |
      {include system_persona}
      {include output_format_json}
      
      Analyze the following user bio and generate a profile.
      Bio: {bio_text}
```

Circular references (e.g., A includes B and B includes A) raise an **ImportError**.

---

### 4. Variable Placeholders – `{variable_name}`

* Any text enclosed in `{}` is treated as a variable.
* Variables are filled using `.format()` or `.partial()`.
* Variable names are validated on load.

---

## 📁 Loading from a Directory

You can also load prompts from a directory:

```
my_txt_prompts/
├── greet.txt
└── farewell.txt
```

```python
manager = PromptManager('my_txt_prompts/')
prompt = manager.get('greet')
```

**Notes:**

* Only `.txt` files are loaded.
* Filenames become prompt names.
* Each prompt defaults to version `v1`.
* No support for metadata, includes, or multiple versions.

---

## 🧠 API Reference

### **Class: `PromptManager`**

#### `__init__(self, source_path: str | Path)`

Loads and validates prompts from a YAML file or `.txt` directory.

**Raises:**
`FileNotFoundError`, `ValueError`, `yaml.YAMLError`

#### `get(self, name: str, version: int | None = None) -> Prompt`

Retrieves a fully resolved `Prompt` object.

**Raises:**
`KeyError`, `ValueError`, `ImportError`

---

### **Class: `Prompt`**

#### `format(self, data: dict, strict: bool = True) -> str`

Substitutes all variables.

#### `partial(self, data: dict) -> Prompt`

Partially fills variables, returning a new `Prompt`.

#### `get_raw_content(self) -> str`

Returns resolved, unformatted prompt text.

#### `get_variables(self) -> dict`

Lists remaining variable placeholders.

#### `get_meta(self) -> dict`

Returns the `_meta` dictionary (if any).

---

## ⚙️ Advanced Usage Examples

### Example 1: Version A/B Testing

```python
manager = PromptManager('prompts.yaml')

prompt_v2 = manager.get('system_persona')
prompt_v1 = manager.get('system_persona', version=1)

print(f"Default: {prompt_v2.get_raw_content()}")
print(f"V1: {prompt_v1.get_raw_content()}")
```

**Output:**

```
Default: You are a helpful, concise, and polite AI assistant.
V1: You are a helpful AI.
```

---

### Example 2: Partial Formatting (Chain-of-Thought)

```python
manager = PromptManager('prompts.yaml')
prompt = manager.get('summarize_task')

print(f"Original variables: {prompt.get_variables().keys()}")

partial_prompt = prompt.partial({
    "text": "This is a long document about the history of computing."
})

print(f"Partial variables: {partial_prompt.get_variables().keys()}")
print("--- Partial Content ---")
print(partial_prompt.get_raw_content())

final_prompt_str = partial_prompt.format({
    "num_sentences": 2
})

print("\n--- Final Content ---")
print(final_prompt_str)
```

---

## ✅ Validation Rules

**Prompt Names**

* Must be valid Python identifiers (e.g., `my_prompt`, `_internal_prompt`).

**Variable Names**

* Must start with a letter.
* May include letters, numbers, `-`, and `_`.
* Cannot end with `-` or `_`.

**Include Names**

* Follow the same rules as variable names.

Invalid configurations raise `ValueError` during load.

---

## 🧰 License

MIT License — Prompt Manager Contributors

---
