simplified

This commit is contained in:
2025-09-16 16:51:50 +02:00
parent 6772c1561c
commit 045a9f669a
9 changed files with 732 additions and 276 deletions

View File

@@ -1,6 +1,6 @@
# LLMUtils
A Python utility library for managing LLM prompts with Jinja2 template support and JSON schemas.
A Python utility library for managing LLM prompts with Jinja2 templating and automatic schema loading.
## Installation
@@ -19,12 +19,11 @@ 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 variable substitution
- **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
- **JSON Schema Support**: Associate structured output schemas with prompts
- **Variable Validation**: Automatic validation of required template variables
- **Flexible API**: Fill variables at retrieval or on-demand
- **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
@@ -32,15 +31,18 @@ pip install git+https://git.project-insanity.de/gmarth/LLMUtils.git
```python
from llmutils.prompt_manager import PromptManager
from jinja2 import UndefinedError
# Get a prompt template
result = PromptManager.get_prompt('greeting')
print(result.variables) # See required variables: {'name', 'age'}
print(result.template) # View the template: "Hello {{name}}, you are {{age}} years old"
# Fill the template
filled = result.fill(name='Alice', age=30)
print(filled) # "Hello Alice, you are 30 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
@@ -51,18 +53,18 @@ result = PromptManager.get_prompt('greeting', name='Alice', age=30)
print(result.prompt) # Already filled: "Hello Alice, you are 30 years old"
```
### Validation
### Handling Optional Variables
Use Jinja2's built-in features for optional variables:
```python
result = PromptManager.get_prompt('greeting')
# Template with optional variables (greeting_flexible.md):
# Hello {{ name | default('Guest') }}!
# {% if age is defined %}You are {{ age }} years old.{% endif %}
# Check if variables are valid
if not result.validate(name='Alice'):
missing = result.get_missing_variables(name='Alice')
print(f"Missing variables: {missing}") # {'age'}
# Fill with all required variables
filled = result.fill(name='Alice', age=30)
result = PromptManager.get_prompt('greeting_flexible')
filled = result.fill(name='Alice') # Works! Age is optional
print(filled) # "Hello Alice!\n"
```
### Advanced Jinja2 Features
@@ -75,8 +77,9 @@ filled = result.fill(
priority='high'
)
# Using conditionals
# 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"
@@ -96,14 +99,35 @@ filled = result.fill(
)
```
### JSON Schema Support
### Configuring Undefined Behavior
```python
# Get prompt with associated schema
result = PromptManager.get_prompt('task_prompt')
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:
```python
# If you have both greeting.md and greeting.json files:
result = PromptManager.get_prompt('greeting')
if result.schema:
print("This prompt has a structured output schema")
print("Schema automatically loaded!")
print(result.schema) # The JSON schema dictionary
```
@@ -111,11 +135,15 @@ if result.schema:
```python
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)
@@ -200,25 +228,28 @@ No items to process.
The `ManagedPrompt` dataclass returned by `get_prompt()`:
- `template: str` - The original Jinja2 template string
- `template: str` - The Jinja2 template string (or filled result if pre-filled)
- `name: str` - The prompt name
- `variables: Set[str]` - Required template variables (auto-extracted from Jinja2)
- `schema: Optional[Dict]` - Associated JSON schema
- `prompt: str` - Property that returns filled prompt or template
- `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
- `validate(**kwargs) -> bool` - Check if all variables provided
- `get_missing_variables(**kwargs) -> Set[str]` - Get missing variables
### PromptManager Methods
- `get_prompt(prompt_name, **kwargs) -> ManagedPrompt` - Get a prompt template
- `get_schema(prompt_name) -> Optional[Dict]` - Get just the schema
- `has_schema(prompt_name) -> bool` - Check if prompt has schema
- `list_prompts() -> Dict` - List all available prompts
- `get_prompt_info(prompt_name) -> Dict` - Get detailed prompt information
- `configure(path=None, caching=None)` - Configure settings
- `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: