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
- 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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e55af30c8c7a912cf913a2e6cd5408b7015a1c61035e9ae09791948526e8ab72
|
|
| MD5 |
be9aba8d356686d9fe8e53717c01a203
|
|
| BLAKE2b-256 |
81636668e8198ec5c5541a900289484684daaa31d4132f3a4afb2ff38a787a63
|
Provenance
The following attestation bundles were made for promptdepot-0.3.0.tar.gz:
Publisher:
ci.yml on aryan-curiel/promptdepot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
promptdepot-0.3.0.tar.gz -
Subject digest:
e55af30c8c7a912cf913a2e6cd5408b7015a1c61035e9ae09791948526e8ab72 - Sigstore transparency entry: 1074915876
- Sigstore integration time:
-
Permalink:
aryan-curiel/promptdepot@56636c6b588be9745c436a34960eaed64d4a4500 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/aryan-curiel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@56636c6b588be9745c436a34960eaed64d4a4500 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a2767396af4e1380d9090080e4c0bf8bec8ea4d1a3baca6d591589878178a2b9
|
|
| MD5 |
5f869a9c580edde35f643e53d9ce3445
|
|
| BLAKE2b-256 |
85b8cd4872858825ab5f02eb0aac1934acd0007b54ba0e53a1800972b8b3019f
|
Provenance
The following attestation bundles were made for promptdepot-0.3.0-py3-none-any.whl:
Publisher:
ci.yml on aryan-curiel/promptdepot
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
promptdepot-0.3.0-py3-none-any.whl -
Subject digest:
a2767396af4e1380d9090080e4c0bf8bec8ea4d1a3baca6d591589878178a2b9 - Sigstore transparency entry: 1074915881
- Sigstore integration time:
-
Permalink:
aryan-curiel/promptdepot@56636c6b588be9745c436a34960eaed64d4a4500 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/aryan-curiel
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@56636c6b588be9745c436a34960eaed64d4a4500 -
Trigger Event:
release
-
Statement type: