|
| 1 | +# Debugging and Rebuilding Backends |
| 2 | + |
| 3 | +When a backend fails at runtime (e.g. a gRPC method error, a Python import error, or a dependency conflict), use this guide to diagnose, fix, and rebuild. |
| 4 | + |
| 5 | +## Architecture Overview |
| 6 | + |
| 7 | +- **Source directory**: `backend/python/<name>/` (or `backend/go/<name>/`, `backend/cpp/<name>/`) |
| 8 | +- **Installed directory**: `backends/<name>/` — this is what LocalAI actually runs. It is populated by `make backends/<name>` which builds a Docker image, exports it, and installs it via `local-ai backends install`. |
| 9 | +- **Virtual environment**: `backends/<name>/venv/` — the installed Python venv (for Python backends). The Python binary is at `backends/<name>/venv/bin/python`. |
| 10 | + |
| 11 | +Editing files in `backend/python/<name>/` does **not** affect the running backend until you rebuild with `make backends/<name>`. |
| 12 | + |
| 13 | +## Diagnosing Failures |
| 14 | + |
| 15 | +### 1. Check the logs |
| 16 | + |
| 17 | +Backend gRPC processes log to LocalAI's stdout/stderr. Look for lines tagged with the backend's model ID: |
| 18 | + |
| 19 | +``` |
| 20 | +GRPC stderr id="trl-finetune-127.0.0.1:37335" line="..." |
| 21 | +``` |
| 22 | + |
| 23 | +Common error patterns: |
| 24 | +- **"Method not implemented"** — the backend is missing a gRPC method that the Go side calls. The model loader (`pkg/model/initializers.go`) always calls `LoadModel` after `Health`; fine-tuning backends must implement it even as a no-op stub. |
| 25 | +- **Python import errors / `AttributeError`** — usually a dependency version mismatch (e.g. `pyarrow` removing `PyExtensionType`). |
| 26 | +- **"failed to load backend"** — the gRPC process crashed or never started. Check stderr lines for the traceback. |
| 27 | + |
| 28 | +### 2. Test the Python environment directly |
| 29 | + |
| 30 | +You can run the installed venv's Python to check imports without starting the full server: |
| 31 | + |
| 32 | +```bash |
| 33 | +backends/<name>/venv/bin/python -c "import datasets; print(datasets.__version__)" |
| 34 | +``` |
| 35 | + |
| 36 | +If `pip` is missing from the venv, bootstrap it: |
| 37 | + |
| 38 | +```bash |
| 39 | +backends/<name>/venv/bin/python -m ensurepip |
| 40 | +``` |
| 41 | + |
| 42 | +Then use `backends/<name>/venv/bin/python -m pip install ...` to test fixes in the installed venv before committing them to the source requirements. |
| 43 | + |
| 44 | +### 3. Check upstream dependency constraints |
| 45 | + |
| 46 | +When you hit a dependency conflict, check what the main library expects. For example, TRL's upstream `requirements.txt`: |
| 47 | + |
| 48 | +``` |
| 49 | +https://github.com/huggingface/trl/blob/main/requirements.txt |
| 50 | +``` |
| 51 | + |
| 52 | +Pin minimum versions in the backend's requirements files to match upstream. |
| 53 | + |
| 54 | +## Common Fixes |
| 55 | + |
| 56 | +### Missing gRPC methods |
| 57 | + |
| 58 | +If the Go side calls a method the backend doesn't implement (e.g. `LoadModel`), add a no-op stub in `backend.py`: |
| 59 | + |
| 60 | +```python |
| 61 | +def LoadModel(self, request, context): |
| 62 | + """No-op — actual loading happens elsewhere.""" |
| 63 | + return backend_pb2.Result(success=True, message="OK") |
| 64 | +``` |
| 65 | + |
| 66 | +The gRPC contract requires `LoadModel` to succeed for the model loader to return a usable client, even if the backend doesn't need upfront model loading. |
| 67 | + |
| 68 | +### Dependency version conflicts |
| 69 | + |
| 70 | +Python backends often break when a transitive dependency releases a breaking change (e.g. `pyarrow` removing `PyExtensionType`). Steps: |
| 71 | + |
| 72 | +1. Identify the broken import in the logs |
| 73 | +2. Test in the installed venv: `backends/<name>/venv/bin/python -c "import <module>"` |
| 74 | +3. Check upstream requirements for version constraints |
| 75 | +4. Update **all** requirements files in `backend/python/<name>/`: |
| 76 | + - `requirements.txt` — base deps (grpcio, protobuf) |
| 77 | + - `requirements-cpu.txt` — CPU-specific (includes PyTorch CPU index) |
| 78 | + - `requirements-cublas12.txt` — CUDA 12 |
| 79 | + - `requirements-cublas13.txt` — CUDA 13 |
| 80 | +5. Rebuild: `make backends/<name>` |
| 81 | + |
| 82 | +### PyTorch index conflicts (uv resolver) |
| 83 | + |
| 84 | +The Docker build uses `uv` for pip installs. When `--extra-index-url` points to the PyTorch wheel index, `uv` may refuse to fetch packages like `requests` from PyPI if it finds a different version on the PyTorch index first. Fix this by adding `--index-strategy=unsafe-first-match` to `install.sh`: |
| 85 | + |
| 86 | +```bash |
| 87 | +EXTRA_PIP_INSTALL_FLAGS+=" --upgrade --index-strategy=unsafe-first-match" |
| 88 | +installRequirements |
| 89 | +``` |
| 90 | + |
| 91 | +Most Python backends already do this — check `backend/python/transformers/install.sh` or similar for reference. |
| 92 | + |
| 93 | +## Rebuilding |
| 94 | + |
| 95 | +### Rebuild a single backend |
| 96 | + |
| 97 | +```bash |
| 98 | +make backends/<name> |
| 99 | +``` |
| 100 | + |
| 101 | +This runs the Docker build (`Dockerfile.python`), exports the image to `backend-images/<name>.tar`, and installs it into `backends/<name>/`. It also rebuilds the `local-ai` Go binary (without extra tags). |
| 102 | + |
| 103 | +**Important**: If you were previously running with `GO_TAGS=auth`, the `make backends/<name>` step will overwrite your binary without that tag. Rebuild the Go binary afterward: |
| 104 | + |
| 105 | +```bash |
| 106 | +GO_TAGS=auth make build |
| 107 | +``` |
| 108 | + |
| 109 | +### Rebuild and restart |
| 110 | + |
| 111 | +After rebuilding a backend, you must restart LocalAI for it to pick up the new backend files. The backend gRPC process is spawned on demand when the model is first loaded. |
| 112 | + |
| 113 | +```bash |
| 114 | +# Kill existing process |
| 115 | +kill <pid> |
| 116 | + |
| 117 | +# Restart |
| 118 | +./local-ai run --debug [your flags] |
| 119 | +``` |
| 120 | + |
| 121 | +### Quick iteration (skip Docker rebuild) |
| 122 | + |
| 123 | +For fast iteration on a Python backend's `backend.py` without a full Docker rebuild, you can edit the installed copy directly: |
| 124 | + |
| 125 | +```bash |
| 126 | +# Edit the installed copy |
| 127 | +vim backends/<name>/backend.py |
| 128 | + |
| 129 | +# Restart LocalAI to respawn the gRPC process |
| 130 | +``` |
| 131 | + |
| 132 | +This is useful for testing but **does not persist** — the next `make backends/<name>` will overwrite it. Always commit fixes to the source in `backend/python/<name>/`. |
| 133 | + |
| 134 | +## Verification |
| 135 | + |
| 136 | +After fixing and rebuilding: |
| 137 | + |
| 138 | +1. Start LocalAI and confirm the backend registers: look for `Registering backend name="<name>"` in the logs |
| 139 | +2. Trigger the operation that failed (e.g. start a fine-tuning job) |
| 140 | +3. Watch the GRPC stderr/stdout lines for the backend's model ID |
| 141 | +4. Confirm no errors in the traceback |
0 commit comments