045a9f669a13844ef5fff887114bcc842c1f79af
LLMUtils
A Python utility library for managing LLM prompts with Jinja2 templating and automatic schema loading.
Installation
Install using uv
uv add git+https://git.project-insanity.de/gmarth/LLMUtils.git
Install using pip
pip install git+https://git.project-insanity.de/gmarth/LLMUtils.git
Features
- Jinja2 Template Engine: Full support for loops, conditionals, filters, and complex data structures
- Smart Prompt Management: Load and manage prompt templates with automatic schema detection
- On-demand Loading: Prompts are loaded lazily at runtime for better performance
- Caching Support: Optional caching to avoid repeated disk reads
- Automatic Schema Loading: JSON schemas are automatically loaded when found next to prompt files
- Flexible Undefined Handling: Configure how Jinja2 handles missing variables (strict, debug, silent)
Quick Start
Basic Usage
from llmutils.prompt_manager import PromptManager
from jinja2 import UndefinedError
# Get a prompt template
result = PromptManager.get_prompt('greeting')
print(result.template) # View the template: "Hello {{name}}, you are {{age}} years old"
# Fill the template - Jinja2 handles missing variables
try:
filled = result.fill(name='Alice', age=30)
print(filled) # "Hello Alice, you are 30 years old"
except UndefinedError as e:
print(f"Missing variable: {e}")
Pre-filling Variables
# Fill variables during retrieval
result = PromptManager.get_prompt('greeting', name='Alice', age=30)
print(result.prompt) # Already filled: "Hello Alice, you are 30 years old"
Handling Optional Variables
Use Jinja2's built-in features for optional variables:
# Template with optional variables (greeting_flexible.md):
# Hello {{ name | default('Guest') }}!
# {% if age is defined %}You are {{ age }} years old.{% endif %}
result = PromptManager.get_prompt('greeting_flexible')
filled = result.fill(name='Alice') # Works! Age is optional
print(filled) # "Hello Alice!\n"
Advanced Jinja2 Features
# Using lists and loops
result = PromptManager.get_prompt('task_list')
filled = result.fill(
tasks=['Write code', 'Review PR', 'Deploy'],
priority='high'
)
# Using conditionals for optional variables
result = PromptManager.get_prompt('status_report')
# Template can handle optional variables with {% if variable %}
filled = result.fill(
error='Connection timeout', # Will show error message
items=[] # Will show "No items"
)
# Using complex nested data
result = PromptManager.get_prompt('user_profile')
filled = result.fill(
user={
'name': 'Alice',
'roles': ['admin', 'developer'],
'projects': [
{'name': 'Project A', 'status': 'active'},
{'name': 'Project B', 'status': 'completed'}
]
}
)
Configuring Undefined Behavior
from jinja2 import DebugUndefined, Undefined
# Default: StrictUndefined (raises errors on missing variables)
PromptManager.configure()
# Debug mode: shows undefined variable names in output
PromptManager.configure(undefined=DebugUndefined)
result = PromptManager.get_prompt('greeting')
filled = result.fill(name='Alice') # Output: "Hello Alice, you are {{ age }} years old"
# Silent mode: undefined variables become empty strings
PromptManager.configure(undefined=Undefined)
result = PromptManager.get_prompt('greeting')
filled = result.fill(name='Alice') # Output: "Hello Alice, you are years old"
Automatic Schema Loading
Schemas are automatically loaded when a .json file exists next to the prompt:
# If you have both greeting.md and greeting.json files:
result = PromptManager.get_prompt('greeting')
if result.schema:
print("Schema automatically loaded!")
print(result.schema) # The JSON schema dictionary
Configuration
from pathlib import Path
from jinja2 import DebugUndefined
from llmutils.prompt_manager import PromptManager
# Configure custom prompts directory (default: ./prompts)
PromptManager.configure(path=Path('/custom/prompts/location'))
# Configure undefined variable handling
PromptManager.configure(undefined=DebugUndefined)
# Disable caching for development
PromptManager.configure(caching=False)
# Clear cache to force reload
PromptManager.reload_prompts()
Prompt Files
Place your prompt templates in the prompts/ directory:
prompts/greeting.md- Markdown file with templateprompts/greeting.json- Optional JSON schema for structured output
Simple Template Example (greeting.md):
Hello {{name}},
You are {{age}} years old.
Jinja2 Template Examples
Lists and Loops (task_list.md):
Priority: {{ priority }}
Tasks to complete:
{% for task in tasks %}
- {{ task }}
{% endfor %}
Total: {{ tasks | length }} tasks
Conditionals (status_report.md):
{% if error %}
⚠️ ERROR: {{ error }}
{% else %}
✅ All systems operational
{% endif %}
{% if items %}
Items ({{ items | length }}):
{% for item in items %}
{{ loop.index }}. {{ item }}
{% endfor %}
{% else %}
No items to process.
{% endif %}
Complex Data (user_profile.md):
# User: {{ user.name }}
## Roles
{{ user.roles | join(', ') }}
## Projects
{% for project in user.projects %}
- {{ project.name }} [{{ project.status | upper }}]
{% endfor %}
JSON Schema Example (greeting.json):
{
"type": "object",
"properties": {
"response": {
"type": "string"
}
}
}
API Reference
ManagedPrompt Class
The ManagedPrompt dataclass returned by get_prompt():
template: str- The Jinja2 template string (or filled result if pre-filled)name: str- The prompt nameschema: Optional[Dict]- Associated JSON schema (automatically loaded)prompt: str- Property that returns the template (backward compatibility)fill(**kwargs) -> str- Fill template with variables using Jinja2
PromptManager Methods
get_prompt(prompt_name, **kwargs) -> ManagedPrompt- Get a prompt templatelist_prompts() -> Dict[str, Dict[str, Any]]- List all available prompts with schema infoconfigure(path=None, caching=None, undefined=None)- Configure settingsreload_prompts()- Clear the cache
Configuration Options
path: Path- Custom prompts directory (default:./prompts)caching: bool- Enable/disable prompt caching (default:True)undefined: type- Jinja2 undefined behavior:StrictUndefined(default): Raises error on undefined variablesDebugUndefined: Shows{{ variable }}for undefined variablesUndefined: Replaces undefined variables with empty string
Jinja2 Template Features
The library supports all standard Jinja2 features:
Filters
{{ items | length }}- Get length of list{{ name | upper }}- Convert to uppercase{{ name | lower }}- Convert to lowercase{{ skills | join(', ') }}- Join list items{{ data | tojson }}- Convert to JSON{{ price | round(2) }}- Round numbers
Loops
{% for item in items %}...{% endfor %}- Iterate over lists{{ loop.index }}- Current iteration (1-indexed){{ loop.index0 }}- Current iteration (0-indexed){% for key, value in dict.items() %}...{% endfor %}- Iterate over dictionaries
Conditionals
{% if condition %}...{% endif %}- Simple conditional{% if condition %}...{% else %}...{% endif %}- If/else{% if condition %}...{% elif other %}...{% else %}...{% endif %}- Multiple conditions
License
MIT
Languages
Python
100%