Skip to main content

A Python library for managing and using prompt templates for language models.

Project description

Prompt Depot

Prompt Depot is a Python library for storing, versioning, loading, and rendering prompt templates for LLM workflows.

It provides:

  • A versioned local filesystem store (LocalTemplateStore) for prompt templates and metadata
  • A renderer abstraction (PromptRenderer) for pluggable template engines
  • Built-in renderer implementations for Mako and Jinja2
  • A manager abstraction (PromptDepotManager) that coordinates store + renderer with version-aware caching
  • A CLI (promptdepot) for managing templates and versions from the terminal

Table of Contents

Features

  • Semantic-versioned prompt storage
  • Metadata validation using Pydantic models
  • List templates and list versions per template
  • Create new templates and versions (empty, cloned from latest, or with provided content)
  • Renderer abstraction to support multiple template engines
  • Manager API to fetch + render prompts through one call
  • CLI for creating, listing, and inspecting templates and versions
  • Settings loaded from [tool.promptdepot] in pyproject.toml
  • Optional dependencies for renderer engines (mako, jinja2)

Requirements

  • Python >= 3.10

Installation

Base package

pip install promptdepot

Or with uv:

uv add promptdepot

With Mako renderer support

pip install "promptdepot[mako]"

Or with uv:

uv add "promptdepot[mako]"

With Jinja2 renderer support

pip install "promptdepot[jinja2]"

Or with uv:

uv add "promptdepot[jinja2]"

Development dependencies

uv sync --frozen --all-extras --group dev

Project Structure

src/promptdepot/
  __init__.py
  py.typed
  manager.py
  cli/
    __init__.py         # Entry point (main function)
    main.py             # Typer app with sub-command groups
    settings.py         # Settings loaded from pyproject.toml
    templates.py        # templates create/ls/show commands
    versions.py         # versions create/ls/show commands
    utils.py            # get_store() helper
  renderers/
    __init__.py
    core.py
    mako.py
    jinja2.py
  stores/
    __init__.py
    core.py             # ABC, domain models, CreationStrategy
    local.py            # LocalTemplateStore implementation
tests/
  test_manager.py
  test_renderers_core.py
  test_renderers_mako.py
  test_renderers_jinja2.py
  test_stores_local.py
  test_cli_settings.py
  test_cli_utils.py
  test_cli_templates.py
  test_cli_versions.py
  test_prompts/           # Fixture data (prompt template directories)
.github/workflows/
  ci.yml

Core Concepts

Domain Models

All domain models live in promptdepot.stores.core and use Pydantic:

  • Template -- represents a template with its id and latest_version.
  • TemplateVersionMetadata -- metadata for a single version: template_id, version, created_at, description, author, tags, model, changelog.
  • TemplateVersion -- a version record containing template_id, version, and its metadata.

TemplateStore

Abstract contract for template persistence. Accepts an optional config mapping in its constructor:

  • list_templates() -> list[Template]
  • get_template(template_id) -> Template
  • create_template(template_id)
  • list_template_versions(template_id) -> list[TemplateVersion]
  • get_template_version(template_id, version) -> TemplateVersion
  • create_version(template_id, version, metadata?, *, strategy, content?)
  • get_template_version_content(template_id, version) -> str

PromptVersion accepts semantic versions (SemanticVersion) or plain strings.

PromptRenderer

Abstract base renderer with:

  • template
  • config
  • from_template(...) constructor helper
  • Required render(context: Mapping[str, Any]) -> str

CreationStrategy

Version creation behavior:

  • CreationStrategy.EMPTY -- create with empty template file content
  • CreationStrategy.FROM_PREVIOUS_VERSION -- copy content from the latest existing version
  • CreationStrategy.WITH_CONTENT -- create with explicitly provided content

PromptDepotManager

Composes a TemplateStore and a PromptRenderer class into a single API:

  • get_prompt(template_id, version, context) -> str

Behavior:

  • Fetches template content from the store on first use of a (template_id, version) pair
  • Instantiates the renderer with from_template(...)
  • Caches renderers by (template_id, version)
  • Accepts context as Mapping[str, Any]
  • Performs a shallow copy of default_config so renderer-side mutations to top-level keys do not leak

Prompt Metadata Schema

Each version is a single file named after its version. Metadata is declared in a YAML frontmatter block at the top of the file (the --- intro section), followed by the template body:

<base_path>/
  <template_id>/
    <version>.md

<version>.md format:

