Skip to content

Packer Module API Reference

The Packer module provides specialized packers for composing prompts with automatic refinement and priority-based ordering. Version 0.1.3+ introduces two specialized packers following the Single Responsibility Principle. Version 0.2.1+ adds default refining strategies and removes token budget constraints. Version 0.2.2 removes unused model parameter for API simplification.

MessagesPacker

Optimized for chat completion APIs (OpenAI, Anthropic). Returns List[Dict[str, str]] directly.

prompt_refiner.packer.MessagesPacker

MessagesPacker(
    track_tokens=False,
    token_counter=None,
    system=None,
    context=None,
    history=None,
    query=None,
)

Bases: BasePacker

Packer for chat completion APIs.

Designed for: - OpenAI Chat Completions (gpt-4, gpt-3.5-turbo, etc.) - Anthropic Messages API (claude-3-opus, claude-3-sonnet, etc.) - Any API using ChatML-style message format

Returns: List[Dict[str, str]] with 'role' and 'content' keys

Example

from prompt_refiner import MessagesPacker

Basic usage with automatic refining

packer = MessagesPacker( ... system="You are helpful.", ... context=["

Doc 1
", "
Doc 2
"], ... query="What's the weather?" ... ) messages = packer.pack()

Use directly: openai.chat.completions.create(messages=messages)

Traditional API still supported

packer = MessagesPacker() packer.add("System prompt", role="system") packer.add("User query", role="user") messages = packer.pack()

Initialize messages packer.

Default Refining Strategies: When no explicit refiner is provided, automatic refining strategies are applied: - system/query: MinimalStrategy (StripHTML + NormalizeWhitespace) - context/history: StandardStrategy (StripHTML + NormalizeWhitespace + Deduplicate)

To override defaults, provide explicit refiner tuple: (content, refiner). For raw content with no refinement, use .add() method with refine_with=None.

Parameters:

Name Type Description Default
track_tokens bool

Enable token tracking to measure refinement effectiveness

False
token_counter Optional[Callable[[str], int]]

Function to count tokens (required if track_tokens=True)

None
system Optional[Union[str, Tuple[str, Refiner]]]

System message. Can be: - str: "You are helpful" (automatically refined with MinimalStrategy) - Tuple[str, Refiner]: ("You are helpful", StripHTML()) - Tuple[str, Pipeline]: ("You are helpful", StripHTML() | NormalizeWhitespace())

None
context Optional[Union[List[str], Tuple[List[str], Refiner]]]

Context documents. Can be: - List[str]: ["doc1", "doc2"] - Tuple[List[str], Refiner]: (["doc1", "doc2"], StripHTML()) - Tuple[List[str], Pipeline]: (["doc1", "doc2"], StripHTML() | NormalizeWhitespace())

None
history Optional[Union[List[Dict[str, str]], Tuple[List[Dict[str, str]], Refiner]]]

Conversation history. Can be: - List[Dict]: [{"role": "user", "content": "Hi"}] - Tuple[List[Dict], Refiner]: ([{"role": "user", "content": "Hi"}], StripHTML()) - Tuple[List[Dict], Pipeline]: ([{"role": "user", "content": "Hi"}], StripHTML() | NormalizeWhitespace())

None
query Optional[Union[str, Tuple[str, Refiner]]]

Current query. Can be: - str: "What's the weather?" - Tuple[str, Refiner]: ("What's the weather?", StripHTML()) - Tuple[str, Pipeline]: ("What's the weather?", StripHTML() | NormalizeWhitespace())

None

Example (Simple - no refiners): >>> packer = MessagesPacker( ... system="You are helpful.", ... context=["

Doc 1
", "

Doc 2

"], ... history=[{"role": "user", "content": "Hi"}], ... query="What's the weather?" ... ) >>> messages = packer.pack()

Example (With single Refiner): >>> from prompt_refiner import MessagesPacker, StripHTML >>> packer = MessagesPacker( ... system="You are helpful.", ... context=(["

Doc 1
", "

Doc 2

"], StripHTML()), ... query="What's the weather?" ... ) >>> messages = packer.pack()

Example (With Pipeline - multiple refiners): >>> from prompt_refiner import MessagesPacker, StripHTML, NormalizeWhitespace, Pipeline >>> cleaner = StripHTML() | NormalizeWhitespace() >>> # Or: cleaner = Pipeline([StripHTML(), NormalizeWhitespace()]) >>> packer = MessagesPacker( ... system="You are helpful.", ... context=(["

Doc 1
", "

Doc 2

"], cleaner), ... query="What's the weather?" ... ) >>> messages = packer.pack()

Example (Traditional API - still supported): >>> packer = MessagesPacker() >>> packer.add("You are helpful.", role="system") >>> packer.add("Doc 1", role="context") >>> messages = packer.pack()

Source code in src/prompt_refiner/packer/messages.py
def __init__(
    self,
    track_tokens: bool = False,
    token_counter: Optional[Callable[[str], int]] = None,
    system: Optional[Union[str, Tuple[str, Refiner]]] = None,
    context: Optional[Union[List[str], Tuple[List[str], Refiner]]] = None,
    history: Optional[
        Union[
            List[Dict[str, str]],
            Tuple[List[Dict[str, str]], Refiner],
        ]
    ] = None,
    query: Optional[Union[str, Tuple[str, Refiner]]] = None,
):
    """
    Initialize messages packer.

    **Default Refining Strategies**:
    When no explicit refiner is provided, automatic refining strategies are applied:
    - system/query: MinimalStrategy (StripHTML + NormalizeWhitespace)
    - context/history: StandardStrategy (StripHTML + NormalizeWhitespace + Deduplicate)

    To override defaults, provide explicit refiner tuple: (content, refiner).
    For raw content with no refinement, use .add() method with refine_with=None.

    Args:
        track_tokens: Enable token tracking to measure refinement effectiveness
        token_counter: Function to count tokens (required if track_tokens=True)
        system: System message. Can be:
            - str: "You are helpful"  (automatically refined with MinimalStrategy)
            - Tuple[str, Refiner]: ("You are helpful", StripHTML())
            - Tuple[str, Pipeline]: ("You are helpful", StripHTML() | NormalizeWhitespace())
        context: Context documents. Can be:
            - List[str]: ["doc1", "doc2"]
            - Tuple[List[str], Refiner]: (["doc1", "doc2"], StripHTML())
            - Tuple[List[str], Pipeline]: (["doc1", "doc2"],
                StripHTML() | NormalizeWhitespace())
        history: Conversation history. Can be:
            - List[Dict]: [{"role": "user", "content": "Hi"}]
            - Tuple[List[Dict], Refiner]: ([{"role": "user", "content": "Hi"}], StripHTML())
            - Tuple[List[Dict], Pipeline]: ([{"role": "user", "content": "Hi"}],
                StripHTML() | NormalizeWhitespace())
        query: Current query. Can be:
            - str: "What's the weather?"
            - Tuple[str, Refiner]: ("What's the weather?", StripHTML())
            - Tuple[str, Pipeline]: ("What's the weather?", StripHTML() | NormalizeWhitespace())

    Example (Simple - no refiners):
        >>> packer = MessagesPacker(
        ...     system="You are helpful.",
        ...     context=["<div>Doc 1</div>", "<p>Doc 2</p>"],
        ...     history=[{"role": "user", "content": "Hi"}],
        ...     query="What's the weather?"
        ... )
        >>> messages = packer.pack()

    Example (With single Refiner):
        >>> from prompt_refiner import MessagesPacker, StripHTML
        >>> packer = MessagesPacker(
        ...     system="You are helpful.",
        ...     context=(["<div>Doc 1</div>", "<p>Doc 2</p>"], StripHTML()),
        ...     query="What's the weather?"
        ... )
        >>> messages = packer.pack()

    Example (With Pipeline - multiple refiners):
        >>> from prompt_refiner import MessagesPacker, StripHTML, NormalizeWhitespace, Pipeline
        >>> cleaner = StripHTML() | NormalizeWhitespace()
        >>> # Or: cleaner = Pipeline([StripHTML(), NormalizeWhitespace()])
        >>> packer = MessagesPacker(
        ...     system="You are helpful.",
        ...     context=(["<div>Doc 1</div>", "<p>Doc 2</p>"], cleaner),
        ...     query="What's the weather?"
        ... )
        >>> messages = packer.pack()

    Example (Traditional API - still supported):
        >>> packer = MessagesPacker()
        >>> packer.add("You are helpful.", role="system")
        >>> packer.add("Doc 1", role="context")
        >>> messages = packer.pack()
    """
    super().__init__(track_tokens, token_counter)
    logger.debug("MessagesPacker initialized")

    # Auto-add items if provided (convenient API)
    # Extract content and refiner from tuple if provided
    # Apply default strategies when no explicit refiner provided
    if system is not None:
        system_content, system_refiner = self._extract_field(system)
        # Apply MinimalStrategy to system if no explicit refiner
        if system_refiner is None:
            system_refiner = MinimalStrategy()
        self.add(system_content, role="system", refine_with=system_refiner)

    if context is not None:
        context_docs, context_refiner = self._extract_field(context)
        # Apply StandardStrategy to context if no explicit refiner
        if context_refiner is None:
            context_refiner = StandardStrategy()
        for doc in context_docs:
            self.add(doc, role="context", refine_with=context_refiner)

    if history is not None:
        history_msgs, history_refiner = self._extract_field(history)
        # Apply StandardStrategy to history if no explicit refiner
        if history_refiner is None:
            history_refiner = StandardStrategy()
        for msg in history_msgs:
            self.add(msg["content"], role=msg["role"], refine_with=history_refiner)

    if query is not None:
        query_content, query_refiner = self._extract_field(query)
        # Apply MinimalStrategy to query if no explicit refiner
        if query_refiner is None:
            query_refiner = MinimalStrategy()
        self.add(query_content, role="query", refine_with=query_refiner)

Functions

quick_pack classmethod
quick_pack(
    system=None, context=None, history=None, query=None
)

One-liner to create packer and pack messages immediately.

Default refining strategies are automatically applied (same as init): - system/query: MinimalStrategy - context/history: StandardStrategy

Parameters:

Name Type Description Default
system Optional[Union[str, Tuple[str, Refiner]]]

System message (str or (str, Refiner/Pipeline) tuple)

None
context Optional[Union[List[str], Tuple[List[str], Refiner]]]

Context documents (list or (list, Refiner/Pipeline) tuple)

None
history Optional[Union[List[Dict[str, str]], Tuple[List[Dict[str, str]], Refiner]]]

Conversation history (list or (list, Refiner/Pipeline) tuple)

None
query Optional[Union[str, Tuple[str, Refiner]]]

Current query (str or (str, Refiner/Pipeline) tuple)

None

Returns:

Type Description
List[Dict[str, str]]

Packed messages ready for LLM API

Example (Simple): >>> messages = MessagesPacker.quick_pack( ... system="You are helpful.", ... context=["

Doc 1
", "

Doc 2

"], ... query="What's the weather?" ... )

Example (With single Refiner): >>> from prompt_refiner import MessagesPacker, StripHTML >>> messages = MessagesPacker.quick_pack( ... system="You are helpful.", ... context=(["

Doc 1
", "

Doc 2

"], StripHTML()), ... query="What's the weather?" ... )

Example (With Pipeline - multiple refiners): >>> from prompt_refiner import MessagesPacker, StripHTML, NormalizeWhitespace, Pipeline >>> cleaner = StripHTML() | NormalizeWhitespace() >>> # Or: cleaner = Pipeline([StripHTML(), NormalizeWhitespace()]) >>> messages = MessagesPacker.quick_pack( ... system="You are helpful.", ... context=(["

Doc 1
", "

Doc 2

"], cleaner), ... query="What's the weather?" ... ) >>> # Ready to use: client.chat.completions.create(messages=messages)

Source code in src/prompt_refiner/packer/messages.py
@classmethod
def quick_pack(
    cls,
    system: Optional[Union[str, Tuple[str, Refiner]]] = None,
    context: Optional[Union[List[str], Tuple[List[str], Refiner]]] = None,
    history: Optional[
        Union[
            List[Dict[str, str]],
            Tuple[List[Dict[str, str]], Refiner],
        ]
    ] = None,
    query: Optional[Union[str, Tuple[str, Refiner]]] = None,
) -> List[Dict[str, str]]:
    """
    One-liner to create packer and pack messages immediately.

    Default refining strategies are automatically applied (same as __init__):
    - system/query: MinimalStrategy
    - context/history: StandardStrategy

    Args:
        system: System message (str or (str, Refiner/Pipeline) tuple)
        context: Context documents (list or (list, Refiner/Pipeline) tuple)
        history: Conversation history (list or (list, Refiner/Pipeline) tuple)
        query: Current query (str or (str, Refiner/Pipeline) tuple)

    Returns:
        Packed messages ready for LLM API

    Example (Simple):
        >>> messages = MessagesPacker.quick_pack(
        ...     system="You are helpful.",
        ...     context=["<div>Doc 1</div>", "<p>Doc 2</p>"],
        ...     query="What's the weather?"
        ... )

    Example (With single Refiner):
        >>> from prompt_refiner import MessagesPacker, StripHTML
        >>> messages = MessagesPacker.quick_pack(
        ...     system="You are helpful.",
        ...     context=(["<div>Doc 1</div>", "<p>Doc 2</p>"], StripHTML()),
        ...     query="What's the weather?"
        ... )

    Example (With Pipeline - multiple refiners):
        >>> from prompt_refiner import MessagesPacker, StripHTML, NormalizeWhitespace, Pipeline
        >>> cleaner = StripHTML() | NormalizeWhitespace()
        >>> # Or: cleaner = Pipeline([StripHTML(), NormalizeWhitespace()])
        >>> messages = MessagesPacker.quick_pack(
        ...     system="You are helpful.",
        ...     context=(["<div>Doc 1</div>", "<p>Doc 2</p>"], cleaner),
        ...     query="What's the weather?"
        ... )
        >>> # Ready to use: client.chat.completions.create(messages=messages)
    """
    packer = cls(
        system=system,
        context=context,
        history=history,
        query=query,
    )
    return packer.pack()
pack
pack()

Pack items into message format for chat APIs.

Automatically maps semantic roles to API-compatible roles: - ROLE_CONTEXT → "user" (RAG documents as user-provided context) - ROLE_QUERY → "user" (current user question) - Other roles (system, user, assistant) remain unchanged

Returns:

Type Description
List[Dict[str, str]]

List of message dictionaries with 'role' and 'content' keys,

List[Dict[str, str]]

ready for OpenAI, Anthropic, and other chat completion APIs.

Example

messages = packer.pack() openai.chat.completions.create(model="gpt-4", messages=messages)

Source code in src/prompt_refiner/packer/messages.py
def pack(self) -> List[Dict[str, str]]:
    """
    Pack items into message format for chat APIs.

    Automatically maps semantic roles to API-compatible roles:
    - ROLE_CONTEXT → "user" (RAG documents as user-provided context)
    - ROLE_QUERY → "user" (current user question)
    - Other roles (system, user, assistant) remain unchanged

    Returns:
        List of message dictionaries with 'role' and 'content' keys,
        ready for OpenAI, Anthropic, and other chat completion APIs.

    Example:
        >>> messages = packer.pack()
        >>> openai.chat.completions.create(model="gpt-4", messages=messages)
    """
    selected_items = self._select_items()

    if not selected_items:
        logger.warning("No items selected, returning empty message list")
        return []

    messages = []
    for item in selected_items:
        # Map semantic roles to API-compatible roles
        api_role = item.role

        if item.role == ROLE_CONTEXT:
            # RAG documents become user messages (context provided by user)
            api_role = "user"
        elif item.role == ROLE_QUERY:
            # Current query becomes user message
            api_role = "user"
        # Other roles (system, user, assistant) remain unchanged

        messages.append({"role": api_role, "content": item.content})

    logger.info(f"Packed {len(messages)} messages for chat API")
    return messages

TextPacker

Optimized for text completion APIs (Llama Base, GPT-3). Returns str directly with multiple text formats.

prompt_refiner.packer.TextPacker

TextPacker(
    track_tokens=False,
    token_counter=None,
    text_format=TextFormat.RAW,
    separator=None,
    system=None,
    context=None,
    history=None,
    query=None,
)

Bases: BasePacker

Packer for text completion APIs.

Designed for: - Base models (Llama-2-base, GPT-3, etc.) - Completion endpoints (not chat) - Custom prompt templates

Returns: str (formatted text ready for completion API)

Supports multiple text formatting strategies to prevent instruction drifting: - RAW: Simple concatenation with separators - MARKDOWN: Grouped sections (INSTRUCTIONS, CONTEXT, CONVERSATION, INPUT) - XML: Semantic content tags

Example

from prompt_refiner import TextPacker, TextFormat

Basic usage with automatic refining

packer = TextPacker( ... text_format=TextFormat.MARKDOWN, ... system="You are helpful.", ... context=["

Doc 1
", "
Doc 2
"], ... query="What's the weather?" ... ) prompt = packer.pack()

Use directly: completion.create(prompt=prompt)

Traditional API still supported

packer = TextPacker() packer.add("System prompt", role="system") prompt = packer.pack()

Initialize text packer.

Default Refining Strategies: When no explicit refiner is provided, automatic refining strategies are applied: - system/query: MinimalStrategy (StripHTML + NormalizeWhitespace) - context/history: StandardStrategy (StripHTML + NormalizeWhitespace + Deduplicate)

To override defaults, provide explicit refiner tuple: (content, refiner). For raw content with no refinement, use .add() method with refine_with=None.

Parameters:

Name Type Description Default
track_tokens bool

Enable token tracking to measure refinement effectiveness

False
token_counter Optional[Callable[[str], int]]

Function to count tokens (required if track_tokens=True)

None
text_format TextFormat

Text formatting strategy (RAW, MARKDOWN, XML)

RAW
separator Optional[str]

String to join items (default: "\n\n" for clarity)

None
system Optional[Union[str, Tuple[str, Refiner]]]

System message. Can be: - str: "You are helpful" - Tuple[str, Refiner]: ("You are helpful", StripHTML()) - Tuple[str, Pipeline]: ("You are helpful", StripHTML() | NormalizeWhitespace())

None
context Optional[Union[List[str], Tuple[List[str], Refiner]]]

Context documents. Can be: - List[str]: ["doc1", "doc2"] - Tuple[List[str], Refiner]: (["doc1", "doc2"], StripHTML()) - Tuple[List[str], Pipeline]: (["doc1", "doc2"], StripHTML() | NormalizeWhitespace())

None
history Optional[Union[List[Dict[str, str]], Tuple[List[Dict[str, str]], Refiner]]]

Conversation history. Can be: - List[Dict]: [{"role": "user", "content": "Hi"}] - Tuple[List[Dict], Refiner]: ([{"role": "user", "content": "Hi"}], StripHTML()) - Tuple[List[Dict], Pipeline]: ([{"role": "user", "content": "Hi"}], StripHTML() | NormalizeWhitespace())

None
query Optional[Union[str, Tuple[str, Refiner]]]

Current query. Can be: - str: "What's the weather?" - Tuple[str, Refiner]: ("What's the weather?", StripHTML()) - Tuple[str, Pipeline]: ("What's the weather?", StripHTML() | NormalizeWhitespace())

None

Example (Simple - no refiners): >>> packer = TextPacker( ... text_format=TextFormat.MARKDOWN, ... system="You are helpful.", ... context=["Doc 1", "Doc 2"], ... query="What's the weather?" ... ) >>> prompt = packer.pack()

Example (With single Refiner): >>> from prompt_refiner import TextPacker, StripHTML >>> packer = TextPacker( ... text_format=TextFormat.MARKDOWN, ... system="You are helpful.", ... context=(["

Doc 1
"], StripHTML()), ... query="What's the weather?" ... ) >>> prompt = packer.pack()

Example (With Pipeline - multiple refiners): >>> from prompt_refiner import TextPacker, StripHTML, NormalizeWhitespace, Pipeline >>> cleaner = StripHTML() | NormalizeWhitespace() >>> # Or: cleaner = Pipeline([StripHTML(), NormalizeWhitespace()]) >>> packer = TextPacker( ... text_format=TextFormat.MARKDOWN, ... system="You are helpful.", ... context=(["

Doc 1
"], cleaner), ... query="What's the weather?" ... ) >>> prompt = packer.pack()

Source code in src/prompt_refiner/packer/text.py
def __init__(
    self,
    track_tokens: bool = False,
    token_counter: Optional[Callable[[str], int]] = None,
    text_format: TextFormat = TextFormat.RAW,
    separator: Optional[str] = None,
    system: Optional[Union[str, Tuple[str, Refiner]]] = None,
    context: Optional[Union[List[str], Tuple[List[str], Refiner]]] = None,
    history: Optional[
        Union[
            List[Dict[str, str]],
            Tuple[List[Dict[str, str]], Refiner],
        ]
    ] = None,
    query: Optional[Union[str, Tuple[str, Refiner]]] = None,
):
    """
    Initialize text packer.

    **Default Refining Strategies**:
    When no explicit refiner is provided, automatic refining strategies are applied:
    - system/query: MinimalStrategy (StripHTML + NormalizeWhitespace)
    - context/history: StandardStrategy (StripHTML + NormalizeWhitespace + Deduplicate)

    To override defaults, provide explicit refiner tuple: (content, refiner).
    For raw content with no refinement, use .add() method with refine_with=None.

    Args:
        track_tokens: Enable token tracking to measure refinement effectiveness
        token_counter: Function to count tokens (required if track_tokens=True)
        text_format: Text formatting strategy (RAW, MARKDOWN, XML)
        separator: String to join items (default: "\\n\\n" for clarity)
        system: System message. Can be:
            - str: "You are helpful"
            - Tuple[str, Refiner]: ("You are helpful", StripHTML())
            - Tuple[str, Pipeline]: ("You are helpful", StripHTML() | NormalizeWhitespace())
        context: Context documents. Can be:
            - List[str]: ["doc1", "doc2"]
            - Tuple[List[str], Refiner]: (["doc1", "doc2"], StripHTML())
            - Tuple[List[str], Pipeline]: (["doc1", "doc2"],
                StripHTML() | NormalizeWhitespace())
        history: Conversation history. Can be:
            - List[Dict]: [{"role": "user", "content": "Hi"}]
            - Tuple[List[Dict], Refiner]: ([{"role": "user", "content": "Hi"}], StripHTML())
            - Tuple[List[Dict], Pipeline]: ([{"role": "user", "content": "Hi"}],
                StripHTML() | NormalizeWhitespace())
        query: Current query. Can be:
            - str: "What's the weather?"
            - Tuple[str, Refiner]: ("What's the weather?", StripHTML())
            - Tuple[str, Pipeline]: ("What's the weather?", StripHTML() | NormalizeWhitespace())

    Example (Simple - no refiners):
        >>> packer = TextPacker(
        ...     text_format=TextFormat.MARKDOWN,
        ...     system="You are helpful.",
        ...     context=["Doc 1", "Doc 2"],
        ...     query="What's the weather?"
        ... )
        >>> prompt = packer.pack()

    Example (With single Refiner):
        >>> from prompt_refiner import TextPacker, StripHTML
        >>> packer = TextPacker(
        ...     text_format=TextFormat.MARKDOWN,
        ...     system="You are helpful.",
        ...     context=(["<div>Doc 1</div>"], StripHTML()),
        ...     query="What's the weather?"
        ... )
        >>> prompt = packer.pack()

    Example (With Pipeline - multiple refiners):
        >>> from prompt_refiner import TextPacker, StripHTML, NormalizeWhitespace, Pipeline
        >>> cleaner = StripHTML() | NormalizeWhitespace()
        >>> # Or: cleaner = Pipeline([StripHTML(), NormalizeWhitespace()])
        >>> packer = TextPacker(
        ...     text_format=TextFormat.MARKDOWN,
        ...     system="You are helpful.",
        ...     context=(["<div>Doc 1</div>"], cleaner),
        ...     query="What's the weather?"
        ... )
        >>> prompt = packer.pack()
    """
    super().__init__(track_tokens, token_counter)
    self.text_format = text_format
    self.separator = separator if separator is not None else "\n\n"

    logger.debug(
        f"TextPacker initialized with format={text_format.value}, "
        f"separator={repr(self.separator)}"
    )

    # Auto-add items if provided (convenient API)
    # Extract content and refiner from tuple if provided
    # Apply default strategies when no explicit refiner provided
    if system is not None:
        system_content, system_refiner = self._extract_field(system)
        # Apply MinimalStrategy to system if no explicit refiner
        if system_refiner is None:
            system_refiner = MinimalStrategy()
        self.add(system_content, role="system", refine_with=system_refiner)

    if context is not None:
        context_docs, context_refiner = self._extract_field(context)
        # Apply StandardStrategy to context if no explicit refiner
        if context_refiner is None:
            context_refiner = StandardStrategy()
        for doc in context_docs:
            self.add(doc, role="context", refine_with=context_refiner)

    if history is not None:
        history_msgs, history_refiner = self._extract_field(history)
        # Apply StandardStrategy to history if no explicit refiner
        if history_refiner is None:
            history_refiner = StandardStrategy()
        for msg in history_msgs:
            self.add(msg["content"], role=msg["role"], refine_with=history_refiner)

    if query is not None:
        query_content, query_refiner = self._extract_field(query)
        # Apply MinimalStrategy to query if no explicit refiner
        if query_refiner is None:
            query_refiner = MinimalStrategy()
        self.add(query_content, role="query", refine_with=query_refiner)

Functions

quick_pack classmethod
quick_pack(
    system=None,
    context=None,
    history=None,
    query=None,
    text_format=TextFormat.RAW,
    separator=None,
)

One-liner to create packer and pack text immediately.

Default refining strategies are automatically applied (same as init): - system/query: MinimalStrategy - context/history: StandardStrategy

Parameters:

Name Type Description Default
system Optional[Union[str, Tuple[str, Refiner]]]

System message (str or (str, Refiner/Pipeline) tuple)

None
context Optional[Union[List[str], Tuple[List[str], Refiner]]]

Context documents (list or (list, Refiner/Pipeline) tuple)

None
history Optional[Union[List[Dict[str, str]], Tuple[List[Dict[str, str]], Refiner]]]

Conversation history (list or (list, Refiner/Pipeline) tuple)

None
query Optional[Union[str, Tuple[str, Refiner]]]

Current query (str or (str, Refiner/Pipeline) tuple)

None
text_format TextFormat

Text formatting strategy (RAW, MARKDOWN, XML)

RAW
separator Optional[str]

String to join items

None

Returns:

Type Description
str

Packed text ready for completion API

Example (Simple): >>> prompt = TextPacker.quick_pack( ... text_format=TextFormat.MARKDOWN, ... system="You are helpful.", ... context=["Doc 1", "Doc 2"], ... query="What's the weather?" ... )

Example (With single Refiner): >>> from prompt_refiner import TextPacker, StripHTML, TextFormat >>> prompt = TextPacker.quick_pack( ... text_format=TextFormat.MARKDOWN, ... system="You are helpful.", ... context=(["

Doc 1
"], StripHTML()), ... query="What's the weather?" ... )

Example (With Pipeline - multiple refiners): >>> from prompt_refiner import ( ... TextPacker, StripHTML, NormalizeWhitespace, TextFormat, Pipeline ... ) >>> cleaner = StripHTML() | NormalizeWhitespace() >>> # Or: cleaner = Pipeline([StripHTML(), NormalizeWhitespace()]) >>> prompt = TextPacker.quick_pack( ... text_format=TextFormat.MARKDOWN, ... system="You are helpful.", ... context=(["

Doc 1
"], cleaner), ... query="What's the weather?" ... )

Source code in src/prompt_refiner/packer/text.py
@classmethod
def quick_pack(
    cls,
    system: Optional[Union[str, Tuple[str, Refiner]]] = None,
    context: Optional[Union[List[str], Tuple[List[str], Refiner]]] = None,
    history: Optional[
        Union[
            List[Dict[str, str]],
            Tuple[List[Dict[str, str]], Refiner],
        ]
    ] = None,
    query: Optional[Union[str, Tuple[str, Refiner]]] = None,
    text_format: TextFormat = TextFormat.RAW,
    separator: Optional[str] = None,
) -> str:
    """
    One-liner to create packer and pack text immediately.

    Default refining strategies are automatically applied (same as __init__):
    - system/query: MinimalStrategy
    - context/history: StandardStrategy

    Args:
        system: System message (str or (str, Refiner/Pipeline) tuple)
        context: Context documents (list or (list, Refiner/Pipeline) tuple)
        history: Conversation history (list or (list, Refiner/Pipeline) tuple)
        query: Current query (str or (str, Refiner/Pipeline) tuple)
        text_format: Text formatting strategy (RAW, MARKDOWN, XML)
        separator: String to join items

    Returns:
        Packed text ready for completion API

    Example (Simple):
        >>> prompt = TextPacker.quick_pack(
        ...     text_format=TextFormat.MARKDOWN,
        ...     system="You are helpful.",
        ...     context=["Doc 1", "Doc 2"],
        ...     query="What's the weather?"
        ... )

    Example (With single Refiner):
        >>> from prompt_refiner import TextPacker, StripHTML, TextFormat
        >>> prompt = TextPacker.quick_pack(
        ...     text_format=TextFormat.MARKDOWN,
        ...     system="You are helpful.",
        ...     context=(["<div>Doc 1</div>"], StripHTML()),
        ...     query="What's the weather?"
        ... )

    Example (With Pipeline - multiple refiners):
        >>> from prompt_refiner import (
        ...     TextPacker, StripHTML, NormalizeWhitespace, TextFormat, Pipeline
        ... )
        >>> cleaner = StripHTML() | NormalizeWhitespace()
        >>> # Or: cleaner = Pipeline([StripHTML(), NormalizeWhitespace()])
        >>> prompt = TextPacker.quick_pack(
        ...     text_format=TextFormat.MARKDOWN,
        ...     system="You are helpful.",
        ...     context=(["<div>Doc 1</div>"], cleaner),
        ...     query="What's the weather?"
        ... )
    """
    packer = cls(
        text_format=text_format,
        separator=separator,
        system=system,
        context=context,
        history=history,
        query=query,
    )
    return packer.pack()
pack
pack()

Pack items into formatted text for completion APIs.

MARKDOWN format uses grouped sections: - INSTRUCTIONS: System prompts (ROLE_SYSTEM) - CONTEXT: RAG documents (ROLE_CONTEXT) - CONVERSATION: User/assistant history (ROLE_USER, ROLE_ASSISTANT) - INPUT: Current user query (ROLE_QUERY)

Returns:

Type Description
str

Formatted text string ready for completion API

Example

prompt = packer.pack() response = completion.create(model="llama-2-70b", prompt=prompt)

Source code in src/prompt_refiner/packer/text.py
def pack(self) -> str:
    """
    Pack items into formatted text for completion APIs.

    MARKDOWN format uses grouped sections:
    - INSTRUCTIONS: System prompts (ROLE_SYSTEM)
    - CONTEXT: RAG documents (ROLE_CONTEXT)
    - CONVERSATION: User/assistant history (ROLE_USER, ROLE_ASSISTANT)
    - INPUT: Current user query (ROLE_QUERY)

    Returns:
        Formatted text string ready for completion API

    Example:
        >>> prompt = packer.pack()
        >>> response = completion.create(model="llama-2-70b", prompt=prompt)
    """
    selected_items = self._select_items()

    if not selected_items:
        logger.warning("No items selected, returning empty string")
        return ""

    # MARKDOWN format: Use grouped sections (saves tokens)
    if self.text_format == TextFormat.MARKDOWN:
        result = self._pack_markdown_grouped(selected_items)
    else:
        # RAW and XML: Use item-by-item formatting
        parts = []
        for item in selected_items:
            formatted = self._format_item(item)
            parts.append(formatted)
        result = self.separator.join(parts)

    logger.info(f"Packed {len(selected_items)} items (format={self.text_format.value})")
    return result

BasePacker

Abstract base class providing common packer functionality. You typically won't use this directly.

prompt_refiner.packer.BasePacker

BasePacker(track_tokens=False, token_counter=None)

Bases: ABC

Abstract base class for prompt packers.

Provides common functionality: - Adding items with priorities - JIT refinement with strategies/operations - Priority-based sorting

Subclasses must implement: - pack(): Format and return packed items

Initialize packer.

Parameters:

Name Type Description Default
track_tokens bool

Enable token tracking to measure refinement effectiveness

False
token_counter Optional[Callable[[str], int]]

Function to count tokens (required if track_tokens=True)

None
Source code in src/prompt_refiner/packer/base.py
def __init__(
    self,
    track_tokens: bool = False,
    token_counter: Optional[Callable[[str], int]] = None,
):
    """
    Initialize packer.

    Args:
        track_tokens: Enable token tracking to measure refinement effectiveness
        token_counter: Function to count tokens (required if track_tokens=True)
    """
    self._items: List[PackableItem] = []
    self._insertion_counter = 0

    # Token tracking
    self._track_tokens = track_tokens
    self._token_counter = token_counter
    self._raw_tokens = 0
    self._refined_tokens = 0

    # Validate: if tracking enabled, counter is required
    if self._track_tokens and self._token_counter is None:
        raise ValueError("token_counter is required when track_tokens=True")

Attributes

token_stats property
token_stats

Get token savings statistics (only available when track_tokens=True).

Returns:

Type Description
Dict[str, Any]

Dictionary with the following keys: - raw_tokens: int - Total tokens before refinement - refined_tokens: int - Total tokens after refinement - saved_tokens: int - Tokens saved by refinement - saving_percent: str - Percentage saved (e.g., "25.5%")

Raises:

Type Description
ValueError

If token tracking is not enabled

Example

packer = MessagesPacker(track_tokens=True, token_counter=character_based_counter) packer.add("

Hello
", role="user", refine_with=StripHTML()) stats = packer.token_stats print(stats)

Functions

add
add(content, role, priority=None, refine_with=None)

Add an item to the packer.

Parameters:

Name Type Description Default
content str

Text content to add

required
role RoleType

Semantic role (required). Use ROLE_* constants: - ROLE_SYSTEM: System instructions - ROLE_QUERY: Current user question - ROLE_CONTEXT: RAG retrieved documents - ROLE_USER: User messages in conversation history - ROLE_ASSISTANT: Assistant messages in history

required
priority Optional[int]

Priority level (use PRIORITY_* constants). If None, infers from role: - ROLE_SYSTEM → PRIORITY_SYSTEM (0) - ROLE_QUERY → PRIORITY_QUERY (10) - ROLE_CONTEXT → PRIORITY_HIGH (20) - ROLE_USER/ROLE_ASSISTANT → PRIORITY_LOW (40) - Other roles → PRIORITY_MEDIUM (30)

None
refine_with Optional[Refiner]

Optional refiner or pipeline to apply before adding. Can be: - Single refiner: StripHTML() - Pipeline: StripHTML() | NormalizeWhitespace() - Pipeline from list: Pipeline([StripHTML(), NormalizeWhitespace()])

None

Returns:

Type Description
BasePacker

Self for method chaining

Source code in src/prompt_refiner/packer/base.py
def add(
    self,
    content: str,
    role: RoleType,
    priority: Optional[int] = None,
    refine_with: Optional[Refiner] = None,
) -> "BasePacker":
    """
    Add an item to the packer.

    Args:
        content: Text content to add
        role: Semantic role (required). Use ROLE_* constants:
            - ROLE_SYSTEM: System instructions
            - ROLE_QUERY: Current user question
            - ROLE_CONTEXT: RAG retrieved documents
            - ROLE_USER: User messages in conversation history
            - ROLE_ASSISTANT: Assistant messages in history
        priority: Priority level (use PRIORITY_* constants). If None, infers from role:
            - ROLE_SYSTEM → PRIORITY_SYSTEM (0)
            - ROLE_QUERY → PRIORITY_QUERY (10)
            - ROLE_CONTEXT → PRIORITY_HIGH (20)
            - ROLE_USER/ROLE_ASSISTANT → PRIORITY_LOW (40)
            - Other roles → PRIORITY_MEDIUM (30)
        refine_with: Optional refiner or pipeline to apply before adding.
            Can be:
            - Single refiner: StripHTML()
            - Pipeline: StripHTML() | NormalizeWhitespace()
            - Pipeline from list: Pipeline([StripHTML(), NormalizeWhitespace()])

    Returns:
        Self for method chaining
    """
    # Token tracking: count raw tokens BEFORE refinement
    if self._track_tokens:
        self._raw_tokens += self._token_counter(content)

    # Smart priority defaults based on semantic roles
    if priority is None:
        if role == ROLE_SYSTEM:
            priority = PRIORITY_SYSTEM  # 0 - Highest priority
        elif role == ROLE_QUERY:
            priority = PRIORITY_QUERY  # 10 - Current query is critical
        elif role == ROLE_CONTEXT:
            priority = PRIORITY_HIGH  # 20 - RAG documents
        elif role in (ROLE_USER, ROLE_ASSISTANT):
            priority = PRIORITY_LOW  # 40 - Conversation history
        else:
            priority = PRIORITY_MEDIUM  # 30 - Unknown roles

    # JIT refinement
    refined_content = content
    if refine_with:
        # Apply refinement (Refiner or Pipeline both use process() method)
        refined_content = refine_with.process(content)

    # Token tracking: count refined tokens AFTER refinement
    if self._track_tokens:
        self._refined_tokens += self._token_counter(refined_content)

    content = refined_content

    item = PackableItem(
        content=content,
        priority=priority,
        insertion_index=self._insertion_counter,
        role=role,
    )

    self._items.append(item)
    self._insertion_counter += 1

    logger.debug(f"Added item: priority={priority}, role={role}")
    return self
add_messages
add_messages(messages, priority=PRIORITY_LOW)

Batch add messages (convenience method).

Defaults to PRIORITY_LOW because conversation history is usually the first to be dropped in favor of RAG context and current queries.

Parameters:

Name Type Description Default
messages List[Dict[str, str]]

List of message dicts with 'role' and 'content' keys

required
priority int

Priority level for all messages (default: PRIORITY_LOW for history)

PRIORITY_LOW

Returns:

Type Description
BasePacker

Self for method chaining

Source code in src/prompt_refiner/packer/base.py
def add_messages(
    self,
    messages: List[Dict[str, str]],
    priority: int = PRIORITY_LOW,
) -> "BasePacker":
    """
    Batch add messages (convenience method).

    Defaults to PRIORITY_LOW because conversation history is usually the first
    to be dropped in favor of RAG context and current queries.

    Args:
        messages: List of message dicts with 'role' and 'content' keys
        priority: Priority level for all messages (default: PRIORITY_LOW for history)

    Returns:
        Self for method chaining
    """
    for msg in messages:
        self.add(content=msg["content"], role=msg["role"], priority=priority)
    return self
reset
reset()

Reset the packer, removing all items and token stats.

Returns:

Type Description
BasePacker

Self for method chaining

Source code in src/prompt_refiner/packer/base.py
def reset(self) -> "BasePacker":
    """
    Reset the packer, removing all items and token stats.

    Returns:
        Self for method chaining
    """
    self._items.clear()
    self._insertion_counter = 0

    # Reset token tracking
    if self._track_tokens:
        self._raw_tokens = 0
        self._refined_tokens = 0

    logger.debug("Packer reset")
    return self
get_items
get_items()

Get information about all added items.

Returns:

Type Description
List[dict]

List of dictionaries containing item metadata

Source code in src/prompt_refiner/packer/base.py
def get_items(self) -> List[dict]:
    """
    Get information about all added items.

    Returns:
        List of dictionaries containing item metadata
    """
    return [
        {
            "priority": item.priority,
            "insertion_index": item.insertion_index,
            "role": item.role,
        }
        for item in self._items
    ]
pack abstractmethod
pack()

Pack items into final format.

Subclasses must implement this to return format-specific output: - MessagesPacker: Returns List[Dict[str, str]] - TextPacker: Returns str

Source code in src/prompt_refiner/packer/base.py
@abstractmethod
def pack(self):
    """
    Pack items into final format.

    Subclasses must implement this to return format-specific output:
    - MessagesPacker: Returns List[Dict[str, str]]
    - TextPacker: Returns str
    """
    pass

Constants

from prompt_refiner import (
    ROLE_SYSTEM,      # "system" - System instructions (auto: PRIORITY_SYSTEM = 0)
    ROLE_QUERY,       # "query" - Current user question (auto: PRIORITY_QUERY = 10)
    ROLE_CONTEXT,     # "context" - RAG documents (auto: PRIORITY_HIGH = 20)
    ROLE_USER,        # "user" - User messages in history (auto: PRIORITY_LOW = 40)
    ROLE_ASSISTANT,   # "assistant" - Assistant messages in history (auto: PRIORITY_LOW = 40)
)

Priority Constants (Optional)

from prompt_refiner import (
    PRIORITY_SYSTEM,   # 0 - Absolute must-have (system prompts)
    PRIORITY_QUERY,    # 10 - Current user query (critical for response)
    PRIORITY_HIGH,     # 20 - Important context (core RAG documents)
    PRIORITY_MEDIUM,   # 30 - Normal priority (general RAG documents)
    PRIORITY_LOW,      # 40 - Optional content (old conversation history)
)

Smart Priority Defaults

You usually don't need to specify priority! Just use semantic roles and priority is auto-inferred:

# Recommended: Use semantic roles (priority auto-inferred)
packer.add("System prompt", role=ROLE_SYSTEM)  # Auto: PRIORITY_SYSTEM (0)
packer.add("User query", role=ROLE_QUERY)      # Auto: PRIORITY_QUERY (10)
packer.add("RAG doc", role=ROLE_CONTEXT)       # Auto: PRIORITY_HIGH (20)

# Advanced: Override priority if needed
packer.add("Urgent RAG doc", role=ROLE_CONTEXT, priority=PRIORITY_QUERY)

TextFormat Enum

from prompt_refiner import TextFormat

TextFormat.RAW       # No delimiters, simple concatenation
TextFormat.MARKDOWN  # Use ### ROLE: headers (grouped sections in v0.1.3+)
TextFormat.XML       # Use <role>content</role> tags

Default Refining Strategies

Version 0.2.1+ introduces automatic refining strategies. When no explicit refiner is provided, packers apply sensible defaults:

  • system/query: MinimalStrategy (StripHTML + NormalizeWhitespace)
  • context/history: StandardStrategy (StripHTML + NormalizeWhitespace + Deduplicate)
from prompt_refiner import MessagesPacker

# Automatic refining with defaults
packer = MessagesPacker(
    system="<p>You are helpful.</p>",  # Auto: MinimalStrategy
    context=["<div>Doc 1</div>"],      # Auto: StandardStrategy
    query="<span>What's the weather?</span>"  # Auto: MinimalStrategy
)

# Override with custom pipeline
packer = MessagesPacker(
    context=(["<div>Doc</div>"], StripHTML() | NormalizeWhitespace())
)

Token Savings Tracking

Version 0.1.5+ introduces automatic token savings tracking to measure the optimization impact of refine_with operations.

Enable Tracking

# Opt-in to tracking with track_savings parameter
packer = MessagesPacker(track_savings=True)

# Add items with refinement
packer.add(
    "<div>  Messy   HTML  </div>",
    role=ROLE_CONTEXT,
    refine_with=[StripHTML(), NormalizeWhitespace()]
)

# Get savings statistics
savings = packer.get_token_savings()
# Returns: {
#   'original_tokens': 25,      # Tokens before refinement
#   'refined_tokens': 12,       # Tokens after refinement
#   'saved_tokens': 13,         # Tokens saved
#   'saving_percent': 52.0,     # Percentage saved
#   'items_refined': 1          # Count of refined items
# }

Key Features

  • Opt-in: Disabled by default (no overhead when not needed)
  • Automatic aggregation: Tracks all items that use refine_with
  • Per-item and total: Aggregates savings across all refined items
  • Works with both packers: Available for MessagesPacker and TextPacker

Example with Real API

from prompt_refiner import MessagesPacker, StripHTML
from openai import OpenAI

client = OpenAI()
packer = MessagesPacker(track_savings=True)

# Add multiple RAG documents with automatic cleaning
for doc in scraped_html_docs:
    packer.add(doc, role="context", refine_with=StripHTML())

# Pack messages and check savings
messages = packer.pack()
savings = packer.get_token_savings()

print(f"Saved {savings['saved_tokens']} tokens ({savings['saving_percent']:.1f}%)")
# Example output: "Saved 1,234 tokens (23.5%)"

# Use cleaned messages with API
response = client.chat.completions.create(
    model="gpt-4o",
    messages=messages
)

When to Use

Use token savings tracking when: - You want to measure ROI of optimization efforts - Demonstrating token savings to stakeholders - A/B testing different refinement strategies - Monitoring optimization impact in production

Skip tracking when: - Not using refine_with parameter (returns empty dict) - Performance is absolutely critical (negligible overhead, but why enable?) - You don't need savings metrics

Combine with CountTokens

For pipeline optimization (not packer), use CountTokens instead:

from prompt_refiner import CountTokens, StripHTML, NormalizeWhitespace

counter = CountTokens(original_text=dirty_html)
pipeline = StripHTML() | NormalizeWhitespace()
clean = pipeline.run(dirty_html)
counter.process(clean)
print(counter.format_stats())

MessagesPacker Examples

Basic Usage

from prompt_refiner import MessagesPacker

packer = MessagesPacker()

packer.add(
    "You are a helpful assistant.",
    role="system"  # Auto: PRIORITY_SYSTEM (0)
)

packer.add(
    "What is prompt-refiner?",
    role="query"  # Auto: PRIORITY_QUERY (10)
)

messages = packer.pack()  # List[Dict[str, str]]
# Use directly: openai.chat.completions.create(messages=messages)

RAG with Conversation History

from prompt_refiner import MessagesPacker, StripHTML

packer = MessagesPacker()

# System prompt
packer.add(
    "Answer based on provided context.",
    role="system"  # Auto: PRIORITY_SYSTEM (0)
)

# RAG documents with JIT cleaning
packer.add(
    "<p>Prompt-refiner is a library...</p>",
    role="context",  # Auto: PRIORITY_HIGH (20)
    refine_with=StripHTML()
)

# Old conversation history
old_messages = [
    {"role": "user", "content": "What is this library?"},
    {"role": "assistant", "content": "It's a tool for optimizing prompts."}
]
packer.add_messages(old_messages)  # Auto: PRIORITY_LOW (40) for history

# Current query
packer.add(
    "How does it reduce costs?",
    role="query"  # Auto: PRIORITY_QUERY (10)
)

messages = packer.pack()

TextPacker Examples

Basic Usage

from prompt_refiner import TextPacker, TextFormat

packer = TextPacker(text_format=TextFormat.MARKDOWN)

packer.add(
    "You are a QA assistant.",
    role="system"  # Auto: PRIORITY_SYSTEM (0)
)

packer.add(
    "Context: Prompt-refiner is a library...",
    role="context"  # Auto: PRIORITY_HIGH (20)
)

packer.add(
    "What is prompt-refiner?",
    role="query"  # Auto: PRIORITY_QUERY (10)
)

prompt = packer.pack()  # str
# Use with: completion.create(prompt=prompt)

Text Format Comparison

from prompt_refiner import TextPacker, TextFormat

# RAW format (simple concatenation)
packer = TextPacker(text_format=TextFormat.RAW)
packer.add("System prompt", role="system")
packer.add("User query", role="query")
prompt = packer.pack()
# Output:
# System prompt
#
# User query

# MARKDOWN format (grouped sections in v0.1.3+)
packer = TextPacker(text_format=TextFormat.MARKDOWN)
packer.add("System prompt", role="system")
packer.add("Doc 1", role="context")
packer.add("Doc 2", role="context")
packer.add("User query", role="query")
prompt = packer.pack()
# Output:
# ### INSTRUCTIONS:
# System prompt
#
# ### CONTEXT:
# - Doc 1
# - Doc 2
#
# ### INPUT:
# User query

# XML format
packer = TextPacker(text_format=TextFormat.XML)
packer.add("System prompt", role="system")
packer.add("User query", role="query")
prompt = packer.pack()
# Output:
# <system>
# System prompt
# </system>
#
# <query>
# User query
# </query>

Common Features

JIT Refinement

Both packers support Just-In-Time refinement:

from prompt_refiner import StripHTML, NormalizeWhitespace

# Single operation
packer.add(
    "<div>HTML content</div>",
    role="context",
    refine_with=StripHTML()
)

# Multiple operations
packer.add(
    "<p>  Messy   HTML  </p>",
    role="context",
    refine_with=[StripHTML(), NormalizeWhitespace()]
)

Method Chaining

from prompt_refiner import MessagesPacker

messages = (
    MessagesPacker()
    .add("System prompt", role="system")
    .add("User query", role="query")
    .pack()
)

Inspection

from prompt_refiner import MessagesPacker

packer = MessagesPacker()
packer.add("Item 1", role="system")
packer.add("Item 2", role="query")

# Inspect items before packing
items = packer.get_items()
for item in items:
    print(f"Priority: {item['priority']}, Role: {item['role']}")

Reset

from prompt_refiner import MessagesPacker

packer = MessagesPacker()
packer.add("First batch", role="context")
messages1 = packer.pack()

# Clear and reuse
packer.reset()
packer.add("Second batch", role="context")
messages2 = packer.pack()

Algorithm Details

  1. Add Phase: Items are added with priorities, optional roles, and automatic/explicit refinement
  2. Refinement (v0.2.1+):
  3. Default strategies applied automatically (MinimalStrategy for system/query, StandardStrategy for context/history)
  4. Override with explicit refiner: context=(docs, StripHTML() | NormalizeWhitespace())
  5. Skip refinement: Use .add() method with refine_with=None
  6. Token Counting: Content tokens counted for savings tracking (when enabled)
  7. Sort Phase: Items are sorted by priority (lower number = higher priority), stable sort preserves insertion order
  8. Order Restoration: All items restored to insertion order for natural reading flow
  9. Format Phase:
  10. MessagesPacker: Returns List[Dict[str, str]] (semantic roles mapped to API roles)
  11. TextPacker: Returns formatted str based on text_format (RAW, MARKDOWN, or XML)

Tips

Choose the Right Packer

  • Use MessagesPacker for chat APIs (OpenAI, Anthropic)
  • Use TextPacker for completion APIs (Llama Base, GPT-3)

Use Semantic Roles (Recommended)

Semantic roles auto-infer priorities, making code clearer:

  • ROLE_SYSTEM: System instructions → PRIORITY_SYSTEM (0)
  • ROLE_QUERY: Current user question → PRIORITY_QUERY (10)
  • ROLE_CONTEXT: RAG documents → PRIORITY_HIGH (20)
  • ROLE_USER / ROLE_ASSISTANT: Conversation history → PRIORITY_LOW (40)
# Recommended: Clear intent with semantic roles
packer.add("System prompt", role=ROLE_SYSTEM)
packer.add("Current query", role=ROLE_QUERY)
packer.add("RAG doc", role=ROLE_CONTEXT)

Override Priority When Needed

Most of the time semantic roles are enough, but you can override:

# Make a RAG document urgent (higher priority than normal)
packer.add("Critical doc", role=ROLE_CONTEXT, priority=PRIORITY_QUERY)

Clean Before Packing

Use refine_with to clean items before token counting:

packer.add(
    dirty_html,
    role=ROLE_CONTEXT,
    refine_with=StripHTML()
)

Grouped MARKDOWN Saves Tokens

TextPacker with MARKDOWN format groups items by section, saving tokens:

# Old (per-item headers): ### CONTEXT:\nDoc 1\n\n### CONTEXT:\nDoc 2
# New (grouped): ### CONTEXT:\n- Doc 1\n- Doc 2