Metadata-Version: 2.4
Name: arduino_app_bricks
Version: 0.13.1
Summary: Arduino App Bricks for Apps Lab
Author-email: Marco Colombo <m.colombo@arduino.cc>, Roberto Gazia <r.gazia@arduino.cc>
License-Expression: MPL-2.0
Project-URL: Homepage, https://github.com/arduino/app-bricks-py
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.13
Description-Content-Type: text/markdown
License-File: LICENSE.txt
Requires-Dist: arduino-router-bridge>=0.5.0
Requires-Dist: pyyaml
Requires-Dist: requests
Requires-Dist: Pillow
Requires-Dist: msgpack
Provides-Extra: dev
Requires-Dist: setuptools; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-asyncio; extra == "dev"
Requires-Dist: websocket-client; extra == "dev"
Requires-Dist: ruff==0.14.2; extra == "dev"
Requires-Dist: docstring_parser>=0.16; extra == "dev"
Requires-Dist: arduino_app_bricks[all]; extra == "dev"
Requires-Dist: deepeval; extra == "dev"
Requires-Dist: mcp<2; extra == "dev"
Requires-Dist: langchain_mcp_adapters; extra == "dev"
Requires-Dist: langchain_ollama; extra == "dev"
Provides-Extra: dbstorage-influx
Requires-Dist: influxdb_client>=1.48.0; extra == "dbstorage-influx"
Provides-Extra: mqtt
Requires-Dist: paho_mqtt>=2.1.0; extra == "mqtt"
Provides-Extra: folderwatcher
Requires-Dist: watchdog>=6.0.0; extra == "folderwatcher"
Provides-Extra: camera
Requires-Dist: opencv-python-headless<4.14,>=4.13; extra == "camera"
Provides-Extra: microphone
Requires-Dist: pyalsaaudio==0.11.0; sys_platform == "linux" and extra == "microphone"
Requires-Dist: numpy; extra == "microphone"
Provides-Extra: web-ui
Requires-Dist: fastapi>=0.121.0; extra == "web-ui"
Requires-Dist: fastapi_socketio; extra == "web-ui"
Requires-Dist: uvicorn[standard]; extra == "web-ui"
Requires-Dist: cryptography; extra == "web-ui"
Provides-Extra: streamlit-ui
Provides-Extra: mood-detector
Requires-Dist: nltk; extra == "mood-detector"
Provides-Extra: pose-estimation
Requires-Dist: numpy; extra == "pose-estimation"
Requires-Dist: websockets; extra == "pose-estimation"
Provides-Extra: stream
Requires-Dist: websockets; extra == "stream"
Provides-Extra: telegram-bot
Requires-Dist: python-telegram-bot>=21.1; extra == "telegram-bot"
Provides-Extra: cloud-llm
Requires-Dist: langchain-core>=1.2.6; extra == "cloud-llm"
Requires-Dist: langchain-anthropic>=1.3.1; extra == "cloud-llm"
Requires-Dist: langchain-openai>=1.1.6; extra == "cloud-llm"
Requires-Dist: langchain-google-genai>=4.1.3; extra == "cloud-llm"
Provides-Extra: mcp-client
Requires-Dist: langchain-mcp-adapters==0.3.1; extra == "mcp-client"
Requires-Dist: mcp==1.29.0; extra == "mcp-client"
Provides-Extra: cloud-asr
Requires-Dist: websocket-client; extra == "cloud-asr"
Requires-Dist: google-cloud-speech>=2.27.0; extra == "cloud-asr"
Provides-Extra: all
Requires-Dist: arduino_app_bricks[dbstorage_influx]; extra == "all"
Requires-Dist: arduino_app_bricks[mqtt]; extra == "all"
Requires-Dist: arduino_app_bricks[folderwatcher]; extra == "all"
Requires-Dist: arduino_app_bricks[camera]; extra == "all"
Requires-Dist: arduino_app_bricks[microphone]; extra == "all"
Requires-Dist: arduino_app_bricks[web_ui]; extra == "all"
Requires-Dist: arduino_app_bricks[streamlit_ui]; extra == "all"
Requires-Dist: arduino_app_bricks[mood_detector]; extra == "all"
Requires-Dist: arduino_app_bricks[pose_estimation]; extra == "all"
Requires-Dist: arduino_app_bricks[stream]; extra == "all"
Requires-Dist: arduino_app_bricks[telegram_bot]; extra == "all"
Requires-Dist: arduino_app_bricks[cloud_llm]; extra == "all"
Requires-Dist: arduino_app_bricks[cloud_asr]; extra == "all"
Requires-Dist: arduino_app_bricks[mcp_client]; extra == "all"
Dynamic: license-file

