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
- Requirements
- Installation
- Project Structure
- Core Concepts
- Prompt Metadata Schema
- Using LocalTemplateStore
- Using Renderers
- Using PromptDepotManager
- CLI
- Configuration
- Error Handling
- Testing
- Coverage
- CI/CD
- Development Notes
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]inpyproject.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 itsidandlatest_version.TemplateVersionMetadata-- metadata for a single version:template_id,version,created_at,description,author,tags,model,changelog.TemplateVersion-- a version record containingtemplate_id,version, and itsmetadata.
TemplateStore
Abstract contract for template persistence. Accepts an optional config mapping in its constructor:
list_templates() -> list[Template]get_template(template_id) -> Templatecreate_template(template_id)list_template_versions(template_id) -> list[TemplateVersion]get_template_version(template_id, version) -> TemplateVersioncreate_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:
templateconfigfrom_template(...)constructor helper- Required
render(context: Mapping[str, Any]) -> str
CreationStrategy
Version creation behavior:
CreationStrategy.EMPTY-- create with empty template file contentCreationStrategy.FROM_PREVIOUS_VERSION-- copy content from the latest existing versionCreationStrategy.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
contextasMapping[str, Any] - Performs a shallow copy of
default_configso 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 nameversion: semantic version of this prompt versioncreated_at: datetime (defaults todatetime.now()when creating)description: optionalauthor: optionaltags: set of strings (default empty)model: optional model hintchangelog: 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/invalidVersionAlreadyExistsError-- creating a version that already existsValidationError-- 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, and3.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.typedmarker (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
Metadata
Release files for promptdepot 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| promptdepot-0.3.0.tar.gz | 12.0 kB | With provenance | Details for promptdepot-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| promptdepot-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for promptdepot-0.3.0-py3-none-any.whl |
Total release size: 28.9 kB
Release files / promptdepot-0.3.0.tar.gz
| Download URL | promptdepot-0.3.0.tar.gz |
|---|---|
| Size | 12.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e55af30c8c7a912cf913a2e6cd5408b7015a1c61035e9ae09791948526e8ab72
|
|
BLAKE2b-256 checksum How to use checksums |
81636668e8198ec5c5541a900289484684daaa31d4132f3a4afb2ff38a787a63
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.
Transparency logRelease files / promptdepot-0.3.0-py3-none-any.whl
| Download URL | promptdepot-0.3.0-py3-none-any.whl |
|---|---|
| Size | 16.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a2767396af4e1380d9090080e4c0bf8bec8ea4d1a3baca6d591589878178a2b9
|
|
BLAKE2b-256 checksum How to use checksums |
85b8cd4872858825ab5f02eb0aac1934acd0007b54ba0e53a1800972b8b3019f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.
Transparency logSecurity for promptdepot 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for promptdepot-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Mar 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.