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 |
Plugin interface contract, PluginRegistry, ModelManager, PluginVenv |
| testing-patterns.md |
Subprocess mocking, async fixtures, conftest patterns, parametrize |
| integration-patterns.md |
App-side architecture, request flow, polling, error handling, storage wiring |
Example Prompts
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 first, then use the
example prompts in this order:
- examples-prompts/plugin-blueprint.md for the reusable class skeleton
- examples-prompts/new-downloader-plugin.md for the end-to-end wiring
- examples-prompts/writing-tests.md for the unit-test shape
The minimum set of surfaces that must stay aligned is:
plugin_name in the class
- the key in
src/plugins/__init__.py
- the
ModelHub enum value in src/api/models.py
- the optional dependency extra in
pyproject.toml
- activation support in
docker/entrypoint.sh
Use the current tuple-based plugin registration format:
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 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:
- Check readiness with
GET /api/v1/health
- Submit work with
POST /api/v1/models/download?download_path=<subdir>
- Store the returned
job_id
- Poll
GET /api/v1/jobs/{job_id} until completed or failed
- 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 → "Job Lifecycle" section.
Quick diagnosis checklist:
# 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
1---2name: model-download-dev3description: 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".4---56# Model Download Developer Skill78Help developers extend, test, debug, and integrate the Model Download microservice.910> Codebase root: `microservices/model-download/`1112## When to Use1314- Adding a new download or conversion plugin15- Writing unit tests for a plugin (subprocess mocking, async fixtures)16- Debugging a job stuck in `downloading` or `converting`17- Understanding how `ModelManager`, `PluginRegistry`, or `PluginVenv` work18- Extending the `ModelHub` enum or `Config` schema19- Tracing plugin activation and `ACTIVATED_PLUGINS` env flow20- Integrating model-download into a backend, gateway, Compose stack, Helm deployment, or CI/CD path21- Designing app-side download/conversion workflows around `/models/download` and `/jobs/{job_id}`22- Wiring model storage, health checks, plugin activation, and failure handling into a wider system2324## Reference Lookup2526| Reference | When to read |27|-----------|-------------|28| [plugin-architecture.md](./references/plugin-architecture.md) | Plugin interface contract, PluginRegistry, ModelManager, PluginVenv |29| [testing-patterns.md](./references/testing-patterns.md) | Subprocess mocking, async fixtures, conftest patterns, parametrize |30| [integration-patterns.md](./references/integration-patterns.md) | App-side architecture, request flow, polling, error handling, storage wiring |3132## Example Prompts3334| File | Covers |35|------|--------|36| [examples-prompts/plugin-blueprint.md](./examples-prompts/plugin-blueprint.md) | Reusable skeleton for new downloader and converter plugins |37| [examples-prompts/new-downloader-plugin.md](./examples-prompts/new-downloader-plugin.md) | Wire a new downloader plugin end-to-end |38| [examples-prompts/writing-tests.md](./examples-prompts/writing-tests.md) | Unit test patterns for plugins with subprocess and async mocks |3940---4142## Plugin Architecture Summary4344```45src/46├── api/47│ ├── main.py ← FastAPI app, endpoints, job dispatch48│ └── models.py ← Pydantic models, ModelHub enum, ModelType, Config49├── core/50│ ├── interfaces.py ← ModelDownloadPlugin ABC (plugin_name, plugin_type, can_handle, download)51│ ├── model_manager.py ← Job lifecycle, ThreadPoolExecutor, status tracking52│ ├── plugin_registry.py ← Auto-discovery, activation check, find_plugin_for_model53│ └── plugin_venv.py ← Per-plugin venv management54└── plugins/55 ├── __init__.py ← PLUGINS tuple mapping — register module path + class name here56 ├── huggingface_plugin.py57 ├── ollama_plugin.py58 ├── openvino_plugin.py59 ├── ultralytics_plugin.py60 ├── geti_plugin.py61 ├── hls_plugin.py62 └── pipeline_zoo_models_plugin.py63```6465---6667## Procedure: Adding a New Plugin6869Read [plugin-architecture.md](./references/plugin-architecture.md) first, then use the70example prompts in this order:71721. [examples-prompts/plugin-blueprint.md](./examples-prompts/plugin-blueprint.md) for the reusable class skeleton732. [examples-prompts/new-downloader-plugin.md](./examples-prompts/new-downloader-plugin.md) for the end-to-end wiring743. [examples-prompts/writing-tests.md](./examples-prompts/writing-tests.md) for the unit-test shape7576The minimum set of surfaces that must stay aligned is:77781. `plugin_name` in the class792. the key in `src/plugins/__init__.py`803. the `ModelHub` enum value in `src/api/models.py`814. the optional dependency extra in `pyproject.toml`825. activation support in `docker/entrypoint.sh`8384Use the current tuple-based plugin registration format:8586```python87PLUGINS = {88 # ... existing entries ...89 "myhub": ("src.plugins.myhub_plugin", "MyHubPlugin"),90}91```9293Important runtime detail:9495- `ENABLED_PLUGINS` controls which modules are imported by `src/plugins/__init__.py`96- `ACTIVATED_PLUGINS` in `/opt/activated_plugins.env` is what `PluginRegistry` checks later9798If the plugin is implemented but does not appear in `/api/v1/plugins`, assume one of those99registration or activation surfaces is out of sync before you assume the core plugin logic is wrong.100101---102103## Procedure: Integrating into an Application or Platform104105Read [integration-patterns.md](./references/integration-patterns.md) first when the user is106embedding model-download into another service or deployment stack.107108Start by identifying the integration role:109110- **Provisioning service**: pre-download models during deployment or CI/CD111- **Runtime dependency**: app calls model-download on demand when a model is missing112- **Ops/admin service**: internal tooling triggers downloads and exposes status to operators113114Prefer the public REST API as the integration boundary:1151161. Check readiness with `GET /api/v1/health`1172. Submit work with `POST /api/v1/models/download?download_path=<subdir>`1183. Store the returned `job_id`1194. Poll `GET /api/v1/jobs/{job_id}` until `completed` or `failed`1205. Use the reported `download_path` or `conversion_path`121122Before proposing code or deployment changes, capture these decisions:123124| Concern | Decide |125|---------|--------|126| Trigger point | deploy time, app startup, first request, or admin action |127| Model source | huggingface, ollama, ultralytics, openvino, geti, pipeline-zoo-models, hls |128| Needed plugins | minimal `--plugins` list |129| Persistence | where `MODEL_PATH` lives and which services mount it |130| Completion model | synchronous wait in caller, async background job, or external orchestrator |131| Failure behavior | retry, fail startup, partial availability, or operator intervention |132133Expected integration outputs include one or more of:134135- an application architecture recommendation136- Docker Compose or Helm changes137- app-side client code for submit + poll + result handling138- env var, plugin, and storage/mount checklists139- a failure-handling and retry strategy140141Ground recommendations in the current API, deployment scripts, and plugin activation flow.142143---144145## Procedure: Debugging a Stuck Job146147Read [plugin-architecture.md](./references/plugin-architecture.md) → "Job Lifecycle" section.148149**Quick diagnosis checklist:**150151```bash152# 1. Check service logs for exceptions153docker logs model-download 2>&1 | tail -100154155# 2. Inspect the job status156curl -s http://localhost:8200/api/v1/jobs/<job-id>157158# 3. Verify the plugin was activated and discovered159curl -s http://localhost:8200/api/v1/plugins160161# 4. Test the plugin in isolation162python3 -c "163import asyncio164from src.plugins.myhub_plugin import MyHubPlugin165p = MyHubPlugin()166result = asyncio.run(p.download('my-model', '/tmp/test'))167print(result)168"169```170171Common causes of stuck jobs:172- Plugin raised an exception that was swallowed — check logs173- Plugin is blocking the event loop (use `asyncio.to_thread` for sync I/O)174- Lock held by a crashed previous job (Ollama `_ollama_download_lock`) — restart container175- Plugin was implemented but not activated — verify `docker/entrypoint.sh`, `ENABLED_PLUGINS`, and `ACTIVATED_PLUGINS`