# Copyright 2024 The HuggingFace Inc. team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project.urls]
homepage = "https://huggingface.co/lerobot"
documentation = "https://huggingface.co/docs/lerobot/index"
source = "https://github.com/huggingface/lerobot"
issues = "https://github.com/huggingface/lerobot/issues"
discord = "https://discord.gg/s3KuuzsPFb"

[project]
name = "lerobot"
version = "0.4.3"
description = "🤗 LeRobot: State-of-the-art Machine Learning for Real-World Robotics in Pytorch"
readme = "README.md"
license = { text = "Apache-2.0" }
requires-python = ">=3.10"
authors = [
    { name = "Rémi Cadène", email = "re.cadene@gmail.com" },
    { name = "Simon Alibert", email = "alibert.sim@gmail.com" },
    { name = "Alexander Soare", email = "alexander.soare159@gmail.com" },
    { name = "Quentin Gallouédec", email = "quentin.gallouedec@ec-lyon.fr" },
    { name = "Steven Palma", email = "imstevenpmwork@ieee.org" },
    { name = "Pepijn Kooijmans", email = "pepijnkooijmans@outlook.com"},
    { name = "Michel Aractingi", email = "michel.aractingi@gmail.com"},
    { name = "Adil Zouitine", email = "adilzouitinegm@gmail.com" },
    { name = "Dana Aubakirova", email = "danaaubakirova17@gmail.com"},
    { name = "Caroline Pascal", email = "caroline8.pascal@gmail.com"},
    { name = "Martino Russi", email = "nopyeps@gmail.com"},
    { name = "Thomas Wolf", email = "thomaswolfcontact@gmail.com" },
]
classifiers = [
    "Development Status :: 3 - Alpha",
    "Intended Audience :: Developers",
    "Intended Audience :: Education",
    "Intended Audience :: Science/Research",
    "License :: OSI Approved :: Apache Software License",
    "Programming Language :: Python :: 3.10",
    "Topic :: Software Development :: Build Tools",
    "Topic :: Scientific/Engineering :: Artificial Intelligence",
]
keywords = ["lerobot", "huggingface", "robotics",  "machine learning", "artificial intelligence"]

dependencies = [

    # Hugging Face dependencies
    "datasets>=4.0.0,<4.2.0",
    "diffusers>=0.27.2,<0.36.0",
    "huggingface-hub[hf-transfer,cli]>=0.34.2,<0.36.0",
    "accelerate>=1.10.0,<2.0.0",

    # Core dependencies
    "setuptools>=71.0.0,<81.0.0",
    "cmake>=3.29.0.1,<4.2.0",
    "einops>=0.8.0,<0.9.0",
    "opencv-python-headless>=4.9.0,<4.13.0",
    "av>=15.0.0,<16.0.0",
    "jsonlines>=4.0.0,<5.0.0",
    "packaging>=24.2,<26.0",
    "pynput>=1.7.7,<1.9.0",
    "pyserial>=3.5,<4.0",
    "wandb>=0.20.0,<0.22.0", # TODO: Bumb dependency (compatible with protobuf)

    "torch>=2.2.1,<2.8.0", # TODO: Bumb dependency
    "torchcodec>=0.2.1,<0.6.0; sys_platform != 'win32' and (sys_platform != 'linux' or (platform_machine != 'aarch64' and platform_machine != 'arm64' and platform_machine != 'armv7l')) and (sys_platform != 'darwin' or platform_machine != 'x86_64')", # TODO: Bumb dependency
    "torchvision>=0.21.0,<0.23.0", # TODO: Bumb dependency

    "draccus==0.10.0", # TODO: Remove ==
    "gymnasium>=1.1.1,<2.0.0",
    "rerun-sdk>=0.24.0,<0.27.0",

    # Support dependencies
    "deepdiff>=7.0.1,<9.0.0",
    "imageio[ffmpeg]>=2.34.0,<3.0.0",
    "termcolor>=2.4.0,<4.0.0",
]

# Optional dependencies
[project.optional-dependencies]

# Common
pygame-dep = ["pygame>=2.5.1,<2.7.0"]
placo-dep = ["placo>=0.9.6,<0.10.0"]
transformers-dep = ["transformers>=4.53.0,<5.0.0"]
grpcio-dep = ["grpcio==1.73.1", "protobuf==6.31.0"] # TODO: Bumb dependency (compatible with wandb)

# Motors
feetech = ["feetech-servo-sdk>=1.0.0,<2.0.0"]
dynamixel = ["dynamixel-sdk>=3.7.31,<3.9.0"]

