Skip to main content

dynaprompt logo

dynaprompt - Dynamic prompt management and configuration library for LLM applications. Powerful, lazy-loading, and supports Jinja2 templates and Pydantic schemas.

MIT License PyPI Code Style Ruff Python 3.9+ Coverage

DynaPrompt is a powerful, lazy-loading prompt configuration manager inspired by Dynaconf. It offers a structured way to manage, version, and render LLM prompts while keeping your templates completely separated from your application logic.


The Problem • Features • Usage Examples • Validation & Hooks


🛑 The Problem: Why DynaPrompt?

Writing LLM apps usually starts simple, but quickly becomes an unmaintainable mess of hardcoded strings, f-strings, and scattered configuration dictionaries.

❌ Without DynaPrompt (The Mess)

import os, json

# Hardcoded, mixed with logic, impossible to swap easily for testing/production
SYSTEM_PROMPT = f"""
You are a helpful assistant.
Current User: {user_name}
Format your output according to this schema:
{json.dumps(MySchema.model_json_schema())}
"""

if os.getenv("ENV") == "production":
    model = "gpt-4"
    temperature = 0.2
else:
    model = "gpt-3.5-turbo"
    temperature = 0.7

response = llm_client.generate(prompt=SYSTEM_PROMPT, model=model, temp=temperature)

✅ With DynaPrompt (Clean & Maintainable)

from dynaprompt import DynaPrompt

# Zero I/O at import. Auto-discovers environments, schemas, and templates.
prompts = DynaPrompt(settings_files=["prompts/"])

# Automatically uses the right model/temp for your current environment!
rendered = prompts.system.render(user_name="Emam")

response = llm_client.generate(
    prompt=rendered.text,
    model=rendered.config["model"],
    temp=rendered.config["temperature"],
    response_format=rendered.response_schema
)

🚀 Installation

# Using pip
pip install dynaprompt

# Using uv (recommended)
uv add dynaprompt

📖 Usage Examples

1. Markdown with YAML Frontmatter (The Cleanest Way)

DynaPrompt allows you to define prompts as standalone Markdown files. You can attach LLM configuration (like model, temperature, or required response_schema) directly at the top of the file using YAML Frontmatter.

prompts/analyzer.md

---
model: gpt-4o
temperature: 0.2
max_tokens: 1000
response_schema: AnalysisSchema
---
You are an expert code analyzer.
Please review the following code snippet from {{ developer_name }}:

