pyproject.toml
Rama main · réplica de lectura del repositorio original.
[build-system]
build-backend = "hatchling.build"
requires = [ "hatch-vcs", "hatchling" ]
[project]
name = "mindroom"
description = "A universal interface for AI agents with persistent memory, where every conversation has a home"
readme = "README.md"
license = "Apache-2.0"
requires-python = ">=3.13,<3.15"
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
]
dynamic = [ "version" ]
dependencies = [
"agno==3.0.9",
"aiohttp>=3.13.3",
# Claude SDK 2 moves requests to API version 2026-10-21, a migration of its own.
"anthropic>=1.17,<2",
"anyio>=4.10",
"authlib>=1.6",
"certifi>=2026.4.22",
"cron-descriptor>=1.4.5",
"croniter>=6",
"cryptography>=47",
# Tool descriptions and parameter descriptions come from docstrings (tool_system/tool_schema.py).
"docstring-parser>=0.18",
"fastapi>=0.136.3",
"google-auth>=2.40.3",
"google-genai>=2",
"h11>=0.16",
"httpx>=0.27",
# 2.15.1 stops a long-lived client's TLS handshakes from growing slower on Linux.
"httpx2>=2.15.1",
"humanize>=4.12.3",
"idna>=3.13",
"jinja2>=3.1",
"json5>=0.13",
"jsonschema>=4.26",
"markdown-it-py>=4",
"mcp[cli]>=2.3,<3",
"mdit-py-plugins>=0.5",
# Owned to-device transport requires the durable NIO API.
"mindroom-nio[e2e]==1.3",
"ollama>=0.6.1",
# 3.30 returns a streamed Chat Completions connection to the pool.
"openai>=3.30",
"packaging>=24",
"pillow>=10.2",
"pydantic>=2.12.5",
"pydantic-settings>=2.10.1",
"pygments>=2.20",
"pyjwt>=2.13",
# Request sizing reads a PDF attachment's page count (token_counting/requests.py).
"pypdf>=6.21.4",
"python-dotenv>=1",
"python-multipart>=0.0.27",
"pyyaml>=6",
"questionary>=2.1.1",
"referencing>=0.37",
"rich",
"segno>=1.6.6",
"starlette>=1.3.1",
"structlog>=24.1",
"tiktoken>=0.12",
"typer>=0.24",
"uvicorn>=0.35",
"watchfiles>=1",
"websockets>=14",
]
[[project.authors]]
name = "mindroom team"
[project.optional-dependencies]
agent_manager = []
agentql = [
"agentql",
"playwright",
]
airflow = []
apify = [
"apify-client>=1.12.2; platform_machine!='aarch64'",
]
approved_egress = []
arxiv = [
"arxiv",
]
atlassian = []
attachments = []
aws_lambda = [
"boto3>=1.40.8",
]
aws_ses = [
"boto3>=1.40.8",
]
baidusearch = [
"baidusearch",
"pycountry",
]
aws_bedrock = [
"boto3>=1.40.8",
]
bitbucket = [
"requests",
]
brandfetch = [
"httpx>=0.27",
]
brightdata = [
"requests",
]
browser_mcp = []
browser = [
"playwright",
]
browserbase = [
"browserbase",
"playwright",
]
cal_com = [
"pytz",
"requests",
]
calculator = []
callback_manager = []
cartesia = [
"cartesia",
]
chat_ui = []
chromadb = [
"chromadb>=1.5.8",
]
claude_agent = [
"claude-agent-sdk>=0.2.140",
]
clickup = [
"requests",
]
coding = []
compact_context = []
composio = [
"composio-agno",
]
config_manager = [
"pydantic>=2",
"pyyaml>=6",
]
confluence = [
"atlassian-python-api",
]
contacts = []
crawl4ai = [
"crawl4ai>=0.7.3",
]
csv = [
"duckdb",
]
custom_api = [
"requests",
]
daytona = [
"daytona",
]
delegate = []
discord = [
"requests",
]
docker = [
"docker",
]
duckdb = [
"duckdb",
]
duckduckgo = [
"ddgs",
]
dynamic_tools = []
dynamic_workflow = []
report_publishing = []
e2b = [
"e2b-code-interpreter",
]
egress_credentials = []
eleven_labs = [
"elevenlabs",
]
email = []
exa = [
"exa-py",
]
external_trigger_manager = []
fal = [
"fal-client",
]
file = []
file_generation = [
"python-docx>=1.2",
"reportlab",
]
financial_datasets_api = [
"requests",
]
firecrawl = [
"firecrawl-py>=3",
]
gemini = [
"google-genai",
]
giphy = [
"httpx>=0.27",
]
github = [
# PyGithub 2.9 injects URL metadata into dictionary responses, breaking Agno's numeric language parser.
"pygithub>=2.5,<2.9",
]
gmail = [
"google-api-python-client>=2.178",
"google-auth>=2.40.3",
"google-auth-httplib2>=0.2",
"google-auth-oauthlib>=1.2.2",
]
google_bigquery = [
"google-api-python-client>=2.178",
"google-auth>=2.40.3",
"google-auth-httplib2>=0.2",
"google-cloud-bigquery>=3.41",
]
google_calendar = [
"google-api-python-client>=2.178",
"google-auth>=2.40.3",
"google-auth-httplib2>=0.2",
"google-auth-oauthlib>=1.2.2",
]
google_drive = [
"google-api-python-client>=2.178",
"google-auth>=2.40.3",
"google-auth-httplib2>=0.2",
"google-auth-oauthlib>=1.2.2",
]
google_docs = [
"google-api-python-client>=2.178",
"google-auth>=2.40.3",
"google-auth-httplib2>=0.2",
]
google_maps = [
"google-maps-places",
"googlemaps",
]
google_scholar = [
"scholarly",
]
google_sheets = [
"google-api-python-client>=2.178",
"google-auth-httplib2>=0.2",
"google-auth-oauthlib>=1.2.2",
]
google_tasks = [
"google-api-python-client>=2.178",
"google-auth>=2.40.3",
"google-auth-httplib2>=0.2",
]
googlesearch = [
"ddgs",
]
groq = [
"groq>=0.31",
]
hackernews = [
"httpx>=0.27",
]
homeassistant = [
"httpx>=0.27",
]
invite_router = []
jina = [
"httpx>=0.27",
"pydantic>=2",
]
jira = [
"jira>=3.10.5",
]
knowledge_graph = [
"graphiti-core[falkordblite]==0.31.1",
# C parser for FalkorDB replies; redis-py parses large ones in pure Python on the event loop otherwise.
"hiredis",
"psutil",
]
kubernetes = [
"kubernetes>=28.1",
]
linear = [
"requests",
]
linkup = [
"linkup-sdk>=0.2.8",
]
lumalabs = [
"lumaai",
]
matrix_api = []
matrix_calls = [
# 1.8.9 runs on OpenAI SDK 3 and pins the livekit 1.1.21 audio mixer the call tests cover.
"livekit-agents[openai]>=1.8.9",
]
matrix-e2ee = []
matrix_message = []
matrix_room = []
matrix_voice_message = []
mem0 = [
"mem0ai>=2.2.1",
]
memory = []
microsoft_files = []
microsoft_meetings = []
microsoft_onenote = []
microsoft_people = []
microsoft_sharepoint = []
microsoft_teams = []
microsoft_todo = []
modelslabs = [
"requests",
]
moviepy_video_tools = [
"moviepy",
]
neo4j = [
"neo4j",
]
newspaper = [
"lxml-html-clean",
"newspaper4k",
]
notion = [
"notion-client",
]
numpy = [
"numpy>=2",
]
oauth_connections = []
openai = [
"openai",
]
openbb = [
"openbb",
"polars",
"pyarrow",
]
openclaw_compat = []
openweather = [
"requests",
]
outlook_calendar = []
outlook_mail = []
oxylabs = [
"oxylabs",
]
pandas = [
"pandas>=2.2",
]
postgres = [
"psycopg[binary]",
]
pubmed = [
"httpx>=0.27",
]
python = []
react = []
reasoning = []
reddit = [
"praw",
]
redshift = [
"redshift-connector",
]
replicate = [
"replicate>=1.0.7",
]
resend = [
"resend",
]
scheduler = []
script = []
scrapegraph = [
"scrapegraph-py>=2.1",
]
searxng = []
self_config = [
"pydantic>=2",
"pyyaml>=6",
]
sentence-transformers = [
"sentence-transformers>=6.1",
"torchvision",
"transformers>=5.19",
]
serpapi = [
"google-search-results>=2.4.2",
]
serper = [
"requests",
]
shared_directories = []
shell = []
shopify = [
"httpx>=0.27",
]
skill_manage = []
slack = []
sleep = []
spider = [
"spider-client",
]
spotify = [
"httpx>=0.27",
]
sql = [
"sqlalchemy",
]
stop_reply = []
supabase = [
"supabase>=2.18.1",
]
tavily = [
"tavily-python",
]
telegram = [
"pytelegrambotapi>=4.32",
]
terminal = []
thread_model = []
thread_move = []
thread_resolution = []
thread_summary = []
thread_tags = []
todo = []
todoist = [
"todoist-api-python",
]
trafilatura = [
"trafilatura",
]
trello = [
"py-trello",
]
twilio = [
"twilio",
]
unsplash = []
update_awareness = []
usage_stats = []
visualization = [
"matplotlib",
]
web_browser_tools = []
webex = [
"webexpythonsdk",
]
website = [
"beautifulsoup4",
"httpx>=0.27",
]
whatsapp = [
"httpx>=0.27",
]
wikipedia = [
"wikipedia",
]
x = [
"tweepy",
]
yfinance = [
"yfinance",
]
youtube = [
"youtube-transcript-api",
]
zendesk = [
"requests",
]
zep = [
"zep-cloud>=3.3",
]
zoom = [
"requests",
]
desktop = [
"pillow>=10.2",
"pyautogui>=0.9.54",
"pyobjc-framework-applicationservices>=12.2.1; sys_platform=='darwin'",
"pyobjc-framework-cocoa>=12.2.1; sys_platform=='darwin'",
"pyobjc-framework-screencapturekit>=12.2.1; sys_platform=='darwin'",
]
[project.scripts]
mindroom-agent = "mindroom.agent_cli.main:main"
mindroom = "mindroom.cli.main:main"
[project.urls]
documentation = "https://github.com/mindroom-ai/mindroom"
homepage = "https://github.com/mindroom-ai/mindroom"
repository = "https://github.com/mindroom-ai/mindroom"
[dependency-groups]
dev = [
"aioresponses>=0.7.8",
"hypothesis>=6.161.1",
"markdown>=3.10.2",
"markdown-code-runner>=2.4",
"markdown-gfm-admonition",
"pre-commit>=4.2",
"pre-commit-uv>=4.1.4",
"privata>=0.8",
"pytest>=8.4.1",
"pytest-asyncio>=1.1",
"pytest-cov>=6.2.1",
"pytest-mock>=3.14.1",
"pytest-shm>=0.1.3",
"pytest-timeout>=2.4",
"pytest-xdist>=3.8",
"requests>=2.32.4",
"ruff>=0.12.5",
"tach>=0.34.1",
"ty>=0.0.17",
"vulture>=2.16",
"zensical",
]
# Only the frozen helper's dependencies; the backend's ML packages are not needed.
# uv resolves this group with the application's exact NIO pin in the same lockfile.
desktop-helper = [
"certifi",
"httpx>=0.27",
"mcp[cli]>=2.3,<3",
"mindroom-nio[e2e]",
"pillow>=10.2",
"pyautogui>=0.9.54",
"pyinstaller==6.16",
"pyobjc-framework-applicationservices>=12.2.1; sys_platform=='darwin'",
"pyobjc-framework-cocoa>=12.2.1; sys_platform=='darwin'",
"pyobjc-framework-screencapturekit>=12.2.1; sys_platform=='darwin'",
"python-dotenv>=1.1.1",
"pyyaml>=6.0.2",
"structlog>=25.4",
]
[tool.hatch.build.targets.wheel]
packages = [ "src/mindroom" ]
[tool.hatch.build.targets.wheel.force-include]
skills = "mindroom/_bundled_skills"
[tool.hatch.build.targets.wheel.hooks.custom]
[tool.hatch.version]
source = "vcs"
[tool.hatch.version.raw-options]
version_scheme = "no-guess-dev"
[tool.uv]
override-dependencies = [
# crawl4ai 0.8.6 declares lxml~=5.3, which caps lxml below 6.
# The locked lxml 5.4.0 has no cp314 Linux wheels.
# On Python 3.14 it source-builds against libxml2/libxslt, so use lxml 6.1+ there.
"lxml>=5.3,<6; python_version < '3.14'",
"lxml>=6.1.0; python_version >= '3.14'",
# composio-core 0.7.21 declares Pillow<11,>=10.2.0.
# The locked Pillow 10.4.0 has no cp314 Linux wheels.
# On Python 3.14 it source-builds against zlib, so use Pillow 11.x there.
"pillow>=10.2,<11; python_version < '3.14'",
"pillow>=11,<12; python_version >= '3.14'",
]
# Exact version pins for everything uv installs into isolated build environments, so release, desktop-helper,
# and image builds install only the build package versions this revision names.
# uv checks no file hashes here, so a file later added to a pinned release on PyPI can still be installed.
# uv build, uv sync (through uv.lock), and uv pip run with --project all read these.
# hatchling, hatch-vcs, and their requirements build this project; editable installs add editables.
# Locked dependencies that publish only sdists build with setuptools; pyscreeze adds wheel,
# and googlemaps adds requests and its requirements.
build-constraint-dependencies = [
"certifi==2026.7.22",
"charset-normalizer==3.5.2",
"editables==0.6",
"hatch-vcs==0.5.0",
"hatchling==1.32.4",
"idna==3.20",
"packaging==26.3",
"pathspec==1.1.1",
"pluggy==1.6.0",
"requests==2.34.2",
"setuptools-scm==10.3.4",
"setuptools==84.0.0",
"tomlkit==0.15.1",
"trove-classifiers==2026.9.21.13",
"urllib3==2.8.0",
"vcs-versioning==2.5.0",
"wheel==0.48.0",
]
[tool.ruff]
target-version = "py313"
line-length = 120
[tool.ruff.lint]
select = [
"ALL",
]
ignore = [
"ASYNC240", # Async pathlib guidance is too broad for this asyncio codebase/tests
"ASYNC250", # Blocking input in async manual tests is intentional
"B905", # `zip()` without an explicit `strict=` parameter
"BLE001", # Do not catch blind exception
"D203", # Incompatible with D211; prefer no blank line before class docstrings
"D107", # Missing docstring in `__init__`
"D213", # Incompatible with D212; prefer multi-line summaries on the first line
"D401", # First line of docstring should be in imperative mood
"D402", # First line should not be the function's signature
"E501", # Line too long
"FBT001", # Boolean-typed positional argument in function definition
"FBT002", # Boolean default value
"FBT003", # Boolean positional value in call
"FIX002", # Line contains TODO
"G004", # Logging statement uses f-string
"PLR0913", # Too many arguments to function call (N > 5)
"PLR2004", # Magic value used
"PLW0108", # Lambda warnings from dependency injection and test doubles are not actionable here
"PLW0603", # Using the global statement to update `X` is discouraged
"PYI034", # `Self` guidance is not useful in concrete test doubles
"S101", # Use of assert detected
"S603", # `subprocess` call: check for execution of untrusted input
"S607", # Starting a process with a partial executable path
"SLF001", # Private member accessed
"T20", # flake8-print
"TD002", # Missing author in TODO; try: `# TODO(<author_name>): ...`
"TD003", # Missing issue link on the line following this TODO
"UP042", # `StrEnum` migration is outside the scope of hook maintenance
"UP047", # PEP 695 type-parameter migration is outside the scope of hook maintenance
]
per-file-ignores.".claude/skills/*/scripts/*" = [
"INP001", # Part of an implicit namespace package (standalone skill scripts)
]
per-file-ignores.".github/scripts/*" = [
"INP001", # Part of an implicit namespace package (standalone scripts)
]
per-file-ignores."cluster/k8s/kind/*" = [
"INP001", # Part of an implicit namespace package (namespace package for python -m invocation)
]
per-file-ignores."tests/fixtures/*/*.py" = [
"INP001", # Example plugins under test fixtures load by path, never as packages
]
per-file-ignores."src/mindroom/tools/*" = [
"PLC0415", # `import` should be at the top-level of a file"
]
per-file-ignores."src/mindroom/tools/__init__.py" = [
"F401", # the package imports each registration only so it registers its tool
]
[tool.vulture]
paths = [ "src/mindroom", "vulture_whitelist.py" ]
ignore_decorators = [ "@register_tool_with_metadata" ]
min_confidence = 60
sort_by_size = true
[tool.ty.environment]
python-version = "3.13"
# Mock patterns cause many false positives in tests (e.g., mock.call_args
# returns _Call | None, attribute access on MagicMock, passing mocks as typed
# arguments).
[[tool.ty.overrides]]
include = [ "tests/" ]
rules = { call-non-callable = "ignore", invalid-argument-type = "ignore", invalid-assignment = "ignore", invalid-return-type = "ignore", no-matching-overload = "ignore", not-iterable = "ignore", not-subscriptable = "ignore", too-many-positional-arguments = "ignore", unresolved-attribute = "ignore", unsupported-operator = "ignore" }
# The macOS accessibility backend imports pyobjc, declared
# `sys_platform=='darwin'` and so genuinely absent everywhere else. Checking on
# another platform cannot resolve it and should not pretend to; the alternative
# is declaring the whole project macOS-targeted, which would hide real errors in
# the paths that are not. The imports are already function-level.
#
# `unused-ignore-comment` goes with it: the per-attribute ignores in those files
# are load-bearing on macOS, where pyobjc resolves and ty sees real types, and
# necessarily unused wherever the import does not resolve. One file cannot
# satisfy both platforms, so the warning is scoped off rather than the
# directives deleted.
[[tool.ty.overrides]]
include = [ "src/mindroom/desktop/" ]
rules = { unresolved-import = "ignore", unused-ignore-comment = "ignore" }
[tool.ty.rules]
deprecated = "ignore" # FastAPI on_event deprecation; refactor to lifespan separately
[tool.ty.src]
include = [ "src", "tests" ]
[tool.ty.terminal]
error-on-warning = true
[tool.pytest.ini_options]
testpaths = [ "tests" ]
python_files = "test_*.py"
asyncio_mode = "auto"
pythonpath = [ "src" ]
addopts = """
-p no:tach
-q
-n auto
--dist worksteal
--asyncio-mode=strict
--timeout 60
--durations 20
"""
markers = [
"e2e: marks end-to-end tests",
"slow: marks tests as slow",
]
# A full run peaks at about 3 GiB of temporary files on /dev/shm, mostly worker virtualenvs.
shm_min_free_gib = 4
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"raise NotImplementedError",
"if TYPE_CHECKING",
"if __name__ == .__main__.:",
]
exclude_also = [ '^\s*\.\.\.$' ]