# Robots
gamepad = ["lerobot[pygame-dep]", "hidapi>=0.14.0,<0.15.0"]
hopejr = ["lerobot[feetech]", "lerobot[pygame-dep]"]
lekiwi = ["lerobot[feetech]", "pyzmq>=26.2.1,<28.0.0"]
unitree_g1 = [
    "pyzmq>=26.2.1,<28.0.0",
    "onnxruntime>=1.16.0"
]
reachy2 = ["reachy2_sdk>=1.0.14,<1.1.0"]
kinematics = ["lerobot[placo-dep]"]
intelrealsense = [
    "pyrealsense2>=2.55.1.6486,<2.57.0 ; sys_platform != 'darwin'",
    "pyrealsense2-macosx>=2.54,<2.55.0 ; sys_platform == 'darwin'",
]
phone = ["hebi-py>=2.8.0,<2.12.0", "teleop>=0.1.0,<0.2.0", "fastapi<1.0"]

# Policies
pi = ["transformers @ git+https://github.com/huggingface/transformers.git@fix/lerobot_openpi"]
smolvla = ["lerobot[transformers-dep]", "num2words>=0.5.14,<0.6.0", "accelerate>=1.7.0,<2.0.0", "safetensors>=0.4.3,<1.0.0"]
groot = [
    "lerobot[transformers-dep]",
    "peft>=0.13.0,<1.0.0",
    "dm-tree>=0.1.8,<1.0.0",
    "timm>=1.0.0,<1.1.0",
    "safetensors>=0.4.3,<1.0.0",
    "Pillow>=10.0.0,<13.0.0",
    "decord>=0.6.0,<1.0.0; (platform_machine == 'AMD64' or platform_machine == 'x86_64')",
    "ninja>=1.11.1,<2.0.0",
    "flash-attn>=2.5.9,<3.0.0 ; sys_platform != 'darwin'"
]
hilserl = ["lerobot[transformers-dep]", "gym-hil>=0.1.13,<0.2.0", "lerobot[grpcio-dep]", "lerobot[placo-dep]"]

# Features
async = ["lerobot[grpcio-dep]", "matplotlib>=3.10.3,<4.0.0"]

# Development
dev = ["pre-commit>=3.7.0,<5.0.0", "debugpy>=1.8.1,<1.9.0", "lerobot[grpcio-dep]", "grpcio-tools==1.73.1"]
test = ["pytest>=8.1.0,<9.0.0", "pytest-timeout>=2.4.0,<3.0.0", "pytest-cov>=5.0.0,<8.0.0", "mock-serial>=0.0.1,<0.1.0 ; sys_platform != 'win32'"]
video_benchmark = ["scikit-image>=0.23.2,<0.26.0", "pandas>=2.2.2,<2.4.0"]

# Simulation
aloha = ["gym-aloha>=0.1.2,<0.2.0"]
pusht = ["gym-pusht>=0.1.5,<0.2.0", "pymunk>=6.6.0,<7.0.0"] # TODO: Fix pymunk version in gym-pusht instead
libero = ["lerobot[transformers-dep]", "hf-libero>=0.1.3,<0.2.0"]
metaworld = ["metaworld==3.0.0"]

# All
all = [
    "lerobot[dynamixel]",
    "lerobot[gamepad]",
    "lerobot[hopejr]",
    "lerobot[lekiwi]",
    "lerobot[reachy2]",
    "lerobot[kinematics]",
    "lerobot[intelrealsense]",
    "lerobot[pi]",
    "lerobot[smolvla]",
    # "lerobot[groot]", TODO(Steven): Gr00t requires specific installation instructions for flash-attn
    "lerobot[hilserl]",
    "lerobot[async]",
    "lerobot[dev]",
    "lerobot[test]",
    "lerobot[video_benchmark]",
    "lerobot[aloha]",
    "lerobot[pusht]",
    "lerobot[phone]",
    "lerobot[libero]",
    "lerobot[metaworld]",
]

[project.scripts]
lerobot-calibrate="lerobot.scripts.lerobot_calibrate:main"
lerobot-find-cameras="lerobot.scripts.lerobot_find_cameras:main"
lerobot-find-port="lerobot.scripts.lerobot_find_port:main"
lerobot-record="lerobot.scripts.lerobot_record:main"
lerobot-replay="lerobot.scripts.lerobot_replay:main"
lerobot-setup-motors="lerobot.scripts.lerobot_setup_motors:main"
lerobot-teleoperate="lerobot.scripts.lerobot_teleoperate:main"
lerobot-eval="lerobot.scripts.lerobot_eval:main"
lerobot-train="lerobot.scripts.lerobot_train:main"
lerobot-dataset-viz="lerobot.scripts.lerobot_dataset_viz:main"
lerobot-info="lerobot.scripts.lerobot_info:main"
lerobot-find-joint-limits="lerobot.scripts.lerobot_find_joint_limits:main"
lerobot-imgtransform-viz="lerobot.scripts.lerobot_imgtransform_viz:main"
lerobot-edit-dataset="lerobot.scripts.lerobot_edit_dataset:main"