\```python
{{ code_snippet }}
\```

Analyze it and return the result strictly matching the schema.

Usage:

prompts = DynaPrompt(settings_files=["prompts/"])

# Renders the Jinja template with your variables
rendered = prompts.analyzer.render(developer_name="Emam", code_snippet="print('hello')")

print(rendered.config["model"]) # "gpt-4o"

2. Environment Layering (Dev vs Prod)

You can define base settings and then override them for specific environments (e.g., development, production). DynaPrompt automatically switches based on ENV_FOR_DYNAPROMPT.

prompts.toml

# Base settings for all environments
[default.summarizer]
template = "Summarize this: {{ text }}"
model = "gpt-3.5-turbo"
temperature = 0.7

# Overrides for production ONLY
[production.summarizer]
model = "gpt-4-turbo"
temperature = 0.1

Usage:

# Default environment
prompts = DynaPrompt(settings_files=["prompts.toml"], env="development")
print(prompts.summarizer.config["model"])  # "gpt-3.5-turbo"

# Switch to production dynamically
with prompts.using_env("production"):
    print(prompts.summarizer.config["model"])  # "gpt-4-turbo"

3. File-Based Templates and Variables

Keep your configuration files pristine. DynaPrompt can automatically resolve templates and variables from external files or Python modules.

[default.customer_service]
# Load text directly from an external markdown file
template = "prompts/customer_service.md"

# Dynamically import a string variable from a Python file!
# (Extracts 'greeting_prompt' from 'config/prompts.py')
fallback_template = "config.prompts.greeting_prompt"

# Merge specific dictionaries or load entire Python modules as global variables
variables = [
    "config/settings.json",
    "myapp.config:constants"
]

4. Auto-Exporting Prompts to TOML

You can automatically export your entire loaded prompt structure into a central pyprompts.toml file. Multiline templates are saved as separate .md files in a prompts/ directory and referenced by relative path.

prompts = DynaPrompt(settings_files=["examples/"], auto_export="build/pyprompts.toml")
_ = prompts.google.gemini  # Triggers lazy-load and export

!!! warning "Never point auto_export at a directory that is also in settings_files" DynaPrompt's directory scanner will re-import its own exported output on the next load, causing infinite filename chains (prompts_prompts_prompts_*) and _2 suffix inflation. Always write to a separate output path (e.g., build/pyprompts.toml) or a non-scanned directory.


🛡️ Validation & Hooks

DynaPrompt allows you to enforce constraints on your rendered prompts (Validation) and intercept the rendering process to inject context automatically (Hooks).

Example: Enforcing Token Limits and Injecting Context

from dynaprompt import DynaPrompt
from dynaprompt.validator import PromptValidator

# 1. Create a Validator to prevent overly long prompts
class TokenLimitValidator(PromptValidator):
    def validate(self, node, rendered) -> None:
        if len(rendered.text.split()) > 2000:
            raise ValueError(f"Prompt '{node.name}' exceeds maximum token length!")

prompts = DynaPrompt(
    settings_files=["prompts/"],
    validators=[TokenLimitValidator()]
)

# 2. Add a Pre-Render Hook to automatically inject the current date into EVERY prompt
def inject_date(node, kwargs):
    from datetime import datetime
    kwargs["current_date"] = datetime.now().strftime("%Y-%m-%d")
    return kwargs

prompts.add_hook("before_render", "inject_date", inject_date)

# 3. Render
# The hook automatically injects `current_date`, and the validator ensures it's safe!
rendered = prompts.system.render(user_name="Emam")

🧹 Cleanup & Context Manager

DynaPrompt creates temporary __pycache__ and .dynaprompt directories when importing Python schema/variable files. Use the context manager to clean them up automatically:

with DynaPrompt(settings_files=["prompts/"]) as prompts:
    rendered = prompts.system.render(user_name="Emam")
# Cache directories are removed here

Or call cleanup() explicitly at any time:

prompts = DynaPrompt(settings_files=["prompts/"])
prompts.reload()  # calls cleanup() first, then re-reads all files

🔑 Secrets

Templates can access environment variables directly via the env() function or SecretStore:

from dynaprompt import DynaPrompt, SecretStore

# In your template: {{ env.OPENAI_API_KEY }} or {{ secrets.OPENAI_API_KEY }}
secrets = SecretStore()  # reads from os.environ
prompts = DynaPrompt(settings_files=["prompts/"])

If a required secret is missing, MissingSecretError is raised with a helpful export NAME=... message.

🔍 Inspection & Tab-Completion

DynaPrompt is designed for developer productivity.

  • Tab-Completion: Use dir(prompts) or hit Tab in your IDE to see all available prompts and schemas.
  • History Tracking: Inspect exactly where a prompt was loaded from and how it was merged across layers.
print(prompts.inspect("customer_support"))

Metadata

Release files for dynaprompt 0.7.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dynaprompt 0.7.3
File Size Uploaded
dynaprompt-0.7.3.tar.gz 1.2 MB Details

Built distribution (wheel)

Table of built distributions (wheels) for dynaprompt 0.7.3
File Interpreter ABI Platform
dynaprompt-0.7.3-py3-none-any.whl Python 3 none any Details

Total release size: 1.2 MB

Release files / dynaprompt-0.7.3.tar.gz

Download URL dynaprompt-0.7.3.tar.gz
Size 1.2 MB
Tags Source
SHA-256 checksum
How to use checksums
eca607578708ee9badea2fb182595d3f18aa2fc8c1940c7a267c64c2d558db52
BLAKE2b-256 checksum
How to use checksums
71df6cc1b73c4f3725833a007c6a306803025beeb8d3b994f0225838615ca8f4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / dynaprompt-0.7.3-py3-none-any.whl

Download URL dynaprompt-0.7.3-py3-none-any.whl
Size 35.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
31bdfb9efdc19561f2615a2bc2576e209d8e4a26382ab0b2f191f950d7902133
BLAKE2b-256 checksum
How to use checksums
83d99a8a75ac34eb7585446ca759b0cb1ddf7ceba066187db7fd910d91d2552e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.7.3 This release

2 release files

0.7.2

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.3

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page