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.
- ui_only#
Whether this message is a UI/status message that must not be sent to the provider. Kept in the log for display, but excluded by prepare_messages().
- 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, ui_only: 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.
- preserve_lossless_log() None#
Write the full lossless history to lossless.jsonl.
Call before manual compaction to ensure the original transcript is preserved even after conversation.jsonl is replaced. Raises on failure so callers can abort before touching conversation.jsonl.
- 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