2025-09-16 16:51:50 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 04:31:07 +02:00
2025-09-16 05:46:05 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 16:05:01 +02:00
2025-09-16 05:46:05 +02:00
2025-09-16 04:31:07 +02:00
2025-09-16 05:46:05 +02:00
2025-09-16 16:05:01 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 16:05:01 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 16:51:50 +02:00
2025-09-16 16:05:01 +02:00

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 template
  • prompts/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 name
  • schema: 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 template
  • list_prompts() -> Dict[str, Dict[str, Any]] - List all available prompts with schema info
  • configure(path=None, caching=None, undefined=None) - Configure settings
  • reload_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 variables
    • DebugUndefined: Shows {{ variable }} for undefined variables
    • Undefined: 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

Description
No description provided
Readme MIT 71 KiB
Languages
Python 100%