Core#
The building blocks of a conversation: messages, the code blocks inside them that tools execute, and the log that stores, loads, and branches a conversation. See Usage for managing conversations, and Glossary for terminology.
Message#
A message in the conversation.
- class gptme.message.Message#
A message in the assistant conversation.
- role#
The role of the message sender (system, user, or assistant).
- content#
The content of the message.
- timestamp#
The timestamp of the message.
- files#
Files attached to the message, could e.g. be images for vision.
- pinned#
Whether this message should be pinned to the top of the chat, and never context-trimmed.
- hide#
Whether this message should be hidden from the chat output (but still be sent to the assistant).
- quiet#
Whether this message should be printed on execution (will still print on resume, unlike hide). This is not persisted to the log file.
- metadata#
Optional metadata including token usage and cost information.
- __init__(role: ~typing.Literal['system', 'user', 'assistant'], content: str, timestamp: ~datetime.datetime = <factory>, files: list[~pathlib.Path | ~gptme.util.uri.URI] = <factory>, file_hashes: dict[str, str] = <factory>, call_id: str | None = None, pinned: bool = False, hide: bool = False, quiet: bool = False, ephemeral_ttl: int | None = None, metadata: ~gptme.message.MessageMetadata | None = None, terminal_display_content: str | None = None) None#
- concat(other: Message, separator: str = '\n\n') Message#
Concatenate two messages of the same role.
Merges content with separator, and combines files and file_hashes. Preserves timestamp from the first message.
- Parameters:
other – Message to concatenate with this one
separator – String to join content with (default: “nn”)
- Returns:
New Message with merged content and files
- Raises:
ValueError – If messages have different roles
- format(oneline: bool = False, highlight: bool = False, max_length: int | None = None) str#
Format the message for display.
- Parameters:
oneline – Whether to format the message as a single line
highlight – Whether to highlight code blocks
max_length – Maximum length of the message. If None, no truncation is applied. If set, will truncate at first newline or max_length, whichever comes first.
- classmethod from_toml(toml: str) Self#
Converts a TOML string to a message.
The string can be a single [[message]].
Codeblock#
A codeblock in a message, possibly executable by tools.
- class gptme.codeblock.Codeblock#
Codeblock(lang: str, content: str, path: str | None = None, start: int | None = None, fence: str = <factory>)
- __init__(lang: str, content: str, path: str | None = None, start: int | None = None, fence: str = <factory>) None#
- classmethod from_xml(content: str) Codeblock#
Example
<codeblock lang=”python” path=”example.py”> print(“Hello, world!”) </codeblock>
LogManager#
Holds the current conversation as a list of messages, saves and loads the conversation to and from files, supports branching, etc.
Conversation log management for gptme.
Split into focused modules:
manager: Log data structure, LogManager orchestrator, message processing
conversations: ConversationMeta, conversation querying and management
- class gptme.logmanager.ConversationMeta#
Metadata about a conversation.
- __init__(id: str, name: str, path: str, created: float, modified: float, messages: int, branches: int, workspace: str, agent_name: str | None = None, agent_path: str | None = None, agent_avatar: str | None = None, agent_urls: dict[str, str] | None = None, model: str | None = None, total_cost: float = 0.0, total_input_tokens: int = 0, total_output_tokens: int = 0, total_cache_read_tokens: int = 0, models_usage: dict[str, dict[str, ~typing.Any]] = <factory>, last_message_role: str | None = None, last_message_preview: str | None = None) None#
- class gptme.logmanager.Log#
Log(messages: list[gptme.message.Message] = <factory>, persisted: tuple[gptme.message.Message, …] = (), persisted_path: pathlib.Path | None = None, persisted_size: int | None = None)
- class gptme.logmanager.LogManager#
Manages a conversation log.
- Can be used as a context manager to ensure locks are properly released:
- with LogManager.load(logdir) as manager:
# use manager pass
- __init__(log: list[Message] | None = None, logdir: str | Path | None = None, branch: str | None = None, lock: bool = True, view: str | None = None)#
- append(msg: Message) None#
Appends a message to the log, writes the log, prints the message.
When on a view branch, implements dual-write: - Appends to master branch (preserves full history) - Appends to current view (maintains compacted context)
- create_view(name: str, log: Log | list[Message]) None#
Create a new view branch with compacted content.
- Parameters:
name – View name (e.g., ‘compacted-001’)
log – The compacted log to store
- classmethod get_current_log() LogManager | None#
Get the current LogManager instance for this context.
- classmethod load(logdir: str | Path, initial_msgs: list[Message] | None = None, branch: str = 'main', create: bool = False, lock: bool = True, **kwargs) LogManager#
Loads a conversation log.
- read_model_trace()#
Read the persisted ModelSelectionTrace from logdir, or None if absent.
- Returns:
ModelSelectionTrace if model_selection_trace.json exists, else None.
- snapshot_message_files(msgs: list[Message]) list[Message]#
Snapshot message attachments while preserving valid existing hashes.
- gptme.logmanager.check_for_modifications(log: Log) bool#
Check if the most recent assistant message (since last user) has file modifications.
Only checks the last assistant message to prevent re-triggering pre-commit/autocommit when the agent responds to hook output (e.g. pre-commit failure) without making new file changes. Checking all assistant messages since the last user would cause infinite loops: the original save is still visible after the agent writes a text response to the failure.
- gptme.logmanager.conversation_name_error(value: str) str | None#
Return a validation error for unsafe conversation names, if any.
- gptme.logmanager.delete_conversation(conv_id: str) bool#
Delete a conversation by its ID.
- Parameters:
conv_id – The conversation ID to delete
- Returns:
True if deleted successfully, False if not found
- Raises:
PermissionError – If the conversation directory cannot be deleted
- gptme.logmanager.ephemeral_cache_boundary(msgs_after_pruning: list[Message]) int | None#
Return the index (in msgs_after_pruning) of the last message before the first surviving ephemeral message, or None if no ephemeral messages remain.
This is the stable boundary that should receive a cache breakpoint: it’s the last message in the non-ephemeral prefix that will remain stable across turns as the ephemeral block continues to expire.
- gptme.logmanager.get_conversation_by_id(conv_id: str, *, detail: bool = True) ConversationMeta | None#
Get a conversation by its ID.
Uses direct path lookup (O(1) file access) rather than scanning all conversations.
- Parameters:
conv_id – The conversation ID to find
- Returns:
ConversationMeta if found, None otherwise
- gptme.logmanager.get_conversation_meta_direct(conv_id: str, *, detail: bool = True, logs_dir: Path | None = None) ConversationMeta | None#
Get a single conversation’s metadata by direct path lookup.
Bypasses the full glob+stat scan used by get_conversations(). O(1) file access instead of O(N_conversations) — suitable for partial cache updates where only one conversation changed.
Returns None when the conversation directory or JSONL file does not exist.
- gptme.logmanager.get_conversations(*, detail: bool = True, include_test: bool = True) Generator[ConversationMeta, None, None]#
Returns all conversations.
- Parameters:
detail – If True (default), performs a full JSONL scan to compute exact costs, token counts, and model info. If False, reads only the tail of each file for a faster scan — suitable for list/search endpoints where cost/token aggregates are not displayed.
include_test – If True (default), includes test/eval conversations. If False, skips them before scanning any files or loading per-conversation config.
- gptme.logmanager.get_user_conversations(*, detail: bool = True) Generator[ConversationMeta, None, None]#
Returns all user conversations, excluding ones used for testing, evals, etc.
- gptme.logmanager.list_conversations(limit: int = 20, include_test: bool = False, *, detail: bool = True) list[ConversationMeta]#
List conversations with a limit.
- Parameters:
limit – Maximum number of conversations to return
include_test – Whether to include test conversations
detail – If True, performs full JSONL scan for costs/tokens. If False, uses fast tail-only scan.
- gptme.logmanager.prepare_messages(msgs: list[Message], workspace: Path | None = None, logdir: Path | None = None) list[Message]#
Prepares the messages before sending to the LLM. - Takes the stored gptme conversation log - Enhances it with context such as file contents - Transforms it to the format expected by LLM providers
- gptme.logmanager.prune_ephemeral_messages(msgs: list[Message]) list[Message]#
Remove messages whose ephemeral_ttl has been exceeded.
ephemeral_ttl=Nmeans: keep the message for N later assistant turns, then drop it. Walk backward counting assistant messages seen after each message; drop ephemeral messages once the count exceeds their TTL. Pinned messages are never dropped regardless of TTL.
- gptme.logmanager.rename_conversation(conv_id: str, new_name: str) bool#
Rename a conversation by updating its display name in the chat config.
- Parameters:
conv_id – The conversation ID to rename
new_name – The new display name for the conversation
- Returns:
True if renamed successfully, False if not found