---
template_id: support_agent
version: 1.0.0
created_at: 2025-01-15T10:30:00
description: Support triage prompt
author: Your Name
tags: [support, prod]
model: gpt-4
changelog:
  - Initial release
---
Hello ${name}, how can I help you today?

Frontmatter fields (validated against TemplateVersionMetadata):

  • template_id: string matching the parent directory name
  • version: semantic version of this prompt version
  • created_at: datetime (defaults to datetime.now() when creating)
  • description: optional
  • author: optional
  • tags: set of strings (default empty)
  • model: optional model hint
  • changelog: list of strings (default empty)

Using LocalTemplateStore

LocalTemplateStore accepts a StoreConfig TypedDict:

from pathlib import Path

from promptdepot.stores.local import LocalTemplateStore, StoreConfig

config: StoreConfig = {
    "base_path": Path("prompts"),
    "initial_version": None,   # defaults to 1.0.0
}

store = LocalTemplateStore(config=config)

Get a template (latest version info)

template = store.get_template("support_agent")

print(template.id)               # "support_agent"
print(template.latest_version)   # SemanticVersion

Get a specific template version

version = store.get_template_version("support_agent", "1.0.0")

print(version.template_id)
print(version.version)
print(version.metadata.description)
print(version.metadata.author)

Get template content

content = store.get_template_version_content("support_agent", "1.0.0")
print(content)

List templates

templates = store.list_templates()
for template in templates:
    print(template.id, template.latest_version)

List all versions for a template

versions = store.list_template_versions("support_agent")
for version in versions:
    print(version.version, version.metadata.description)

Create a new template

Creates the template directory with an initial empty version (defaults to 1.0.0):

store.create_template(template_id="support_agent")

Create a new version

from promptdepot.stores.core import CreationStrategy

# Copy content from the latest version
store.create_version(
    template_id="support_agent",
    version="1.1.0",
    strategy=CreationStrategy.FROM_PREVIOUS_VERSION,
)

# Create with empty content
store.create_version(
    template_id="support_agent",
    version="2.0.0",
    strategy=CreationStrategy.EMPTY,
)

# Create with explicit content
store.create_version(
    template_id="support_agent",
    version="2.1.0",
    strategy=CreationStrategy.WITH_CONTENT,
    content="Hello ${name}, how can I help you today?",
)

Using Renderers

Mako renderer

from promptdepot.renderers.mako import MakoPromptRenderer

renderer = MakoPromptRenderer(
    template="Hello ${name}!",
    config={"lookup": None},
)

result = renderer.render(context={"name": "World"})
print(result)  # Hello World!

Jinja2 renderer

from promptdepot.renderers.jinja2 import Jinja2PromptRenderer

renderer = Jinja2PromptRenderer(
    template="Hello {{ name }}!",
    config={"environment": None},
)

result = renderer.render(context={"name": "World"})
print(result)  # Hello World!

Generic renderer construction pattern

renderer = SomeRenderer.from_template(template="...", config={...})
output = renderer.render(context={"key": "value"})

Using PromptDepotManager

from pathlib import Path

from promptdepot.manager import PromptDepotManager
from promptdepot.renderers.jinja2 import Jinja2PromptRenderer
from promptdepot.stores.local import LocalTemplateStore

store = LocalTemplateStore(config={
    "base_path": Path("prompts"),
    "initial_version": None,
})

manager = PromptDepotManager(
    store=store,
    renderer=Jinja2PromptRenderer,
    default_config={"environment": None},
)

output = manager.get_prompt(
    template_id="support_agent",
    version="1.0.0",
    context={"user_name": "Aryan", "priority": "high"},
)

print(output)

CLI

Prompt Depot includes a CLI built with Typer. After installation, the promptdepot command is available:

promptdepot --help

The CLI reads its configuration from [tool.promptdepot] in your project's pyproject.toml (see Configuration).

Templates

Create a template

Prompts for a template ID interactively:

promptdepot templates create

List all templates

promptdepot templates ls

Displays a table with each template's ID and latest version.

Show a template

promptdepot templates show <template_id>

Displays all versions of the given template with their descriptions.

Versions

Create a version

# Copy content from the latest version (default)
promptdepot versions create <template_id> --version 2.0.0

# Copy from previous explicitly
promptdepot versions create <template_id> --version 2.0.0 --from-previous

# Create with empty content
promptdepot versions create <template_id> --version 2.0.0 --empty

