hbllmutils.history.history
Conversation history utilities for Large Language Model (LLM) workflows.
This module provides a compact set of utilities for building and managing
LLM-compatible message histories. It supports common content types such as
plain text, PIL.Image.Image instances, and mixed content lists.
Histories are stored in an immutable, sequence-like container that can be
serialized to JSON or YAML for persistence and reuse.
The module contains the following public components:
create_llm_message()- Convert content into an LLM API-compatible messageLLMHistory- Immutable sequence container for conversation history
Note
The history container is immutable. All modification methods return new instances, leaving the original history unchanged.
Example:
>>> from hbllmutils.history.history import LLMHistory, create_llm_message
>>> history = LLMHistory().with_user_message("Hello!")
>>> history = history.with_assistant_message("Hi there.")
>>> len(history)
2
>>> history[0]["role"]
'user'
>>> msg = create_llm_message(["Text", "More text"])
>>> msg["role"]
'user'
LLMContentTyping
- hbllmutils.history.history.LLMContentTyping
alias of
str|Image|List[str|Image]
LLMRoleTyping
- hbllmutils.history.history.LLMRoleTyping
alias of
Literal[‘system’, ‘user’, ‘assistant’, ‘tool’, ‘function’]
LLMHistory
- class hbllmutils.history.history.LLMHistory(history: List[dict] | None = None)[source]
A sequence-like container for managing LLM conversation history.
This class provides methods to build and maintain a conversation history with different roles (user, assistant, system, etc.). It implements the
collections.abc.Sequenceprotocol, allowing indexing and iteration. The class is designed to be immutable - all modification operations return new instances rather than modifying the existing one.The class supports:
Adding messages with specific roles (user, assistant, system, etc.)
Setting and updating system prompts
Serialization to JSON and YAML formats
Deserialization from JSON and YAML files
Sequence operations (indexing, slicing, iteration, length)
Hashing and equality comparison
Note
LLMHistoryis an immutable object. Any operation will cause a new object creation.- Parameters:
history (Optional[List[dict]]) – Optional initial history as a list of message dictionaries.
Example:
>>> history = LLMHistory() >>> history = history.with_user_message("Hello!") >>> history = history.with_assistant_message("Hi there!") >>> len(history) 2 >>> history[0] {'role': 'user', 'content': 'Hello!'}
- __eq__(other: Any) bool[source]
Check equality between LLMHistory instances.
Two LLMHistory instances are considered equal if they have the same message history content. Returns False if the other object is not an LLMHistory instance.
- Parameters:
other (LLMHistory) – Another LLMHistory instance to compare with.
- Returns:
True if histories are equal, False otherwise.
- Return type:
bool
Example:
>>> history1 = LLMHistory().with_user_message("Hello!") >>> history2 = LLMHistory().with_user_message("Hello!") >>> history1 == history2 True
- __getitem__(index: int | slice) dict | LLMHistory[source]
Get an item or slice from the history.
When indexing with a single integer, returns a deep copy of the message dictionary at that position. When slicing, returns a new
LLMHistoryinstance containing the sliced messages.- Parameters:
index (int or slice) – The index or slice to retrieve.
- Returns:
A single message dict (deep copy) or a new LLMHistory instance for slices.
- Return type:
dict or LLMHistory
Example:
>>> history = LLMHistory().with_user_message("Hello!") >>> history[0] {'role': 'user', 'content': 'Hello!'} >>> history[0:1] <LLMHistory object with 1 message>
- __hash__() int[source]
Generate a hash value for the LLMHistory instance.
The hash is computed based on the message history content, allowing LLMHistory instances to be used as dictionary keys or in sets. The hash is computed by recursively converting nested data structures (dicts, lists) to hashable types (tuples).
- Returns:
Hash value of the history.
- Return type:
int
Example:
>>> history1 = LLMHistory().with_user_message("Hello!") >>> history2 = LLMHistory().with_user_message("Hello!") >>> hash(history1) == hash(history2) True >>> history_set = {history1, history2} >>> len(history_set) 1
- __init__(history: List[dict] | None = None) None[source]
Initialize the LLMHistory instance.
- Parameters:
history (Optional[List[dict]]) – Optional initial history as a list of message dictionaries. Each dictionary should contain ‘role’ and ‘content’ keys.
- __len__() int[source]
Get the number of messages in the history.
- Returns:
The number of messages.
- Return type:
int
Example:
>>> history = LLMHistory() >>> len(history) 0 >>> history = history.with_user_message("Hello!") >>> len(history) 1
- clone() LLMHistory[source]
Create a deep copy of the current LLMHistory instance.
This method creates a new
LLMHistoryobject with a deep copy of the internal message history, ensuring that modifications to the clone do not affect the original instance.- Returns:
A new LLMHistory instance with copied message history.
- Return type:
Example:
>>> history = LLMHistory() >>> history = history.with_user_message("Hello!") >>> cloned = history.clone() >>> cloned = cloned.with_user_message("Another message") >>> len(history) 1 >>> len(cloned) 2
- dump_json(file: str, **params: Any) None[source]
Export the history to a JSON file.
Saves the conversation history to a JSON file with configurable formatting. The parent directory will be created if it doesn’t exist. Default parameters provide pretty-printed output with 2-space indentation, UTF-8 encoding, and sorted keys.
- Parameters:
file (str) – The file path to save the JSON data.
params – Additional parameters to pass to
json.dump()(e.g.,indent,ensure_ascii). Default parameters:indent=2,ensure_ascii=False,sort_keys=True.
- Raises:
IOError – If the file cannot be written.
Example:
>>> history = LLMHistory() >>> history = history.with_user_message("Hello!") >>> history.dump_json("conversation.json", indent=2)
- dump_yaml(file: str, **params: Any) None[source]
Export the history to a YAML file.
Saves the conversation history to a YAML file with configurable formatting. The parent directory will be created if it doesn’t exist. Default parameters provide human-readable output with block style, UTF-8 encoding, 2-space indentation, and sorted keys.
- Parameters:
file (str) – The file path to save the YAML data.
params – Additional parameters to pass to
yaml.dump()(e.g.,default_flow_style,indent). Default parameters:default_flow_style=False,allow_unicode=True,indent=2,sort_keys=True.
- Raises:
IOError – If the file cannot be written.
ImportError – If PyYAML is not installed.
Example:
>>> history = LLMHistory() >>> history = history.with_user_message("Hello!") >>> history.dump_yaml("conversation.yaml", default_flow_style=False)
- classmethod load_json(file: str) LLMHistory[source]
Load history from a JSON file.
Reads and validates a JSON file containing conversation history. The file must contain a list of message dictionaries, where each dictionary has
roleandcontentfields.- Parameters:
file (str) – The file path to load the JSON data from.
- Returns:
A new LLMHistory instance loaded from the file.
- Return type:
- Raises:
FileNotFoundError – If the file does not exist.
json.JSONDecodeError – If the file contains invalid JSON.
ValueError – If the JSON structure is invalid for LLMHistory.
Example:
>>> history = LLMHistory.load_json("conversation.json") >>> len(history) 1
- classmethod load_yaml(file: str) LLMHistory[source]
Load history from a YAML file.
Reads and validates a YAML file containing conversation history. The file must contain a list of message dictionaries, where each dictionary has
roleandcontentfields.- Parameters:
file (str) – The file path to load the YAML data from.
- Returns:
A new LLMHistory instance loaded from the file.
- Return type:
- Raises:
FileNotFoundError – If the file does not exist.
yaml.YAMLError – If the file contains invalid YAML.
ValueError – If the YAML structure is invalid for LLMHistory.
ImportError – If PyYAML is not installed.
Example:
>>> history = LLMHistory.load_yaml("conversation.yaml") >>> len(history) 1
- to_json() List[dict][source]
Convert the history to a JSON-serializable list of dictionaries.
Returns a deep copy of the internal message history, ensuring that modifications to the returned list do not affect the original history.
- Returns:
A list of message dictionaries.
- Return type:
List[dict]
Example:
>>> history = LLMHistory() >>> history = history.with_user_message("Hello!") >>> history.to_json() [{'role': 'user', 'content': 'Hello!'}]
- with_assistant_message(message: str | Image | List[str | Image]) LLMHistory[source]
Append an assistant message to the history.
This is a convenience method equivalent to calling
LLMHistory.with_message()withrole='assistant'. Creates a new LLMHistory instance with the appended message.- Parameters:
message (LLMContentTyping) – The message content.
- Returns:
A new LLMHistory instance with the appended assistant message.
- Return type:
Example:
>>> history = LLMHistory() >>> new_history = history.with_assistant_message("How can I help you?") >>> new_history[0]["role"] 'assistant'
- with_message(role: Literal['system', 'user', 'assistant', 'tool', 'function'], message: str | Image | List[str | Image]) LLMHistory[source]
Append a message with a specific role to the history.
This method creates a new
LLMHistoryinstance with the appended message, leaving the original instance unchanged. The message content is processed throughcreate_llm_message()to ensure proper formatting.- Parameters:
role (LLMRoleTyping) – The role of the message sender.
message (LLMContentTyping) – The message content.
- Returns:
A new LLMHistory instance with the appended message.
- Return type:
Example:
>>> history = LLMHistory() >>> new_history = history.with_message("user", "Hello!") >>> len(history) 0 >>> len(new_history) 1
- with_system_prompt(message: str | Image | List[str | Image]) LLMHistory[source]
Set or update the system prompt.
If a system message already exists at the beginning of the history, it will be replaced. Otherwise, the new system message will be inserted at the start of the history. This method creates a new
LLMHistoryinstance.System prompts are typically used to set the behavior or context for the LLM at the start of a conversation.
- Parameters:
message (LLMContentTyping) – The system prompt content.
- Returns:
A new LLMHistory instance with the system prompt set or updated.
- Return type:
Example:
>>> history = LLMHistory() >>> new_history = history.with_system_prompt("You are a helpful assistant.") >>> new_history[0]["role"] 'system' >>> new_history[0]["content"] 'You are a helpful assistant.'
- with_user_message(message: str | Image | List[str | Image]) LLMHistory[source]
Append a user message to the history.
This is a convenience method equivalent to calling
LLMHistory.with_message()withrole='user'. Creates a new LLMHistory instance with the appended message.- Parameters:
message (LLMContentTyping) – The message content.
- Returns:
A new LLMHistory instance with the appended user message.
- Return type:
Example:
>>> history = LLMHistory() >>> new_history = history.with_user_message("Hello!") >>> new_history[0]["role"] 'user'
create_llm_message
- hbllmutils.history.history.create_llm_message(message: str | Image | List[str | Image], role: Literal['system', 'user', 'assistant', 'tool', 'function'] = 'user') dict[source]
Create a structured LLM message from various content types.
This function converts different types of message content (text, images, or mixed) into a standardized dictionary format suitable for LLM APIs. The function handles:
Plain text strings: Returned as-is in the content field
PIL Images: Converted to blob URLs and wrapped in image_url format
Lists of mixed content: Each item is converted to appropriate format (text or image_url)
- Parameters:
message (LLMContentTyping) – The message content, which can be a string, PIL Image, or list of strings/images.
role (LLMRoleTyping) – The role of the message sender (default is ‘user’).
- Returns:
A dictionary containing the role and formatted content.
- Return type:
dict
- Raises:
TypeError – If the message type is unsupported or if a list item has an unsupported type.
Example:
>>> create_llm_message("Hello, world!") {'role': 'user', 'content': 'Hello, world!'} >>> # Mixed content: text plus an image instance >>> create_llm_message(["Text message", Image.new("RGB", (10, 10))], role="assistant") {'role': 'assistant', 'content': [{'type': 'text', 'text': 'Text message'}, {'type': 'image_url', 'image_url': '...'}]}