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.3.tar.gz (56.7 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.3-py3-none-any.whl (41.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: prompt_assemble-0.3.3.tar.gz
  • Upload date:
  • Size: 56.7 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.3.tar.gz
Algorithm Hash digest
SHA256 ae37bff3ca22840c006023fe98aa53f686577254a75557f4c4e8c1bc6dcbaf7a
MD5 69f000ffe8ca6f770b6a51ce53de8196
BLAKE2b-256 7e06e8c3fa9c279e035433932b86cd160dc3ae4a3e60310a32da3f343fd8eb0e

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for prompt_assemble-0.3.3-py3-none-any.whl
Algorithm Hash digest
SHA256 ae1837f68ee4bde9c36b32c6bf0e74c7ccc44b4b8d1cc22314e264793af9f4c4
MD5 21ba3d6051b7e39611757715c8b8b8bf
BLAKE2b-256 79c6392ba0402e17496ab0ee5d7e871a52f03b0b3caeb26f13a56c8c9a72d072

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