# Create with explicit content
promptdepot versions create <template_id> --version 2.0.0 --with-content --content "Hello \${name}!"

If --version is omitted, the CLI shows the current latest version and prompts for input. If --with-content is used without --content, the CLI prompts for the content interactively.

List versions

promptdepot versions ls <template_id>

Displays a table with version, description, created_at, author, tags, model, and changelog.

Show a version

promptdepot versions show <template_id> <version>

Displays full metadata and content for a specific version.

Configuration

The CLI reads settings from the [tool.promptdepot] section in pyproject.toml:

[tool.promptdepot]
store_path = "promptdepot.stores.local.LocalTemplateStore"

[tool.promptdepot.store.config]
base_path = "prompts"
Key Description Default
store_path Dotted import path to the TemplateStore class promptdepot.stores.local.LocalTemplateStore
store.config Dictionary passed as config to the store constructor {}

For LocalTemplateStore, the recognized config keys are:

Key Description Default
base_path Path to the root directory containing templates (required)
initial_version Version string for create_template 1.0.0

Error Handling

LocalTemplateStore may raise:

  • TemplateNotFoundError -- template id, version, or template file not found; or frontmatter missing/invalid
  • VersionAlreadyExistsError -- creating a version that already exists
  • ValidationError -- Pydantic validation failure on metadata

Mako/Jinja2 constructors can raise syntax errors on invalid templates:

  • Mako: mako.exceptions.SyntaxException
  • Jinja2: jinja2.TemplateSyntaxError

Testing

Run all tests:

uv run pytest tests

Run specific files:

uv run pytest tests/test_stores_local.py
uv run pytest tests/test_cli_templates.py

Run a single test:

uv run pytest tests/test_stores_local.py::test_local_template_store_init__should_set_class_properly_when_path_is_string -v -s

Coverage

Run tests with coverage:

uv run pytest tests --cov=promptdepot --cov-report=term-missing --cov-report=html

Open HTML report (macOS):

open htmlcov/index.html

CI/CD

The repository includes a GitHub Actions workflow at .github/workflows/ci.yml.

On push (all branches) and pull_request (targeting main), it runs:

  • Ruff lint: ruff check .
  • Ruff format check: ruff format --check .
  • Type checks: ty check src
  • Test matrix on Python 3.10, 3.11, 3.12, and 3.13

On release: published (targeting main), after CI passes, it:

  • Builds distributions with uv build
  • Publishes to PyPI via Trusted Publishing (OIDC)

For PyPI publication, configure Trusted Publishing in your PyPI project settings for this repository/workflow.

Development Notes

  • Linting and formatting are configured with Ruff in pyproject.toml
  • The project uses static typing with a py.typed marker (PEP 561)
  • Fixtures in tests/test_prompts/ illustrate accepted and invalid store structures
  • Optional renderer packages (mako, jinja2) are separated from core dependencies
  • CLI dependencies (typer, rich, pydantic-settings) are part of the core package

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

promptdepot-0.3.0.tar.gz (12.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

promptdepot-0.3.0-py3-none-any.whl (16.9 kB view details)

Uploaded Python 3

File details

Details for the file promptdepot-0.3.0.tar.gz.

File metadata

  • Download URL: promptdepot-0.3.0.tar.gz
  • Upload date:
  • Size: 12.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for promptdepot-0.3.0.tar.gz
Algorithm Hash digest
SHA256 e55af30c8c7a912cf913a2e6cd5408b7015a1c61035e9ae09791948526e8ab72
MD5 be9aba8d356686d9fe8e53717c01a203
BLAKE2b-256 81636668e8198ec5c5541a900289484684daaa31d4132f3a4afb2ff38a787a63

See more details on using hashes here.

Provenance

The following attestation bundles were made for promptdepot-0.3.0.tar.gz:

Publisher: ci.yml on aryan-curiel/promptdepot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file promptdepot-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: promptdepot-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 16.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for promptdepot-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a2767396af4e1380d9090080e4c0bf8bec8ea4d1a3baca6d591589878178a2b9
MD5 5f869a9c580edde35f643e53d9ce3445
BLAKE2b-256 85b8cd4872858825ab5f02eb0aac1934acd0007b54ba0e53a1800972b8b3019f

See more details on using hashes here.

Provenance

The following attestation bundles were made for promptdepot-0.3.0-py3-none-any.whl:

Publisher: ci.yml on aryan-curiel/promptdepot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page