Skip to main content

How Message Placeholders Work

Define placeholder in prompt template

Create a chat prompt and insert a message placeholder at the desired position. The placeholder is a special message object with type: "placeholder" and a name you’ll reference at runtime.
prompt=[
  {"role": "system", "content": "You are an {{criticlevel}} movie critic"},
  {"type": "placeholder", "name": "chat_history"},  # Placeholder
  {"role": "user", "content": "What should I watch next?"}
]
The placeholder sits between the system message and final user message, ready to accept chat history at runtime.

Fetch prompt at runtime

In your application, fetch the prompt using the ABV SDK. The SDK retrieves the template with placeholders intact, ready for compilation.
from abvdev import ABV

abv = ABV(api_key="sk-abv-...")
prompt = abv.get_prompt("movie-critic-chat")
The fetched prompt contains the template structure, including all placeholders and variables.

Compile with message arrays

Use the .compile(variables, placeholders) method to resolve both template variables and message placeholders. Pass a dictionary mapping placeholder names to message arrays.
compiled_prompt = prompt.compile(
    criticlevel="expert",  # Resolve variable
    chat_history=[         # Resolve placeholder
        {"role": "user", "content": "I love Ron Fricke movies like Baraka"},
        {"role": "assistant", "content": "Baraka is a masterpiece of visual storytelling."},
        {"role": "user", "content": "Also, Memories of a Murderer"}
    ]
)
The SDK injects the chat_history array at the placeholder’s position while resolving the {{criticlevel}} variable.

Send compiled messages to LLM

The compiled result is a complete message array ready for the LLM API. ABV handles ordering, role consistency, and variable interpolation automatically.
# compiled_prompt = [
#   {"role": "system", "content": "You are an expert movie critic"},
#   {"role": "user", "content": "I love Ron Fricke movies like Baraka"},
#   {"role": "assistant", "content": "Baraka is a masterpiece..."},
#   {"role": "user", "content": "Also, Memories of a Murderer"},
#   {"role": "user", "content": "What should I watch next?"}
# ]

response = openai_client.chat.completions.create(
    model="gpt-4o",
    messages=compiled_prompt
)

Best Practices

Name placeholders based on their semantic purpose, not their position.Good:
{"type": "placeholder", "name": "chat_history"}
{"type": "placeholder", "name": "few_shot_examples"}
{"type": "placeholder", "name": "retrieved_documents"}
Avoid:
{"type": "placeholder", "name": "placeholder1"}
{"type": "placeholder", "name": "messages"}
{"type": "placeholder", "name": "data"}
Clear names make templates self-documenting and prevent compilation errors.
Ensure message arrays passed to placeholders have valid structure (role, content fields) to avoid LLM API errors.
def validate_messages(messages):
    for msg in messages:
        assert "role" in msg, f"Message missing 'role': {msg}"
        assert "content" in msg, f"Message missing 'content': {msg}"
        assert msg["role"] in ["system", "user", "assistant"], f"Invalid role: {msg['role']}"
    return messages

# Use before compilation
compiled = prompt.compile(
    chat_history=validate_messages(conversation_history)
)
This catches errors early rather than failing at the LLM API call.
Place placeholders in the message sequence that matches conversation flow: system context → examples → history → current input.
[
  {"role": "system", "content": "You are a helpful assistant"},
  {"type": "placeholder", "name": "few_shot_examples"},      # First: examples
  {"type": "placeholder", "name": "conversation_history"},   # Then: history
  {"role": "user", "content": "{{current_question}}"}        # Finally: current input
]
This order matches how conversations naturally unfold and how LLMs process context.
Use template variables for simple string substitution and message placeholders for dynamic message arrays.
[
  {"role": "system", "content": "You are a {{role}} for {{company}}"},  # Variables
  {"type": "placeholder", "name": "context"},                           # Placeholder
  {"role": "user", "content": "{{question}}"}                           # Variable
]
Compilation:
compiled = prompt.compile(
    role="customer support agent",        # Variable
    company="Acme Corp",                  # Variable
    question="How do I return an item?",  # Variable
    context=[                             # Placeholder
        {"role": "system", "content": "Return policy: 30 days"},
        {"role": "system", "content": "Restocking fee: 15%"}
    ]
)
This combination provides maximum flexibility for both static and dynamic content.
Design your application to handle cases where placeholder arrays might be empty (e.g., first message in a conversation has no history).
# Fetch prompt
prompt = abv.get_prompt("customer-support-chat")

