# Model Download Dev

> Extend, test, debug, or integrate the Model Download microservice codebase. Use this skill when a developer wants to: add a new plugin to the microservice; write tests for a plugin (including mocking subprocess calls, async methods, or the Ollama server); debug a job stuck in "downloading" or "converting"; understand the plugin interface or registration mechanism; trace how a request flows through ModelManager; extend the OpenVINO conversion parameters; add a new ModelHub value; or embed model-download into an app, Docker Compose stack, Helm deployment, CI/CD flow, or startup path. Trigger on phrases like "add plugin", "write test", "stuck job", "extend microservice", "plugin not working", "how does model_manager work", "mock subprocess", "register new hub", "integrate model-download", "call the model-download API", "poll model job", or "mount downloaded models".

- Skill: `open-edge-platform/model-download-dev` (Agent Skill, multi-file: 8 files)
- Install (CLI): `npx skillmds@latest add open-edge-platform/model-download-dev`
- Raw SKILL.md: https://api.skillmd.com/api/skills/open-edge-platform/model-download-dev/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: open-edge-platform (https://skillmd.com/u/open-edge-platform)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/open-edge-platform/model-download-dev

---


# Model Download Developer Skill

Help developers extend, test, debug, and integrate the Model Download microservice.

> Codebase root: `microservices/model-download/`

## When to Use

- Adding a new download or conversion plugin
- Writing unit tests for a plugin (subprocess mocking, async fixtures)
- Debugging a job stuck in `downloading` or `converting`
- Understanding how `ModelManager`, `PluginRegistry`, or `PluginVenv` work
- Extending the `ModelHub` enum or `Config` schema
- Tracing plugin activation and `ACTIVATED_PLUGINS` env flow
- Integrating model-download into a backend, gateway, Compose stack, Helm deployment, or CI/CD path
- Designing app-side download/conversion workflows around `/models/download` and `/jobs/{job_id}`
- Wiring model storage, health checks, plugin activation, and failure handling into a wider system

## Reference Lookup

| Reference | When to read |
|-----------|-------------|
| [plugin-architecture.md](./references/plugin-architecture.md) | Plugin interface contract, PluginRegistry, ModelManager, PluginVenv |
| [testing-patterns.md](./references/testing-patterns.md) | Subprocess mocking, async fixtures, conftest patterns, parametrize |
| [integration-patterns.md](./references/integration-patterns.md) | App-side architecture, request flow, polling, error handling, storage wiring |

## Example Prompts

| File | Covers |
|------|--------|
| [examples-prompts/plugin-blueprint.md](./examples-prompts/plugin-blueprint.md) | Reusable skeleton for new downloader and converter plugins |
| [examples-prompts/new-downloader-plugin.md](./examples-prompts/new-downloader-plugin.md) | Wire a new downloader plugin end-to-end |
| [examples-prompts/writing-tests.md](./examples-prompts/writing-tests.md) | Unit test patterns for plugins with subprocess and async mocks |

---

## Plugin Architecture Summary

```
src/
├── api/
│   ├── main.py          ← FastAPI app, endpoints, job dispatch
│   └── models.py        ← Pydantic models, ModelHub enum, ModelType, Config
├── core/
│   ├── interfaces.py    ← ModelDownloadPlugin ABC (plugin_name, plugin_type, can_handle, download)
│   ├── model_manager.py ← Job lifecycle, ThreadPoolExecutor, status tracking
│   ├── plugin_registry.py ← Auto-discovery, activation check, find_plugin_for_model
│   └── plugin_venv.py   ← Per-plugin venv management
└── plugins/
    ├── __init__.py      ← PLUGINS tuple mapping — register module path + class name here
    ├── huggingface_plugin.py
    ├── ollama_plugin.py
    ├── openvino_plugin.py
    ├── ultralytics_plugin.py
    ├── geti_plugin.py
    ├── hls_plugin.py
    └── pipeline_zoo_models_plugin.py
```

---

## Procedure: Adding a New Plugin

Read [plugin-architecture.md](./references/plugin-architecture.md) first, then use the
example prompts in this order:

