Skip to main content

A lightweight prompt assembly library using sigil-based substitution with no template logic

Project description

prompt-assemble

A lightweight prompt assembly library for building dynamic prompts with sigil-based substitution. No logic in templates — templates stay dumb, logic stays in Python.

Features

  • Sigil-based substitution — Simple placeholder syntax ([[VAR_NAME]] and [[PROMPT: name]])
  • Format-agnostic — Works with loose XML, JSON, or plain text
  • Recursive substitution — Variable values can contain sigils, resolved in a second pass
  • Comments support — Single-line (#!) and multiline (<!-- -->) comments
  • No template logic — Loops, conditionals, and transforms belong in Python
  • Portable — Easy to use across different environments

Installation

pip install prompt-assemble

Quick Start

from prompt_assemble import assemble

template = """
<system>
You are a [[PROMPT: persona]] specializing in [[DOMAIN]].
</system>

<task>
[[PROMPT: task-instructions]]
</task>

<question>
[[QUESTION]]
</question>
"""

components = {
    "persona": "expert software architect",
    "task-instructions": "Analyze the code and provide recommendations.",
}

variables = {
    "DOMAIN": "Python development",
    "QUESTION": "How can we improve this function?",
}

result = assemble(template, components=components, variables=variables)
print(result)

Format Support

Loose XML

<system>You are a [[PROMPT: persona]]</system>
<task>[[PROMPT: task-instructions]]</task>

JSON

{
  "system": "You are a [[PROMPT: persona]]",
  "task": "[[PROMPT: task-instructions]]"
}

Plain Text

Subject: [[SUBJECT]]

Body:
[[BODY]]

Sigil Syntax

Sigil Purpose
[[VAR_NAME]] Simple variable substitution
[[PROMPT: name]] Inject a named prompt component
[[PROMPT_TAG: tag1, tag2]] Inject all prompts matching tags (AND intersection)
[[PROMPT_TAG:N: tag1, tag2]] Inject N most recent prompts matching tags

Comments

#! Single line comment

<!-- Multi-line
     comment -->

Comments are stripped before substitution and never reach the model.

Recursive Substitution

Variable values can themselves contain sigils:

variables = {
    "TASK": "Analyze [[CODE_TYPE]] code",
    "CODE_TYPE": "Python",
}

# Second pass resolves nested sigils

CLI Usage

pambl --template prompt.prompt --components components.json --variables vars.json

Database

PostgreSQL is required. The DatabaseSource implementation uses PostgreSQL-specific features including automatic reconnection on timeout and transaction management optimized for reliability.

PostgreSQL Support

  • PostgreSQL 10+ (required for production and testing)
  • Automatic connection reconnection on timeout
  • Multi-tenant support via table prefixes
  • Version history and tagging

Quick Setup with PostgreSQL

import psycopg2
from prompt_assemble.sources import DatabaseSource

# Connect to PostgreSQL
conn = psycopg2.connect(
    host="localhost",
    database="prompts",
    user="postgres",
    password="secret"
)

# Create source with table prefix for multi-tenant support
source = DatabaseSource(conn, table_prefix="prod_")

# Use with PromptProvider
from prompt_assemble import PromptProvider

provider = PromptProvider(source)

Docker Compose with PostgreSQL

See DOCKER.md for a complete Docker Compose setup with PostgreSQL.

Bulk Import (Preloading Database)

The bulk_import() function allows you to migrate all prompts and their metadata from one source to another. This is useful for:

  • Populating a database with prompts stored in the filesystem
  • Migrating between storage backends
  • Backing up/restoring prompts
from prompt_assemble import (
    PromptProvider,
    FileSystemSource,
    DatabaseSource,
    bulk_import
)
import psycopg2

# Load prompts from filesystem
filesystem_source = FileSystemSource('./prompts')
source_provider = PromptProvider(filesystem_source)

# Connect to PostgreSQL database
conn = psycopg2.connect(
    host="localhost",
    database="prompts",
    user="postgres",
    password="secret"
)
database_source = DatabaseSource(conn, table_prefix="prod_")
target_provider = PromptProvider(database_source)

# Bulk import with all metadata (tags, description, owner)
results = bulk_import(source_provider, target_provider, verbose=True)

print(f"✅ Imported {results['imported']} prompts")
print(f"⚠️  Skipped {results['skipped']} existing prompts")
print(f"❌ Errors: {results['errors']}")

Bulk Import Options

# Skip prompts already in target
results = bulk_import(
    source_provider,
    target_provider,
    skip_existing=True  # Don't overwrite existing prompts
)

# Verbose output for debugging
results = bulk_import(
    source_provider,
    target_provider,
    verbose=True  # Log each import operation
)

Return Value

The bulk_import() function returns a dictionary with import statistics:

{
    "imported": 42,      # Successfully imported
    "skipped": 3,        # Skipped (already exist or skip_existing=True)
    "errors": 1,         # Failed imports
    "errors_list": [     # Details of failures
        {"name": "failing_prompt", "error": "Connection timeout"}
    ]
}

What Gets Transferred

All metadata is preserved during bulk import:

  • ✅ Prompt content
  • ✅ Tags (AND intersection filtering supported)
  • ✅ Description
  • ✅ Owner
  • ✅ Versioning (for database targets)

Docker Deployment

Unified Docker Image (Backend + Frontend)

A pre-built unified image is available that includes both the Python backend and the React frontend in a single container. This is the easiest way to get started with the full application.

Image Name: ghcr.io/{owner}/prompt-assemble-with-ui

Quick Start with Unified Image

Run with filesystem backend (no database required):

docker run -p 8000:8000 \
  -v ./prompts:/app/prompts \
  ghcr.io/hominemAI/prompt-assemble-with-ui:latest

Then open http://localhost:8000 in your browser.

Run with PostgreSQL database:

docker run -p 8000:8000 \
  -e DB_HOSTNAME=postgres.example.com \
  -e DB_PORT=5432 \
  -e DB_USERNAME=postgres \
  -e DB_PASSWORD=your_secure_password \
  -e DB_DATABASE=prompts \
  -e PROMPT_ASSEMBLE_TABLE_PREFIX=prod_ \
  ghcr.io/hominemAI/prompt-assemble-with-ui:latest

With Docker Compose (PostgreSQL):

version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    environment:
      POSTGRES_DB: prompts
      POSTGRES_PASSWORD: secret
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

  prompt-assemble:
    image: ghcr.io/hominemAI/prompt-assemble-with-ui:latest
    ports:
      - "8000:8000"
    environment:
      DB_HOSTNAME: postgres
      DB_PORT: 5432
      DB_USERNAME: postgres
      DB_PASSWORD: secret
      DB_DATABASE: prompts
      PROMPT_ASSEMBLE_TABLE_PREFIX: prod_
    depends_on:
      - postgres

volumes:
  postgres_data:

Building the Unified Image Locally

To build the unified image yourself:

docker build -f Dockerfile.unified -t prompt-assemble-with-ui:latest .

The Dockerfile.unified multi-stage build:

  1. Frontend stage: Clones and builds the frontend from HominemAI/prompt-assemble-ui
  2. Backend stage: Builds the Python prompt-assemble library
  3. Runtime stage: Combines both, serving everything on port 8000

GitHub Actions Workflow

The .github/workflows/build-image-with-ui.yml workflow automatically builds and publishes the unified image:

  • Triggers: Pushes to main and tags matching ui-v*.*.*
  • Image: Published to GitHub Container Registry as prompt-assemble-with-ui
  • Signing: Images are signed with cosign for security

To publish a release:

git tag ui-v1.0.0
git push origin ui-v1.0.0

Architecture

The unified image uses a single port for both frontend and backend:

  • Port 8000: All traffic (frontend + API)
  • Frontend: React UI served at /
  • API: REST endpoints at /api/*
  • Frontend automatically configures: API base URL set to /api (relative path)

What's Included

  • Backend: Prompt Assembly library with all features (sigil substitution, versioning, variable sets)
  • Frontend: React-based UI from prompt-assemble-ui
  • Storage Options:
    • Filesystem: .prompt files in /app/prompts directory
    • PostgreSQL: Full database backend with versioning and multi-tenancy

Environment Variables (Unified Image)

Same as regular backend, plus frontend-specific options:

# Backend API (port 8000)
FLASK_HOST=0.0.0.0
FLASK_PORT=8000

# Database (optional - defaults to filesystem)
DB_HOSTNAME=postgres
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=secret
DB_DATABASE=prompts
PROMPT_ASSEMBLE_TABLE_PREFIX=prod_

# Frontend (optional)
VITE_API_URL=/api  # Default: /api (relative path to port 8000)

Environment Variables

The prompt-assemble library and UI support the following environment variables for configuration:

Starting the UI Server

Use the provided startup script:

python start_ui_db.py

This script automatically:

  • Connects to PostgreSQL using environment variables
  • Initializes the database schema
  • Starts the Flask UI server
  • Creates tables with the configured prefix

Database Configuration (PostgreSQL)

Variable Type Default Description
DB_HOSTNAME string localhost PostgreSQL server hostname
DB_PORT int 5432 PostgreSQL server port
DB_USERNAME string postgres PostgreSQL username
DB_PASSWORD string (required) PostgreSQL password
DB_DATABASE string prompts PostgreSQL database name
DB_SSLMODE string require SSL mode: require, prefer, disable
DB_PREFIX string pambl_ Table name prefix (e.g., tables become pambl_prompts, pambl_prompt_tags)
PORT int 8000 Port for the Flask UI server

Example - Local PostgreSQL:

export DB_HOSTNAME=localhost
export DB_PORT=5432
export DB_USERNAME=postgres
export DB_PASSWORD=your_password
export DB_DATABASE=prompts
export DB_PREFIX=pambl_
export PORT=8000

python start_ui_db.py

Example - DigitalOcean Managed PostgreSQL:

export DB_HOSTNAME=db-postgresql-sfo2-xxxx-do-user-xxxxx-0.e.db.ondigitalocean.com
export DB_PORT=25060
export DB_USERNAME=pambl_user
export DB_PASSWORD=your_secure_password
export DB_DATABASE=pambl_db
export DB_SSLMODE=require
export DB_PREFIX=pambl_
export PORT=8000

python start_ui_db.py

Programmatic Configuration (Legacy)

Variable Type Default Description
PROMPT_ASSEMBLE_UI bool false Enable/disable the web UI server. Set to "true" to activate
PROMPT_ASSEMBLE_TABLE_PREFIX string "" (empty) Table prefix (deprecated - use DB_PREFIX instead)

Listener & Event Configuration

Variable Type Default Description
None currently - - Listener callbacks are configured programmatically

Configuration Examples

Development Environment (Local PostgreSQL)

export DB_HOSTNAME=localhost
export DB_PORT=5432
export DB_USERNAME=postgres
export DB_PASSWORD=dev_password
export DB_DATABASE=prompts_dev
export DB_SSLMODE=disable
export DB_PREFIX=dev_
export PORT=8000

python start_ui_db.py

Production (DigitalOcean PostgreSQL)

export DB_HOSTNAME=db-postgresql-sfo2-xxxxx-do-user-xxxxx-0.e.db.ondigitalocean.com
export DB_PORT=25060
export DB_USERNAME=prod_user
export DB_PASSWORD=your_secure_password
export DB_DATABASE=prompts_prod
export DB_SSLMODE=require
export DB_PREFIX=prod_
export PORT=8000

python start_ui_db.py

Testing

export DB_HOSTNAME=localhost
export DB_PORT=5432
export DB_USERNAME=postgres
export DB_PASSWORD=test_password
export DB_DATABASE=prompts_test
export DB_PREFIX=test_
export PORT=8001

python start_ui_db.py

Programmatic Configuration

You can also configure these settings directly in Python:

from prompt_assemble.sources import DatabaseSource
from prompt_assemble.api import run_server
import psycopg2

# Configure PostgreSQL database with table prefix
conn = psycopg2.connect(
    host="localhost",
    database="prompts",
    user="postgres",
    password="secret"
)
source = DatabaseSource(conn, table_prefix='myapp_')

# Configure Flask server
run_server(
    source=source,
    host='0.0.0.0',
    port=8000,
    debug=False
)

Quick Reference

All Environment Variables

# UI Server (Required for web interface)
PROMPT_ASSEMBLE_UI=true

# Flask Configuration (Optional)
FLASK_HOST=0.0.0.0
FLASK_PORT=8000
FLASK_DEBUG=false

# PostgreSQL Database Connection
DB_HOSTNAME=localhost
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=secret
DB_DATABASE=prompts

# Database Options
PROMPT_ASSEMBLE_TABLE_PREFIX=myapp_  # Table prefix for multi-tenancy

Database Driver

Install psycopg2 for PostgreSQL:

pip install psycopg2-binary

Connection Resilience

The DatabaseSource automatically handles connection timeouts and network interruptions:

  • Automatic reconnection — Detects closed connections and reconnects transparently
  • Health checks — Validates connection before each operation
  • Timeout recovery — Handles PostgreSQL idle timeout gracefully

No configuration required — reconnection happens automatically on these operations:

  • save_prompt() - Save/update prompts
  • delete_prompt() - Delete prompts
  • refresh() - Reload metadata
  • get_raw() - Fetch content
  • All variable set operations

Testing

Run the full test suite:

pytest tests/

Note: Database-specific tests require PostgreSQL:

# Skip database tests (for environments without PostgreSQL)
pytest tests/ -k "not test_database"

# Run only with PostgreSQL available
PGHOST=localhost PGUSER=postgres PGPASSWORD=secret PGDATABASE=test_prompts pytest tests/test_database_source.py

Test Coverage: 135 passing tests covering core library, FileSystem source, listener system, save/delete operations, and bulk import functionality. PostgreSQL-specific tests are marked as requiring PostgreSQL.

Contributing

Contributions welcome! Please open an issue or submit a pull request.

License

MIT License — see LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

prompt_assemble-0.3.4.tar.gz (56.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

prompt_assemble-0.3.4-py3-none-any.whl (41.8 kB view details)

Uploaded Python 3

File details

Details for the file prompt_assemble-0.3.4.tar.gz.

File metadata

  • Download URL: prompt_assemble-0.3.4.tar.gz
  • Upload date:
  • Size: 56.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for prompt_assemble-0.3.4.tar.gz
Algorithm Hash digest
SHA256 28a186f2c9e425f382faceccfc939cc83ef1ff297728b745365ea6adba9e8eb3
MD5 8d2e27c31b5796950c49018ac0150048
BLAKE2b-256 18c4b000c787e3f459addb89ec995e6ce2ee826aa3b9fac8d000737630163d18

See more details on using hashes here.

File details

Details for the file prompt_assemble-0.3.4-py3-none-any.whl.

File metadata

File hashes

Hashes for prompt_assemble-0.3.4-py3-none-any.whl
Algorithm Hash digest
SHA256 3544a4b943c7b94d72352dfb1c1e9f7db3f03af1c1aa6ff0b4f7373e84011a81
MD5 ff3e7b00e4785277e4e6b5504b0ffa02
BLAKE2b-256 ad17797d1d1c05cf51c33836eaf9aa876e158512be66d549218f2c61e3c4376e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page