API Reference
This section provides a detailed API reference for the core components of manolo-bot.
AI Components
- exception manolo_bot.ai.llmbot.FileTooLargeError[source]
Bases:
ValueErrorException raised when a file exceeds the allowed size.
- class manolo_bot.ai.llmbot.LLMBot(llm, bot_config, system_instructions, messages_storage, tools=None, documents_storage=None, system_instructions_mapping=None)[source]
Bases:
objectBase class for a Telegram LLM Chat Bot.
Handles interaction with the LLM, message processing, and context management.
- async answer_document_message(chat_id, text, document_url, filename)[source]
Answer a document message.
- Parameters:
chat_id (
int) – Chat IDtext (
str) – Text to answer (user prompt/caption)document_url (
str) – URL to download the documentfilename (
str) – Original filename
- Return type:
BaseMessage- Returns:
Response
- async answer_image_message(chat_id, text, image)[source]
Answer an image message. :type chat_id:
int:param chat_id: Chat ID :type text:str:param text: Text to answer :type image:str:param image: Image to answer- Return type:
BaseMessage- Returns:
Response
- async answer_message(chat_id, message)[source]
Processes a text message and returns the LLM’s response.
- Parameters:
chat_id (
int) – The ID of the chat.message (
str) – The text of the message to process.
- Return type:
BaseMessage- Returns:
The response message from the LLM.
- async answer_voice_message(chat_id, text, audio)[source]
Answer a voice message. :type chat_id:
int:param chat_id: Chat ID :type audio:str:param audio: Voice message audio
- async answer_webcontent(message_text, response_content)[source]
Answer a web content message. :type message_text:
str:param message_text: Text to answer :type response_content:str:param response_content: Response content :param chat_id: Chat ID- Return type:
str|None- Returns:
New response content if the call was successful, None otherwise
- bind_tools_on_init = True
- async call_sdapi(prompt)[source]
Call the StableDiffusion API. :type prompt:
str:param prompt: The prompt to send to the StableDiffusion API.- Return type:
dict|None- Returns:
The response from the StableDiffusion API.
- count_tokens(messages)[source]
Count the number of tokens in the messages using the LLM provider’s native method.
- Parameters:
messages (
list[BaseMessage]) – List of messages- Return type:
int- Returns:
Number of tokens
- async generate_feedback_message(prompt, max_length=200, chat_id=None)[source]
Generate a feedback message using the LLM.
- Parameters:
prompt (
str) – Prompt to generate the feedback messagemax_length (
int) – Maximum length of the feedback messagechat_id (
int|None) – Optional chat ID for metadata
- Return type:
str- Returns:
Generated feedback message
- async generate_image(prompt)[source]
Generate an image. :type prompt:
str:param prompt: Prompt to generate the image- Return type:
str|None- Returns:
Image representation in base64 format if the call was successful, None otherwise
- async initialize_async_resources()[source]
Initialize all async resources (MCP, etc.).
- Return type:
None
- async postprocess_response(response, message_text, chat_id)[source]
Postprocess the response from the LLM. :type response:
BaseMessage:param response: Response from the LLM :type message_text:str:param message_text: Text of the user message :type chat_id:int:param chat_id: Chat ID return: Final response data- Return type:
dict|None
- property system_instructions: list[langchain_core.messages.BaseMessage]
Get the system instructions mapping.
- async truncate_chat_context()[source]
Compact the chat context when it exceeds the token limit.
When summarization is enabled, the oldest messages are folded into an incremental LLM-generated summary that is persisted at the front of the message list and included in subsequent prompts. At most one summarization LLM call is made per invocation: the number of recent messages kept intact is reduced (down to a floor of 2) to the largest retention that can plausibly fit together with a summary capped at
summary_max_tokens, so a single pass has the best chance to bring the context under budget. Drop-oldest is only used as a last resort: when summarization is disabled, fails, or is exhausted.- Return type:
None
- class manolo_bot.ai.llmbot.LLMBuilder(llm_config)[source]
Bases:
objectFactory class for creating LangChain Chat Model instances.
- class manolo_bot.ai.llmagent.LLMAgent(llm, bot_config, system_instructions, messages_storage, tools=None, documents_storage=None, system_instructions_mapping=None)[source]
Bases:
LLMBotAdvanced Telegram LLM Chat Bot using a LangGraph-based agent.
This bot can use tools and dynamically integrate with MCP servers.
- async answer_document_message(chat_id, text, document_url, filename)[source]
Answer a document message using the agent.
- Parameters:
chat_id (
int) – Chat IDtext (
str) – Text to answerdocument_url (
str) – Document URLfilename (
str) – Original filename
- Return type:
BaseMessage- Returns:
Response
- async answer_image_message(chat_id, text, image)[source]
Answer an image message. :type chat_id:
int:param chat_id: Chat ID :type text:str:param text: Text to answer :type image:str:param image: Image to answer- Return type:
BaseMessage- Returns:
Response
- async answer_message(chat_id, message)[source]
Processes a text message and returns the LLM’s response.
- Parameters:
chat_id (
int) – The ID of the chat.message (
str) – The text of the message to process.
- Return type:
BaseMessage- Returns:
The response message from the LLM.
- async answer_voice_message(chat_id, text, audio)[source]
Answer a voice message. :type chat_id:
int:param chat_id: Chat ID :type text:str:param text: Text to answer :type audio:str:param audio: Audio to answer- Return type:
BaseMessage- Returns:
Response
- bind_tools_on_init = False
- class manolo_bot.ai.llmdeepagent.LLMDeepAgent(llm, bot_config, system_instructions, messages_storage, tools=None, documents_storage=None, system_instructions_mapping=None, backend=None, skills_paths=None, skills_backend=None, memory_paths=None, memory_backend=None, memory_add_cache_control=False)[source]
Bases:
LLMAgentAdvanced Telegram LLM Chat Bot using LangChain Deep Agents harness.
Extends LLMAgent with the full deep agents stack: - To-do list planning (TodoListMiddleware) - Virtual filesystem (FilesystemMiddleware + StateBackend) - Sub-agents (via create_deep_agent subagents parameter) - Skills (progressive disclosure via SkillsMiddleware) - Long-term memory (AGENTS.md files loaded into the system prompt via MemoryMiddleware)
The deep agent harness has its own internal system prompt (planning, filesystem, sub-agent instructions). The bot’s character/persona instructions from
system_instructionsare passed as thesystem_promptstring so they are merged with the harness prompt:bot_instructions + "\n\n" + harness_base_prompt.The virtual filesystem backend is injected from outside (see
main.instance_llm_bot()), keeping the class decoupled from chat-specific path logic. Skills follow the same explicit injection pattern:LLMDeepAgentdoes not instantiate a skills backend itself. Passskills_pathstogether with an explicitskills_backendwrapper (typically aSkillsFilesystemDeepAgentBackendconstructed once at module load and shared across every chat in the process). Ifskills_backendisNonetheSkillsMiddlewareis omitted entirely, even whenskills_pathsis set.Skills are operator-provided, not per-chat state, and are intentionally not cleared by
clean_context().When
skills_pathsormemory_pathsis configured, the agent’s main filesystem backend is wrapped with aCompositeBackendwhose routes come from the injected wrappers’routes()factories — each wrapper owns the storage-specific path→backend mapping (filesystem wrappers return sandboxedFilesystemBackendroutes rooted at each source; Redis/DB wrappers may map prefixes toStoreBackend-style backends or return{}). This lets the agent’s runtimeread_file/write_file/edit_file/lstools reach those files at their absolute paths — the per-chat backend’svirtual_mode=Trueroot would otherwise reject any path outside the chat workspace. For skills this is read-only access to capability bundles; for memory it also enables durable write-back: learnings the agent persists viaedit_fileland on the real memory file on disk.Long-term memory follows the same explicit-injection pattern as skills:
LLMDeepAgentdoes not instantiate a memory backend itself. Pass an explicitmemory_backendwrapper (typically a per-chatMemoryFilesystemDeepAgentBackendconstructed alongside the main backend inmain.instance_llm_bot()). Ifmemory_backendisNonetheMemoryMiddlewareis omitted entirely. The injected wrapper supplies the backend used to read the memory file, the chat-scoped source path (derived automatically whenmemory_pathsis not given), and the langgraph store forwarded tocreate_deep_agent(store=...); theMemoryMiddlewareitself is constructed by the agent, exactly likeSkillsMiddleware.memory_add_cache_controlis passed through toMemoryMiddleware(adds an Anthropic prompt-cache breakpoint on the memory block; no-op on other models).Memory is per-chat state: each chat gets its own independent, seeded
AGENTS.mdfile (no cross-chat leakage), andclean_context()clears it along with the chat workspace —/flushcontextwipes both. The agent writes new knowledge back to memory viaedit_file.Memory files are fully loaded into the system prompt on every turn, so keep them concise — unlike skills, they cost tokens on every message.
- bind_tools_on_init = False
MCP Manager for handling Model Context Protocol servers.
- class manolo_bot.ai.mcp_manager.MCPManager(config)[source]
Bases:
objectManages MCP server connections and tool loading.
- property is_connected: bool
Check if MCP is connected.
- class manolo_bot.ai.document_loaders.DocumentLoader[source]
Bases:
objectUtility class for extracting text from different document formats using LangChain native parsers.
- SUPPORTED_EXTENSIONS = ['pdf', 'docx', 'txt', 'md', 'csv']
- extract_text(file_content, filename)[source]
Dispatcher method to extract text based on file extension.
- Return type:
str
- extract_text_from_docx(file)[source]
Extracts text from a DOCX file using DocxParser.
- Return type:
str
- extract_text_from_pdf(file)[source]
Extracts text from a PDF file using PyPDFParser.
- Return type:
str
- extract_text_from_txt(file)[source]
Extracts text from a TXT/MD/CSV file using TextParser.
- Return type:
str
- classmethod validate_filename(filename)[source]
Validates if a filename has a supported extension.
- Parameters:
filename (
str) – The filename to validate.- Raises:
UnsupportedFileError – If the extension is not supported.
- Return type:
None
- class manolo_bot.ai.document_loaders.DocxParser(*args, **kwargs)[source]
Bases:
BaseBlobParserParser for DOCX files using python-docx. Follows the LangChain BaseBlobParser interface. We use this instead of MsWordParser to avoid the heavy ‘unstructured’ dependency.
Storage Components
- class manolo_bot.storage.deep_agent_backends.base.BaseDeepAgentBackend(bot_uuid, chat_id)[source]
Bases:
ABCAbstract base class for deep agent filesystem backends.
Handles the bot_uuid and chat_id scoping, delegating file operations to the underlying deepagents backend (StateBackend, FilesystemBackend, etc.).
- property backend: deepagents.backends.protocol.BackendProtocol
Returns the underlying deepagents backend instance.
- Returns:
The BackendProtocol instance used for file operations.
- class manolo_bot.storage.deep_agent_backends.base.BaseMemoryBackend[source]
Bases:
ABCAbstract base class for deep-agent memory backends.
Memory is per-chat, durable long-term knowledge (a single
AGENTS.mdfile per chat) loaded into the agent’s system prompt viaMemoryMiddleware. It is scoped by(bot_uuid, chat_id)exactly likeBaseDeepAgentBackend— each chat gets its own independent memory so no information leaks between chats. The agent writes new knowledge back to memory viaedit_file, so the file persists across sessions.Subclasses provide the per-chat backend instance; the constructor contract is
(bot_uuid, chat_id, ...)likeBaseDeepAgentBackend.clear()semantics are implementation-defined: the filesystem implementation deletes the chat’s memory directory (/flushcontextwipes chat memory along with the workspace).Library users may subclass this to provide custom storage (Redis, S3, in-memory, etc.) — same extension model as
BaseDeepAgentBackend.Note
routes()is a breaking addition to this ABC vs. the previously merged memory shape (issue #71) — acceptable, this is a project-internal library.- abstract property backend: deepagents.backends.protocol.BackendProtocol
The underlying
BackendProtocolinstance used byMemoryMiddleware.
- abstractmethod async clear()[source]
Clear the backend state.
Memory is per-chat state. The filesystem implementation deletes the chat’s memory directory; other implementations define their own semantics (e.g. dropping the chat’s memory keys).
- Return type:
None
- abstractmethod routes(sources)[source]
Return CompositeBackend route entries (prefix → BackendProtocol) so the agent’s runtime tools can reach memory files.
Keys are virtual path prefixes ending in
"/"; values are backends rooted at the routed location —CompositeBackendstrips the route prefix before delegating, so each value must resolve the stripped path inside its own root. Unlike skills, memory routes must support WRITE (durableedit_filewrite-back), not just read. Non-filesystem implementations may map prefixes toStoreBackend-style backends, or return{}if memory is served exclusively throughMemoryMiddleware.- Return type:
dict[str,BackendProtocol]
- class manolo_bot.storage.deep_agent_backends.base.BaseSkillsBackend[source]
Bases:
ABCAbstract base class for deep-agent skills backends.
Skills are operator-provided, versioned, auditable capability bundles described by
SKILL.mdfiles. They are global (shared across all chats and bot instances in the same process) and must never be cleared byLLMDeepAgent.clean_context().This class is intentionally not scoped by
(bot_uuid, chat_id)— it parallelsBaseDeepAgentBackendbut for the global (not per-chat) namespace. Subclasses provide a single backend instance that every chat shares.Library users may subclass this to provide custom storage (Redis, S3, in-memory, etc.) — same extension model as
BaseDeepAgentBackend.Note
routes()is a breaking addition to this ABC vs. the previously merged skills shape (issue #70) — acceptable, this is a project-internal library.- abstract property backend: deepagents.backends.protocol.BackendProtocol
The underlying
BackendProtocolinstance used bySkillsMiddleware.
- abstractmethod async clear()[source]
Clear the backend state.
Skills are operator-provided, not per-chat state. Implementations should typically be a no-op — wiping operator content when a user runs
/flushcontextwould be destructive and surprising.- Return type:
None
- abstractmethod routes(sources)[source]
Return CompositeBackend route entries (prefix → BackendProtocol) so the agent’s runtime tools can reach skill content.
Keys are virtual path prefixes ending in
"/"; values are backends rooted at the routed location —CompositeBackendstrips the route prefix before delegating, so each value must resolve the stripped path inside its own root. Non-filesystem implementations may map prefixes toStoreBackend-style backends, or return{}if skill content is served exclusively throughSkillsMiddleware.- Return type:
dict[str,BackendProtocol]
- class manolo_bot.storage.deep_agent_backends.memory_backend.MemoryDeepAgentBackend(bot_uuid, chat_id)[source]
Bases:
BaseDeepAgentBackendIn-memory implementation of the deep agent filesystem backend.
Uses a module-level dict of StateBackend instances keyed by (bot_uuid, chat_id), mirroring the pattern used by MemoryMessagesStorage while preventing cross-bot data leakage in multi-tenant deployments.
- class manolo_bot.storage.deep_agent_backends.filesystem_backend.FilesystemDeepAgentBackend(bot_uuid, chat_id, workspace_path, virtual_mode=True)[source]
Bases:
BaseDeepAgentBackendFilesystem-based implementation of the deep agent filesystem backend.
Builds a per-chat filesystem path from
bot_uuidandchat_id, mirroring the pattern used byFileDocumentsStorage.Security model — defense in depth, two layers:
Layer 1 — input validation at construction time (fail-fast).
bot_uuidis required to match[A-Za-z0-9_-]+(refuses/,.., control characters, etc.).workspace_pathis required to be an absolute path. A misconfigured operator gets aValueErrorat startup, before any agent can run.Layer 2 — containment check at use time (defense in depth).
_build_backend()andclear()resolve the constructedchat_pathand verify it is still underworkspace_path. This catches symlink-based escapes that pass Layer 1 (the workspace root itself contains a symlink pointing outside) and runtime mutations of_workspace_path.
Note:
workspace_pathshould be a directory only the bot process can read. The default (/tmp/manolo_bot/workspace) lives under the system temp directory, which on Linux is world-readable; on shared hosts setDEEP_AGENT_WORKSPACE_PATHto a private path (e.g.~/.local/share/manolo_bot/workspacewith mode0700).- property backend: deepagents.backends.filesystem.FilesystemBackend
Returns the underlying deepagents backend instance.
- Returns:
The BackendProtocol instance used for file operations.
Filesystem-based backend for the deep agent’s SkillsMiddleware.
Skills are operator-provided, versioned, auditable capability bundles described
by SKILL.md files. They are global (shared across all chats and bot
instances in the same process) and must never be cleared by clean_context
— clearing a filesystem-backed skills store would delete operator content
when a user runs /flushcontext.
Unlike FilesystemDeepAgentBackend, this class is intentionally
not scoped by (bot_uuid, chat_id). It wraps FilesystemBackend
with virtual_mode=False so the configured skill source paths
(DEEP_AGENT_SKILLS_PATHS) can be at any absolute location on the host
filesystem, not just inside a per-chat workspace subdirectory.
virtual_mode=False is safe here because this instance is handed exclusively
to SkillsMiddleware, which only calls ls() and download_files()
(read-only) at the configured source paths. No write/edit/delete is ever
invoked through this backend.
- class manolo_bot.storage.deep_agent_backends.skills_filesystem_backend.SkillsFilesystemDeepAgentBackend(workspace_path=None)[source]
Bases:
BaseSkillsBackendFilesystem-backed implementation of
BaseSkillsBackend.One instance is intended to be reused across every agent in the process:
manolo_bot.mainconstructs a single instance at module load and passes it to everyLLMDeepAgentit creates.- Parameters:
workspace_path (
str|None) – Optional root directory for relative skill source paths. Absolute paths inDEEP_AGENT_SKILLS_PATHSare unaffected. Defaults toNone(uses the process current working directory).
Example
from manolo_bot.storage.deep_agent_backends.skills_filesystem_backend import ( SkillsFilesystemDeepAgentBackend, ) # Shared — reuse across every chat in the process. skills_backend = SkillsFilesystemDeepAgentBackend() llm_bot = LLMDeepAgent( ..., skills_paths=["/etc/manolo_bot/skills", ("$HOME/.local/share/manolo_bot/skills", "User")], skills_backend=skills_backend, )
- property backend: deepagents.backends.filesystem.FilesystemBackend
The underlying
deepagents.backends.filesystem.FilesystemBackendinstance.
- async clear()[source]
No-op.
Skills are operator-provided, not per-chat state. They are intentionally not cleared by
LLMDeepAgent.clean_context()— wiping operator content when a user runs/flushcontextwould be destructive and surprising. If a caller really wants to drop skills, they can replace the singleton with a fresh instance.- Return type:
None
- routes(sources)[source]
Build CompositeBackend routes for the given skill source paths.
Each source (bare path or
(path, label)tuple) becomes a route prefix<path>/mapped to a sandboxedFilesystemBackendrooted at that source withvirtual_mode=True.CompositeBackendstrips the prefix before delegating, so the agent’s runtimeread_file/ls/glob/greptools reach skill files at their absolute paths — the per-chat backend’svirtual_mode=Trueroot would otherwise reject any path outside the chat workspace. Repeated prefixes are deduped.- Return type:
dict[str,FilesystemBackend]
Filesystem-based backend for the deep agent’s MemoryMiddleware.
Memory is per-chat, durable long-term knowledge (a single AGENTS.md file)
loaded into the agent’s system prompt via MemoryMiddleware. Each chat gets
its own independent memory directory under memory_root — no information
leaks between chats. The agent writes new knowledge back to memory via
edit_file, so the file persists across sessions.
Unlike the skills backend (global), this class is scoped by
(bot_uuid, chat_id) exactly like FilesystemDeepAgentBackend.
The wrapper exposes two distinct FilesystemBackend instances
because no single virtual_mode serves both consumers:
backend(virtual_mode=False) is handed toMemoryMiddleware, whose sources are RAW ABSOLUTE paths (<chat_memory_dir>/AGENTS.md). Withvirtual_mode=Truethose absolute paths would be appended under the root (<chat_memory_dir>/<chat_memory_dir>/AGENTS.md) and silently not found — hence the middleware-facing backend must bevirtual_mode=Falseso absolute paths pass through unchanged. Containment is guaranteed by construction (the root is derived from the validatedbot_uuid/chat_idvia the Layer-1/Layer-2 security model) and by the fact this backend is handed ONLY toMemoryMiddleware, which accesses exactly the one chat-scoped source the wrapper derives.routes()returns a separatevirtual_mode=Trueinstance for the agent’s mainCompositeBackend:_route_for_pathstrips the route prefix and hands the target a key WITH a leading slash ("/AGENTS.md"), whichvirtual_mode=Truecorrectly resolves under the chat root — avirtual_mode=Falsetarget would resolve it against the filesystem root (wrong and unsafe). This mirrors how the skills wrapper separates its middleware-facing backend from itsroutes()targets.
On construction the chat memory directory is created (if missing) and seeded
with a minimal NON-EMPTY AGENTS.md template. The template must be plain
markdown text — deepagents’ MemoryMiddleware._format_agent_memory skips
empty sources (HTML comments are stripped, so a comment-only file counts as
empty) and would render “(No memory loaded)” while hiding the source path
from the agent.
- class manolo_bot.storage.deep_agent_backends.memory_filesystem_backend.MemoryFilesystemDeepAgentBackend(bot_uuid, chat_id, memory_root, store=None)[source]
Bases:
BaseMemoryBackendFilesystem-backed, per-chat implementation of
BaseMemoryBackend.One instance is constructed per chat (typically inside
main.instance_llm_bot(), alongside the mainFilesystemDeepAgentBackend) and passed to that chat’sLLMDeepAgent. Memory is scoped by(bot_uuid, chat_id)so chats never share or leak memory.Security model — defense in depth, two layers (mirrors
FilesystemDeepAgentBackend):Layer 1 — input validation at construction time (fail-fast).
bot_uuidmust match[A-Za-z0-9_-]+;memory_rootmust be a non-empty absolute path. A misconfigured operator gets aValueErrorat startup, before any agent can run.Layer 2 — containment check at use time (defense in depth).
_safe_memory_path()resolves the chat memory dir and verifies it is still undermemory_root, catching symlink-based escapes that pass Layer 1.
- Parameters:
bot_uuid (str) – Bot identifier (validated against
[A-Za-z0-9_-]+).chat_id (int) – Chat identifier; scopes the memory directory.
memory_root (str) – Absolute root directory under which each chat’s memory lives at
memory_root/bot_uuid/chat_id.store (BaseStore | None) – Optional langgraph
BaseStoreforwarded tocreate_deep_agent(store=...). Defaults to a process-localInMemoryStorecreated at construction time — the default lives here, in the storage module, never insideLLMDeepAgent.
Example
from manolo_bot.storage.deep_agent_backends.memory_filesystem_backend import ( MemoryFilesystemDeepAgentBackend, ) # One instance per chat — pass it to that chat's agent. memory_backend = MemoryFilesystemDeepAgentBackend( bot_uuid="bot-1", chat_id=1001, memory_root="/var/lib/manolo_bot/memory" ) llm_bot = LLMDeepAgent( ..., memory_backend=memory_backend, )
- property backend: deepagents.backends.filesystem.FilesystemBackend
The middleware-facing
FilesystemBackend(virtual_mode=False).Handed to
MemoryMiddleware, whose sources are raw absolute paths that must pass through unchanged. Never exposed to agent tools; see the module docstring for the two-instance rationale.
- async clear()[source]
Delete the chat’s memory directory (destructive).
Memory is per-chat state —
/flushcontextwipes the chat’s memory along with its workspace. Layer 2 containment check runs first so a tampered_memory_rootor a symlink escape can never delete directories outside the configured root.- Return type:
None
- routes(sources)[source]
Build CompositeBackend routes for the chat memory file.
The chat memory directory becomes a single route prefix
<dir>/mapped to a SEPARATEFilesystemBackendwithvirtual_mode=True(distinct from the middleware-facingbackend):CompositeBackendstrips the route prefix and hands the target a leading-slash key ("/AGENTS.md"), whichvirtual_mode=Trueresolves under the chat root — avirtual_mode=Falsetarget would resolve it against the filesystem root (wrong and unsafe). This is what lets the agent’s runtimeread_file/write_file/edit_filetools reach the real seededAGENTS.mdon disk for durable write-back. Thesourcesargument is accepted for ABC compatibility; the route is always the chat’s own memory directory.- Return type:
dict[str,FilesystemBackend]
- property source: str
The chat-scoped memory file path loaded by
MemoryMiddleware.
- property store: langgraph.store.base.BaseStore
The langgraph
BaseStoreforwarded tocreate_deep_agent(store=...).This property is the one addition over
BaseSkillsBackend:MemoryMiddleware/create_deep_agentneed a langgraph store for persistent agent state, and skills did not — so the store is a storage concern owned by this wrapper, not byLLMDeepAgent.
- class manolo_bot.storage.messages.base.BaseMessagesStorage(bot_uuid, chat_id)[source]
Bases:
ABCAbstract base class for message storage.
Provides the interface for persisting and retrieving chat messages.
- abstractmethod async clear_messages()[source]
Clears all messages from the storage.
- Return type:
None
- abstractmethod async commit()[source]
Include new messages and remove deleted messages from the database asynchronously.
- Return type:
None
- delete_message(index)[source]
Deletes a message from the storage by index.
The index refers to the position among non-deleted messages (the same indexing used by
messages). Note that inserting a summary viaset_summary()shifts these indices, so callers should operate on fresh state.- Return type:
None
- get_summary()[source]
Returns the content of the persisted conversation summary, if any.
The summary is stored as a
SystemMessage(flagged withSUMMARY_PREFIX) at the front of the message list. This method scans non-deleted messages and returns the first one that carries the marker prefix, so it keeps working even if a backend reorders messages (e.g. after a refresh).- Return type:
str|None- Returns:
The summary text without the marker prefix, or None if absent.
- property messages: list[langchain_core.messages.BaseMessage]
Returns a list of non-deleted messages.
- abstractmethod async refresh_messages()[source]
Updates the messages list from the database asynchronously.
- Return type:
None
- set_summary(text)[source]
Replaces the conversation summary with the given text.
Any existing summary message is marked as deleted, then a new
SystemMessage(new=True) is inserted at the front of the message list, before the first non-deleted message, so it is the first message returned bymessages. Both Memory and Redis backends persist it transparently as a regular message.Note: inserting the summary shifts the non-deleted indices used by
delete_message(). Callers should always operate on fresh state (i.e. querymessages/get_summaryafter any insertion).- Parameters:
text (
str) – The summary text.- Return type:
None
- manolo_bot.storage.messages.base.SUMMARY_PREFIX = 'CONVERSATION SUMMARY: '
Marker prefix that distinguishes the auto-generated conversation summary (stored as a leading SystemMessage) from any other system message.
- class manolo_bot.storage.messages.base.StorageMessage(message, deleted=False, new=False)[source]
Bases:
object- deleted: bool = False
- message: langchain_core.messages.BaseMessage
- new: bool = False
- manolo_bot.storage.messages.base.convert_json_to_message(json_message)[source]
Converts a JSON string representation of a message into a BaseMessage instance.
- Return type:
BaseMessage
- manolo_bot.storage.messages.base.get_messages_key(bot_uuid, chat_id)[source]
Generates a key for storing messages in a database based on bot UUID and chat ID.
- Return type:
str
- class manolo_bot.storage.messages.memory_storage.MemoryMessagesStorage(bot_uuid, chat_id)[source]
Bases:
BaseMessagesStorageIn-memory implementation of message storage.
- async clear_messages()[source]
Clears all messages from the memory storage for the current chat.
- Return type:
None
- class manolo_bot.storage.messages.redis_storage.RedisDBHelper(db_url)[source]
Bases:
BaseDBHelperHelper class for Redis database operations.
- class manolo_bot.storage.messages.redis_storage.RedisMessagesStorage(db, bot_uuid, chat_id)[source]
Bases:
BaseMessagesStorageRedis-based implementation of message storage.
- async clear_messages()[source]
Clears all messages from the Redis database for the current chat.
- Return type:
None
- class manolo_bot.storage.documents.base.BaseDocumentStorage(bot_uuid)[source]
Bases:
ABCAbstract base class for document storage.
Provides the interface for persisting and retrieving extracted document text.
- abstractmethod async clear(chat_id)[source]
Clears all stored documents for a specific chat.
- Parameters:
chat_id (
int) – The ID of the chat.- Return type:
None
- abstractmethod async list_documents(chat_id)[source]
Lists all stored documents for a specific chat.
- Parameters:
chat_id (
int) – The ID of the chat.- Return type:
list[str]- Returns:
A list of filenames.
- class manolo_bot.storage.documents.file_storage.FileDocumentsStorage(bot_uuid, base_path=None)[source]
Bases:
BaseDocumentStorageFile-based implementation of document storage.
- async clear(chat_id)[source]
Clears all stored documents for a specific chat from the filesystem.
- Parameters:
chat_id (
int) – The ID of the chat.- Return type:
None
- async list_documents(chat_id)[source]
Lists all stored documents for a specific chat in the filesystem.
- Parameters:
chat_id (
int) – The ID of the chat.- Return type:
list[str]- Returns:
A list of filenames.
Configuration
- class manolo_bot.ai.config.BotConfig(bot_uuid, bot_name, bot_username, bot_token, user_id, agent_instructions=None, allowed_chat_ids=<factory>, bot_instructions='', bot_instructions_character='', bot_instructions_extra='', simulate_typing=True, simulate_typing_wpm=100, simulate_typing_max_time=10, use_tools=False, enable_mcp=False, mcp_servers_config=<factory>, context_max_tokens=4096, context_summarization=True, summary_max_tokens=512, summary_keep_messages=6, preferred_language='English', add_no_answer=False, is_image_multimodal=False, is_audio_multimodal=False, is_document_multimodal=False, is_group_assistant=False, agent_mode=False, web_content_request_timeout=10, max_document_size=2097152, max_voice_size=2097152, can_use_tavily_search=False, sdapi_url='', sdapi_params=<factory>, sdapi_negative_prompt='')[source]
Bases:
object- add_no_answer: bool = False
- agent_instructions: str | None = None
- agent_mode: bool = False
- allowed_chat_ids: list
- bot_instructions: str = ''
- bot_instructions_character: str = ''
- bot_instructions_extra: str = ''
- bot_name: str
- bot_token: str
- bot_username: str
- bot_uuid: str
- can_use_tavily_search: bool = False
- context_max_tokens: int = 4096
- context_summarization: bool = True
- enable_mcp: bool = False
- is_audio_multimodal: bool = False
- is_document_multimodal: bool = False
- is_group_assistant: bool = False
- is_image_multimodal: bool = False
- max_document_size: int = 2097152
- max_voice_size: int = 2097152
- mcp_servers_config: dict
- preferred_language: str = 'English'
- sdapi_negative_prompt: str = ''
- sdapi_params: dict
- sdapi_url: str = ''
- simulate_typing: bool = True
- simulate_typing_max_time: int = 10
- simulate_typing_wpm: int = 100
- summary_keep_messages: int = 6
- summary_max_tokens: int = 512
- use_tools: bool = False
- user_id: int
- web_content_request_timeout: int = 10
- class manolo_bot.ai.config.LLMConfig(google_api_key, google_api_model, openai_api_key, openai_api_model, openai_api_base_url, ollama_model, rate_limiter_requests_per_second=0.25, rate_limiter_check_every_n_seconds=0.1, rate_limiter_max_bucket_size=10)[source]
Bases:
object- google_api_key: str
- google_api_model: str
- ollama_model: str
- openai_api_base_url: str
- openai_api_key: str
- openai_api_model: str
- rate_limiter_check_every_n_seconds: float = 0.1
- rate_limiter_max_bucket_size: int = 10
- rate_limiter_requests_per_second: float = 0.25
- class manolo_bot.config.Config(lazy=False)[source]
Bases:
EnvModel- add_no_answer = <envmodel.fields.BooleanField object>
- agent_instructions = <envmodel.fields.StringField object>
- agent_mode = <envmodel.fields.BooleanField object>
- ai_mode = <envmodel.fields.StringField object>
- allow_private_chats = <envmodel.fields.BooleanField object>
- allowed_chat_ids = <envmodel.fields.StringListField object>
- bot_instructions = <envmodel.fields.StringField object>
- bot_instructions_character = <envmodel.fields.StringField object>
- bot_instructions_extra = <envmodel.fields.StringField object>
- bot_name = <envmodel.fields.StringField object>
- bot_token = <envmodel.fields.StringField object>
- bot_username = <envmodel.fields.StringField object>
- bot_uuid = <envmodel.fields.StringField object>
- context_max_tokens = <envmodel.fields.IntegerField object>
- deep_agent_backend = <envmodel.fields.StringField object>
- deep_agent_memory_add_cache_control = <envmodel.fields.BooleanField object>
- deep_agent_memory_path = <envmodel.fields.StringField object>
- deep_agent_skills_paths = <envmodel.fields.StringListField object>
- deep_agent_workspace_path = <envmodel.fields.StringField object>
- default_sdapi_params = {'cfg_scale': 1, 'height': 512, 'steps': 1, 'timestep_spacing': 'trailing', 'width': 512}
- document_storage_path = <envmodel.fields.StringField object>
- property effective_ai_mode: str
Resolve the effective AI mode, handling backward compatibility with AGENT_MODE.
- Precedence:
AI_MODEif set to a non-empty value"agent"whenAGENT_MODEis True (backward-compat)"agent"by default (documented default)
- enable_context_summarization = <envmodel.fields.BooleanField object>
- enable_mcp = <envmodel.fields.BooleanField object>
- google_api_key = <envmodel.fields.StringField object>
- google_api_model = <envmodel.fields.StringField object>
- is_audio_multimodal = <envmodel.fields.BooleanField object>
- is_document_multimodal = <envmodel.fields.BooleanField object>
- is_group_assistant = <envmodel.fields.BooleanField object>
- is_image_multimodal = <envmodel.fields.BooleanField object>
- logging_level = <envmodel.fields.StringField object>
- max_document_size = <envmodel.fields.IntegerField object>
- max_voice_size = <envmodel.fields.IntegerField object>
- mcp_servers_config = <envmodel.fields.JsonField object>
- ollama_model = <envmodel.fields.StringField object>
- openai_api_base_url = <envmodel.fields.StringField object>
- openai_api_key = <envmodel.fields.StringField object>
- openai_api_model = <envmodel.fields.StringField object>
- preferred_language = <envmodel.fields.StringField object>
- rate_limiter_check_every_n_seconds = <envmodel.fields.FloatField object>
- rate_limiter_max_bucket_size = <envmodel.fields.IntegerField object>
- rate_limiter_requests_per_second = <envmodel.fields.FloatField object>
- redis_url = <envmodel.fields.StringField object>
- sdapi_negative_prompt = <envmodel.fields.StringField object>
- sdapi_params = <envmodel.fields.JsonField object>
- sdapi_url = <envmodel.fields.StringField object>
- simulate_typing = <envmodel.fields.BooleanField object>
- simulate_typing_max_time = <envmodel.fields.IntegerField object>
- simulate_typing_wpm = <envmodel.fields.IntegerField object>
- storage_type = <envmodel.fields.StringField object>
- summary_keep_messages = <envmodel.fields.IntegerField object>
- summary_max_tokens = <envmodel.fields.IntegerField object>
- use_tavily_search = <envmodel.fields.BooleanField object>
- use_tools = <envmodel.fields.BooleanField object>
- user_id = <envmodel.fields.IntegerField object>
- web_content_request_timeout = <envmodel.fields.IntegerField object>