# Arduino Apps Brick Library

The library is composed of configurable and reusable 'Bricks', based on optional infrastructure (executed via Docker Compose) and wrapping Python® code (to simplify code usage).

## What is a Brick?

A **Brick** is a modular, reusable building block that provides specific functionality for Arduino applications. Each Brick is self-contained with standardized configuration, consistent APIs, and optional Docker service definitions.

## Directory Structure

Every Brick must follow this standardized directory structure:

```
src/arduino/app_bricks/brick_name/
├── __init__.py                 # Required: Public API exports
├── brick_config.yaml          # Required: Brick metadata
├── brick_compose.yaml         # Optional: Docker services
├── README.md                  # Required: Documentation
├── [implementation_files.py]  # Brick logic
└── [assets]                   # Static resources
```

Brick usage examples live in the [app-bricks-examples](https://github.com/arduino/app-bricks-examples) repository, under the `bricks/` folder.

## Configuration variables

| Variable  | Description |
| ------------- | ------------- |
| APP_HOME  | Base application directory context  |
| LOCAL_DEV | To switch logic for local library development |
| APPSLAB_VERSION | To override the image versions referenced in brick_compose.yaml files |

## Library compile and build 

To build wheel file suitable for release, use following commands:
```sh
pip install build
python -m build .
```
To build package as snapshot for latest development build, use following build command:
```sh
pip install build
python -m build --config-setting "build_type=dev" .
```

## Library development steps
To start the development, clone the repository and create a virtual environment.

Install the Taskfile CLI tool: https://taskfile.dev/installation/.

Then, run the following command to set up the development environment:

```sh
task init
```

This task will check the python version and install the required dependencies.

To force a specific Arduino App Lab container version, use 'APPSLAB_VERSION' environment variable.

## Linting and formatting

To improve the development experience in VS Code, we recommend adding a `.vscode` folder to the repository root containing the following JSON files:

- `extensions.json`

```json
{
  "recommendations": [
    "charliermarsh.ruff",
    "github.vscode-pull-request-github",
    "ms-python.python",
    "tamasfe.even-better-toml"
  ],
  "unwantedRecommendations": [
    "ms-python.pylint"
  ]
}
```

- `settings.json`

```json
{
    // Set the Python interpreter to the virtual environment
    "python.defaultInterpreterPath": "${workspaceFolder}/.venv",
    "flake8.enabled": false,  // Disable flake8 since we use ruff
    "ruff.enable": true,
    "python.testing.pytestArgs": [
        "tests"
    ],
    "python.testing.unittestEnabled": false,
    "python.testing.pytestEnabled": true,

    // Linting and formatting settings on save
    "[python]": {
        // 1) use ruff as the default formatter
        "editor.defaultFormatter": "charliermarsh.ruff",
        
        // 2) automatically format the code on save
        // comment this setting if you don't want to automatically format your code on save
        "editor.formatOnSave": true,

        // 3) apply secure linter fixes on save
        // comment this setting if you don't want to automatically fix with the linter your code on save
        "editor.codeActionsOnSave": {
            "source.fixAll.ruff": "explicit",
        }
    }
}
```

After adding those files, VS Code will suggest installing the Python and Ruff extensions, which are properly configured for this project.

Alternatively, you can use the Ruff CLI to safely auto-fix linting issues and format your code by running:

```sh
task lint
```

```sh
task fmt
```

## Testing

All tests must be added in tests/ folder. To execute tests, run command:
```sh
task test
```

or, to execute specific tests, use:
```sh
task test:arduino/app_bricks
```

Modules can use LOCAL_DEV=true env variable to set development specific configurations.

For development purposes, it is possible to point to development containers (instead of the released ones) using two variables:
```sh
export DOCKER_REGISTRY_BASE=ghcr.io/<githubuser>/
export DOCKER_PYTHON_BASE_IMAGE=app-bricks/python-apps-base:dev-pose-classification
```
Development containers are published by the dev CI (`docker-build.yml`) tagged as `dev-<branch-name>` (e.g. branch `pose-classification` → tag `dev-pose-classification`).

## Pyright checks

Type checking is driven by `pyright-rules.json` at the repository root, shipped in the wheel as `arduino/app_bricks/static/pyright-rules.json` so that the same rules reach the CI of this repository, the CI of [app-bricks-examples](https://github.com/arduino/app-bricks-examples) and the App Lab editor. The library owns the rules, through two profiles: `app-bricks-py` for its own sources (strict, so the public API carries complete and truthful annotations) and `api-user` for code written against its API (standard, for the published examples and the apps edited in App Lab). The tools own the environment: paths, interpreter, execution root.

Two local checks, both needing the project venv with the current dependencies installed (`pip install -e ".[dev]"`; the checks refuse to run against an outdated environment) and, for the first, a clone of app-bricks-examples next to this repository:

```sh
task check:api      # the examples analyzed against this checkout (profile api-user), then the bricks without examples
task check:typing   # the library sources analyzed against themselves (profile app-bricks-py)
```

Extra arguments go to the underlying `run`/`typing` mode of `scripts/check_pyright.py` (custom paths, JSON output); see `python3 scripts/check_pyright.py --help` for the other modes, including the PR base/head `diff` the workflows use.

On pull requests the `check-pyright.yml` workflow runs both checks against the PR base and head. It is not a required status check, so it never blocks the merge: a library change may legitimately require a matching change in the examples, and blocking the two repositories on each other would deadlock. The job still ends red when the report does, i.e. when a check has new errors, as a visible signal on the PR; warnings leave it green. The report (new errors introduced by the PR, errors fixed, pre-existing ones collapsed) goes to the job summary and to a sticky comment on the PR, with a label while new errors exist. On PRs from forks the analysis job runs with a read-only token, so the comment is posted by `comment-pyright.yml`, which runs afterwards with a write token and never executes code from the PR. New API errors mean the change breaks the contract the published examples rely on: either adapt the change, or open the matching PR on app-bricks-examples and merge the library first.

## Release

Release is based on tags pushed to `main`. A single workflow (`docker-publish.yml`) handles all container
releases: **the tag prefix is the `containers/` sub-folder to release**.

| Tag | What it releases |
|---|---|
| `bricks/X.Y.Z` | everything in `containers/bricks/` (`python-apps-base`, `models-downloader`) + Python `.whl` uploaded to GitHub Release |
| `ai/X.Y.Z` | everything in `containers/ai/` (the model runners) |

Release cycles for AI containers and Bricks are independent — they use separate folders and tag prefixes,
and can be released at any time without affecting each other.

After releasing a new version of AI containers, compose files that use AI containers are updated automatically via a generated PR.

**Dependencies**: base images in `containers/base/` are not released on their own. Whatever a tagged
group depends on is rebuilt first, in dependency order, and tagged with the same version — releasing
`bricks/X.Y.Z` builds `python-slim` and `python-base` before `python-apps-base`. No manual step required.

For development, the dev build pipeline (`docker-build.yml`) is triggered manually (`workflow_dispatch`) on a branch and builds the selected containers (or all of them), tagging the images as `dev-<branch-name>`. Dependent containers are built in the correct order — downstream containers wait for their upstream to finish and use the freshly built image.

See [`.github/README.md`](.github/README.md) for full CI documentation.

### Container layers

Library containers are based on a set of pre-defined Python base images, in `containers/base/`, that are
updated with a different frequency wrt library release.
Base images are never released on their own: they are rebuilt as a dependency of whichever group is being
released, and tagged with that release version.

Base images are required to:
* reduce the amount of updated layers during a single library update
* promote reuse of existing layers in multiple builds
* cache pre-compiled python libraries as much as possible

Non-base images should start from common base images for performance and disk usage needs.

## License
See [LICENSE](./LICENSE.txt) file for details.

## Dependency licenses
`task license:deps` checks the licenses of the Python packages shipped by the library and by every container, using Docker. Records live under `.licenses/`, the allowed licenses and reviewed packages in `.licensed.yml`. See [scripts/licensed/README.md](scripts/licensed/README.md) for how it works and what to do when it fails.

## SBOM (Software Bill of Materials)
SBOMs are not kept in the tree. Each `bricks/X.Y.Z` release attaches `sboms.zip` to the GitHub Release, with one folder per distributed image holding three SPDX documents:

- `base.spdx.json` — packages of the base image the container derives `FROM` (declared as `sbom.runtime_base` in the container's `ci.json`)
- `full.spdx.json` — complete package list of the container image
- `delta.spdx.json` — packages added by the container on top of its base image

See [containers/README.md](containers/README.md#sboms) for how the set of images is resolved. To generate delta SBOMs locally, run:
```sh
task sbom:delta
```
optionally passing container names and the image tag to scan, e.g.:
```sh
task sbom:delta -- python-apps-base --version 1.0.0
```

**Note**: To run this task, you need `syft` installed and access to the container registry.
