Metadata-Version: 2.4
Name: promptfw
Version: 0.2.0
Summary: Prompt Template Framework — 4-layer Jinja2 template engine for LLM applications
Project-URL: Homepage, https://github.com/achimdehnert/platform
Project-URL: Repository, https://github.com/achimdehnert/platform/tree/main/packages/promptfw
Author-email: Achim Dehnert <achim@dehnert.com>
License: MIT
Keywords: ai,anthropic,jinja2,llm,openai,prompt,prompt-engineering,template
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.11
Requires-Dist: jinja2>=3.1
Requires-Dist: pydantic>=2.6
Provides-Extra: all
Requires-Dist: pyyaml>=6.0; extra == 'all'
Requires-Dist: tiktoken>=0.6; extra == 'all'
Requires-Dist: watchdog>=4.0; extra == 'all'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-mock>=3.12; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: tiktoken>=0.6; extra == 'dev'
Provides-Extra: hotreload
Requires-Dist: watchdog>=4.0; extra == 'hotreload'
Provides-Extra: testing
Requires-Dist: pytest-asyncio>=0.23; extra == 'testing'
Requires-Dist: pytest-mock>=3.12; extra == 'testing'
Requires-Dist: pytest>=8.0; extra == 'testing'
Provides-Extra: tiktoken
Requires-Dist: tiktoken>=0.6; extra == 'tiktoken'
Description-Content-Type: text/markdown

# promptfw — Prompt Template Framework

4-layer Jinja2 template engine for LLM applications.

## Installation

```bash
pip install promptfw
# With token counting:
pip install promptfw[tiktoken]
```

## Quick Start

```python
from promptfw import PromptStack, PromptTemplate, TemplateLayer

stack = PromptStack()
stack.register(PromptTemplate(
    id="story.task.write",
    layer=TemplateLayer.TASK,
    template="Write a {{ genre }} story about {{ topic }} in {{ words }} words.",
    variables=["genre", "topic", "words"],
))

rendered = stack.render("story.task.write", {
    "genre": "fantasy",
    "topic": "a dragon who learns to code",
    "words": 500,
})

# rendered.system  →  system prompt (SYSTEM + FORMAT layers)
# rendered.user    →  user prompt   (CONTEXT + TASK layers)
```

## 4-Layer Stack

```
SYSTEM   → Role & base behaviour  (stable, cacheable)
FORMAT   → Format-specific rules   (stable, cacheable)
CONTEXT  → Runtime context         (dynamic: characters, world, prior text)
TASK     → Concrete task           (dynamic: what to write now)
```

```python
rendered = stack.render_stack(
    ["system.base", "format.roman", "context.scene", "task.write_scene"],
    context={
        "role": "professional author",
        "style_rules": "Show don't tell",
        "characters": "Alice, Bob",
        "current_scene": "The forest at night",
        "task": "Write scene 3.2",
    }
)
```

## Load Templates from YAML

```python
# templates/story/task/write_scene.yaml
# id: story.task.write_scene
# layer: task
# template: |
#   Write scene {{ scene_id }}: {{ scene_description }}
#   Characters: {{ characters }}

stack = PromptStack.from_directory("templates/")
rendered = stack.render("story.task.write_scene", context)
```

## Wildcard Lookup

```python
# Matches "roman.first_draft.scene_generation"
template = stack.registry.get("roman.*.scene_generation")
```
