
Most **LLM **apps start with a few hardcoded prompts. Then the project grows, the logic spreads across files, and prompt handling becomes a mix of strings, environment checks, and scattered config.
That is exactly the problem DynaPrompt is trying to solve: a dynamic prompt management and configuration library for LLM applications, with lazy loading, Jinja2 templates, and Pydantic schema support.
GitHub: mohamed-em2m/dynaprompt
Why prompt management gets messy
When prompts live directly inside application code, they become harder to test, harder to version, and harder to reuse. DynaPrompt takes the opposite approach: keep templates separate from business logic, and treat prompts like configuration instead of inline strings.
Here is the kind of code that gets ugly fast:
import os
import json
user_name = os.getenv("USER_NAME", "Guest")
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(), indent=2)}
"""
if os.getenv("ENV") == "production":
model = "gpt-4"
temperature = 0.2
else:
model = "gpt-3.5-turbo"
temperature = 0.7
What DynaPrompt changes
DynaPrompt lets you load prompts from files, render them at runtime, and keep environment-specific config outside your app logic.
A cleaner version looks like this:
from dynaprompt import DynaPrompt
prompts = DynaPrompt(settings_files=["prompts/"])
rendered = prompts.system.render(user_name="Emam")
response = llm_client.generate(
prompt=rendered.text,
model=rendered.config["model"],
temperature=rendered.config["temperature"],
response_format=rendered.response_schema,
)
Markdown prompts with YAML frontmatter
One of the nicest parts of DynaPrompt is that prompts can live in Markdown files with YAML frontmatter at the top, letting you set model settings and schema metadata right next to the prompt text.
---
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 }}:
{{ code_snippet }}
Analyze it and return the result strictly matching the schema.
And then render it like this:
from dynaprompt import DynaPrompt
prompts = DynaPrompt(settings_files=["prompts/"])
rendered = prompts.analyzer.render(
developer_name="Emam",
code_snippet="print('hello')",
)
print(rendered.config["model"])
Environment layering for dev and prod
The library also supports environment-based layering. You can define a base prompt config with a production override, switching behavior without rewriting code.
[default.summarizer]
template = "Summarize this: {{ text }}"
model = "gpt-3.5-turbo"
temperature = 0.7
[production.summarizer]
model = "gpt-4-turbo"
temperature = 0.1
Usage:
from dynaprompt import DynaPrompt
prompts = DynaPrompt(settings_files=["prompts.toml"], env="development")
print(prompts.summarizer.config["model"])
with prompts.using_env("production"):
print(prompts.summarizer.config["model"])
Validation and hooks
DynaPrompt also includes validation and hooks, useful when you want guardrails around prompt rendering. The example below shows a validator that blocks prompts that are too long, plus a hook that injects the current date into every prompt before rendering.
from dynaprompt import DynaPrompt
from dynaprompt.validator import PromptValidator
class TokenLimitValidator(PromptValidator):
def validate(self, node, rendered) -> None:
if len(rendered.text.split()) > 2000:
raise ValueError(
f"Prompt '{node.name}' exceeds the maximum allowed length!"
)
prompts = DynaPrompt(
settings_files=["prompts/"],
validators=[TokenLimitValidator()],
)
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)
rendered = prompts.system.render(user_name="Emam")
Why this is useful
DynaPrompt is a good fit for LLM apps that need structure, reusable templates, and clean separation between prompt text and application code. It also includes lazy loading, auto-exporting to TOML, and inspection helpers for tracking how prompts were loaded and merged.
Installation
pip install dynaprompt
Or, if you use uv:
uv add dynaprompt
Final thoughts
If your prompt logic is starting to feel like spaghetti, DynaPrompt is worth a look. It gives you a more organized way to manage prompts, environment overrides, schemas, and validation without stuffing everything into Python strings. The project is MIT licensed and written in Python.
➡️ GitHub: mohamed-em2m/dynaprompt
Top comments (0)