1. [examples-prompts/plugin-blueprint.md](./examples-prompts/plugin-blueprint.md) for the reusable class skeleton
2. [examples-prompts/new-downloader-plugin.md](./examples-prompts/new-downloader-plugin.md) for the end-to-end wiring
3. [examples-prompts/writing-tests.md](./examples-prompts/writing-tests.md) for the unit-test shape

The minimum set of surfaces that must stay aligned is:

1. `plugin_name` in the class
2. the key in `src/plugins/__init__.py`
3. the `ModelHub` enum value in `src/api/models.py`
4. the optional dependency extra in `pyproject.toml`
5. activation support in `docker/entrypoint.sh`

Use the current tuple-based plugin registration format:

```python
PLUGINS = {
    # ... existing entries ...
    "myhub": ("src.plugins.myhub_plugin", "MyHubPlugin"),
}
```

Important runtime detail:

- `ENABLED_PLUGINS` controls which modules are imported by `src/plugins/__init__.py`
- `ACTIVATED_PLUGINS` in `/opt/activated_plugins.env` is what `PluginRegistry` checks later

If the plugin is implemented but does not appear in `/api/v1/plugins`, assume one of those
registration or activation surfaces is out of sync before you assume the core plugin logic is wrong.

---

## Procedure: Integrating into an Application or Platform

Read [integration-patterns.md](./references/integration-patterns.md) first when the user is
embedding model-download into another service or deployment stack.

Start by identifying the integration role:

- **Provisioning service**: pre-download models during deployment or CI/CD
- **Runtime dependency**: app calls model-download on demand when a model is missing
- **Ops/admin service**: internal tooling triggers downloads and exposes status to operators

Prefer the public REST API as the integration boundary:

1. Check readiness with `GET /api/v1/health`
2. Submit work with `POST /api/v1/models/download?download_path=<subdir>`
3. Store the returned `job_id`
4. Poll `GET /api/v1/jobs/{job_id}` until `completed` or `failed`
5. Use the reported `download_path` or `conversion_path`

Before proposing code or deployment changes, capture these decisions:

| Concern | Decide |
|---------|--------|
| Trigger point | deploy time, app startup, first request, or admin action |
| Model source | huggingface, ollama, ultralytics, openvino, geti, pipeline-zoo-models, hls |
| Needed plugins | minimal `--plugins` list |
| Persistence | where `MODEL_PATH` lives and which services mount it |
| Completion model | synchronous wait in caller, async background job, or external orchestrator |
| Failure behavior | retry, fail startup, partial availability, or operator intervention |

Expected integration outputs include one or more of:

- an application architecture recommendation
- Docker Compose or Helm changes
- app-side client code for submit + poll + result handling
- env var, plugin, and storage/mount checklists
- a failure-handling and retry strategy

Ground recommendations in the current API, deployment scripts, and plugin activation flow.

---

## Procedure: Debugging a Stuck Job

Read [plugin-architecture.md](./references/plugin-architecture.md) → "Job Lifecycle" section.

**Quick diagnosis checklist:**

```bash
# 1. Check service logs for exceptions
docker logs model-download 2>&1 | tail -100

# 2. Inspect the job status
curl -s http://localhost:8200/api/v1/jobs/<job-id>

# 3. Verify the plugin was activated and discovered
curl -s http://localhost:8200/api/v1/plugins

# 4. Test the plugin in isolation
python3 -c "
import asyncio
from src.plugins.myhub_plugin import MyHubPlugin
p = MyHubPlugin()
result = asyncio.run(p.download('my-model', '/tmp/test'))
print(result)
"
```

Common causes of stuck jobs:
- Plugin raised an exception that was swallowed — check logs
- Plugin is blocking the event loop (use `asyncio.to_thread` for sync I/O)
- Lock held by a crashed previous job (Ollama `_ollama_download_lock`) — restart container
- Plugin was implemented but not activated — verify `docker/entrypoint.sh`, `ENABLED_PLUGINS`, and `ACTIVATED_PLUGINS`

