- name
- model-download-dev
- description
- 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".
- argument-hint
- Describe what you want to build or debug (e.g. "add a new downloader plugin for an internal model hub" or "wire model-download into our compose stack")
# 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]
>
> - `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`
GitHub에서 보기