# Handle empty chat history (first message)
chat_history = get_conversation_history(user_id)  # Might return []

compiled = prompt.compile(
    current_question=user_question,
    conversation_history=chat_history  # Can be empty list
)
ABV handles empty arrays gracefully—the placeholder simply doesn’t insert any messages. Your prompt template works for both first messages and ongoing conversations.

Implementation Examples

Message placeholder UI
  1. Navigate to Prompt Management in ABV dashboard
  2. Create a new chat prompt or edit an existing one
  3. Click Add message placeholder button
  4. Enter a descriptive name (e.g., chat_history, examples)
  5. Position the placeholder in your message sequence
  6. Save the prompt
The placeholder appears as a distinct message type in the prompt editor.
Install ABV SDK:
pip install abvdev
Create prompt with message placeholder:
from abvdev import ABV

# Initialize ABV client
abv = ABV(
    api_key="sk-abv-...",
    host="https://app.abv.dev"  # or "https://eu.app.abv.dev" for EU region
)

# Create prompt with placeholder
abv.create_prompt(
    name="movie-critic-chat",
    type="chat",
    prompt=[
        {"role": "system", "content": "You are an {{criticlevel}} movie critic"},
        {"type": "placeholder", "name": "chat_history"},
        {"role": "user", "content": "What should I watch next?"}
    ],
    labels=["production"]
)
Fetch and compile at runtime:
# Fetch prompt
prompt = abv.get_prompt("movie-critic-chat")

# Compile with variable and placeholder
compiled_prompt = prompt.compile(
    criticlevel="expert",
    chat_history=[
        {"role": "user", "content": "I love Ron Fricke movies like Baraka"},
        {"role": "assistant", "content": "Baraka is a visual masterpiece."},
        {"role": "user", "content": "Also, Memories of a Murderer"}
    ]
)

# Result:
# [
#   {"role": "system", "content": "You are an expert movie critic"},
#   {"role": "user", "content": "I love Ron Fricke movies like Baraka"},
#   {"role": "assistant", "content": "Baraka is a visual masterpiece."},
#   {"role": "user", "content": "Also, Memories of a Murderer"},
#   {"role": "user", "content": "What should I watch next?"}
# ]
Install ABV SDK:
npm install @abvdev/client
Set up environment variables:
npm install dotenv
Create .env file:
.env
ABV_API_KEY=sk-abv-...
ABV_BASEURL=https://app.abv.dev  # or https://eu.app.abv.dev for EU region
Create prompt with placeholder:
import { ABVClient } from "@abvdev/client";
import dotenv from "dotenv";
dotenv.config();

const abv = new ABVClient();

async function createPrompt() {
    await abv.prompt.create({
        name: "movie-critic-chat",
        type: "chat",
        prompt: [
            { role: "system", content: "You are an {{criticlevel}} movie critic" },
            { type: "placeholder", name: "chat_history" },
            { role: "user", content: "What should I watch next?" }
        ],
        labels: ["production"]
    });
}

createPrompt();
Fetch and compile at runtime:
async function usePrompt() {
    const prompt = await abv.prompt.get("movie-critic-chat", { type: "chat" });

    const compiledPrompt = prompt.compile(
        // Variables
        { criticlevel: "expert" },
        // Placeholders
        {
            chat_history: [
                { role: "user", content: "I love Ron Fricke movies like Baraka" },
                { role: "assistant", content: "Baraka is a visual masterpiece." },
                { role: "user", content: "Also, Memories of a Murderer" }
            ]
        }
    );

    console.log(compiledPrompt);
    // [
    //   { role: "system", content: "You are an expert movie critic" },
    //   { role: "user", content: "I love Ron Fricke movies like Baraka" },
    //   { role: "assistant", content: "Baraka is a visual masterpiece." },
    //   { role: "user", content: "Also, Memories of a Murderer" },
    //   { role: "user", content: "What should I watch next?" }
    // ]
}

usePrompt();
Alternative: Constructor parameters instead of environment variables:
const abv = new ABVClient({
    apiKey: "sk-abv-...",
    baseUrl: "https://app.abv.dev"
});

Next Steps