# ---------------- Tool Configurations ----------------
[tool.setuptools.packages.find]
where = ["src"]

[tool.ruff]
target-version = "py310"
line-length = 110
exclude = ["tests/artifacts/**/*.safetensors", "*_pb2.py", "*_pb2_grpc.py"]

[tool.ruff.lint]
# E, W: pycodestyle errors and warnings
# F: PyFlakes
# I: isort
# UP: pyupgrade
# B: flake8-bugbear (good practices, potential bugs)
# C4: flake8-comprehensions (more concise comprehensions)
# A: flake8-builtins (shadowing builtins)
# SIM: flake8-simplify
# RUF: Ruff-specific rules
# D: pydocstyle (for docstring style/formatting)
# S: flake8-bandit (some security checks, complements Bandit)
# T20: flake8-print (discourage print statements in production code)
# N: pep8-naming
# TODO: Uncomment rules when ready to use
select = [
    "E", "W", "F", "I", "B", "C4", "T20", "N", "UP", "SIM" #, "A", "S", "D", "RUF"
]
ignore = [
    "E501", # Line too long
    "T201", # Print statement found
    "T203", # Pprint statement found
    "B008", # Perform function call in argument defaults
]

[tool.ruff.lint.per-file-ignores]
"__init__.py" = ["F401", "F403"]

[tool.ruff.lint.isort]
combine-as-imports = true
known-first-party = ["lerobot"]

[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.ruff.format]
quote-style = "double"
indent-style = "space"
skip-magic-trailing-comma = false
line-ending = "auto"
docstring-code-format = true

[tool.bandit]
exclude_dirs = [
    "tests",
    "benchmarks",
    "src/lerobot/datasets/push_dataset_to_hub",
]
skips = ["B101", "B311", "B404", "B603", "B615"]

[tool.typos]
default.extend-ignore-re = [
    "(?Rm)^.*(#|//)\\s*spellchecker:disable-line$",                      # spellchecker:disable-line
    "(?s)(#|//)\\s*spellchecker:off.*?\\n\\s*(#|//)\\s*spellchecker:on", # spellchecker:<on|off>
]
default.extend-ignore-identifiers-re = [
    # Add individual words here to ignore them
    "2nd",
    "pn",
    "ser",
    "ein",
    "thw",
    "inpt",
]

# TODO: Uncomment when ready to use
# [tool.interrogate]
# ignore-init-module = true
# ignore-init-method = true
# ignore-nested-functions = false
# ignore-magic = false
# ignore-semiprivate = false
# ignore-private = false
# ignore-property-decorators = false
# ignore-module = false
# ignore-setters = false
# fail-under = 80
# output-format = "term-missing"
# color = true
# paths = ["src/lerobot"]

# TODO: Enable mypy gradually module by module across multiple PRs
# Uncomment [tool.mypy] first, then uncomment individual module overrides as they get proper type annotations

[tool.mypy]
python_version = "3.10"
ignore_missing_imports = true
follow_imports = "skip"
# warn_return_any = true
# warn_unused_configs = true
# strict = true
# disallow_untyped_defs = true
# disallow_incomplete_defs = true
# check_untyped_defs = true

[[tool.mypy.overrides]]
module = "lerobot.*"
ignore_errors = true

[[tool.mypy.overrides]]
module = "lerobot.envs.*"
ignore_errors = false


# [[tool.mypy.overrides]]
# module = "lerobot.utils.*"
# ignore_errors = false

[[tool.mypy.overrides]]
module = "lerobot.configs.*"
ignore_errors = false

# extra strictness for configs
disallow_untyped_defs = true
disallow_incomplete_defs = true
check_untyped_defs = true

# [[tool.mypy.overrides]]
# module = "lerobot.optim.*"
# ignore_errors = false

[[tool.mypy.overrides]]
module = "lerobot.model.*"
ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.processor.*"
# ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.datasets.*"
# ignore_errors = false

[[tool.mypy.overrides]]
module = "lerobot.cameras.*"
ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.motors.*"
# ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.robots.*"
# ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.teleoperators.*"
# ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.policies.*"
# ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.rl.*"
# ignore_errors = false


# [[tool.mypy.overrides]]
# module = "lerobot.async_inference.*"
# ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.transport.*"
# ignore_errors = false

# [[tool.mypy.overrides]]
# module = "lerobot.scripts.*"
# ignore_errors = false
