SKILL.md
Multimodal Embedding Serving — User
Run and call the embedding service. Run commands yourself and relay output. REST base URL: http://localhost:9777 (host port hardcoded by setup.sh; container 8000).
When to Use
- Deploy the embedding service (Docker Compose or standalone) and confirm health
- Embed text, images, or videos over REST on port 9777
- Choose or switch among the CLIP/SigLIP/MobileCLIP/CN-CLIP/Blip2/QwenText models
- Integrate embeddings in-process via the Python SDK wheel
- Diagnose 400/422 errors or model-capability mismatches
Example Prompts
Sample Problem-solving scenarios this skill handles end-to-end:
| Example | Problem it solves |
|---|---|
| [image-similarity-finder.md](./example-prompts/image-similarity-finder.md) | Find visually/semantically similar images in a local folder |
| [text-to-image-search.md](./example-prompts/text-to-image-search.md) | Search an image folder with natural-language queries ("Google Lens" for local media) |
| [image-duplicate-detector.md](./example-prompts/image-duplicate-detector.md) | Flag duplicate / near-duplicate images in a folder for cleanup, QC, and dataset deduplication |
Docs & deploy files — with or without a clone
All paths below are relative to microservices/multimodal-embedding-serving/ in the edge-ai-libraries repo. No clone? Fetch any of them from GitHub raw:
https://raw.githubusercontent.com/open-edge-platform/edge-ai-libraries/main/microservices/multimodal-embedding-serving/<path>
Load these existing docs only when needed:
| Resource | Load when… |
|---|---|
docs/user-guide/api-reference.md + docs/user-guide/api-docs/openapi.yaml |
building non-text payloads (image/video, base64, segment_config) or parsing responses/errors |
docs/user-guide/supported-models.md |
choosing or switching models (dimensions, modalities, language, size) |
docs/user-guide/sdk-usage.md + docs/user-guide/wheel-installation.md |
integrating in-process via the Python SDK wheel |
docs/user-guide/get-started.md |
more curl examples and env-var tables |
setup.sh, docker/compose.yaml |
the deploy artifacts used below |
1. Context routing — repo clone or standalone? REST or SDK?
- In-process Python integration wanted (no separate server): the service
doubles as an SDK — build the wheel with poetry build (needs the repo) and use getmodelhandler(...) + EmbeddingModel; see docs/user-guide/sdk-usage.md. Rule of thumb: default to REST; pick the SDK when a Python process embeds heavily and an HTTP hop per item would dominate. Note: MobileCLIP/Blip2 extras exist only in the Docker image, and the wheel is not on PyPI.
- Otherwise detect a clone:
``bash [ -f setup.sh ] && grep -q 'name = "multimodal-embedding-serving"' pyproject.toml 2>/dev/null \ && echo REPO || echo STANDALONE ` REPO → Step 2 from the microservice root. STANDALONE → fetch the two deploy files, then the exact same Step 2: `bash RAW=https://raw.githubusercontent.com/open-edge-platform/edge-ai-libraries/main/microservices/multimodal-embedding-serving mkdir -p embedding-serving/docker && cd embedding-serving curl -fsSL $RAW/setup.sh -o setup.sh curl -fsSL $RAW/docker/compose.yaml -o docker/compose.yaml ` Already running (curl -sf localhost:9777/health`) → Step 3.
2. Bring-up (identical in both contexts)
- Pick a model — default
CLIP/clip-vit-b-32(512-dim, text+image+video).
Trade-offs: docs/user-guide/supported-models.md.
setup.shmust be sourced and hard-fails without
EMBEDDINGMODELNAME. REGISTRYURL=intel selects the prebuilt image; --no-build prevents a source build. Run in the background — first start downloads the model: ``bash bash -c 'export EMBEDDINGMODELNAME="CLIP/clip-vit-b-32" REGISTRYURL=intel TAG=latest \ && source setup.sh && docker compose -f docker/compose.yaml up -d --no-build' ` Intel GPU: also export EMBEDDING_DEVICE=GPU` (setup.sh then auto-enables OpenVINO + THROUGHPUT mode).
- Wait for readiness:
``bash until curl -sf http://localhost:9777/health; do sleep 5; done ``
3. Capability check (mandatory before non-text inputs)
curl -s http://localhost:9777/model/capabilities
QwenText models are text-only — image/video requests return 400. GET /model/current shows the exact loaded model id to use in requests.
4. Embed
Text (single string or list of strings):
curl -s http://localhost:9777/embeddings -H 'Content-Type: application/json' -d '{
"model": "CLIP/clip-vit-b-32",
"input": {"type": "text", "text": "a red truck at a loading dock"},
"encoding_format": "float"
}'
Response: {"embedding": [...]} — a flat vector for text/image; a list of per-frame vectors for video inputs.
modelmust equal the loaded model (else 400).- Image:
{"type":"imageurl","imageurl":"https://…"}(plain string, not a
nested object) or imagebase64. Video: videourl/videobase64/ videoframes with segmentconfig (numframes default 64, extractionfps, frameindexes) — full shapes and examples: docs/user-guide/api-reference.md.
5. Stop / clean
docker compose -f docker/compose.yaml down- Volumes
ov-models(model caches) anddata-preppersist; removing them
forces re-downloads — confirm with the user first.
Troubleshooting
| Symptom | Likely cause → action |
|---|---|
source setup.sh prints ERROR and stops |
EMBEDDINGMODELNAME not exported → export it first |
| No response on 9777 | still starting/downloading → docker logs -f multimodal-embedding-serving |
| 400 "model mismatch" | request model ≠ loaded model → GET /model/current |
| 400 unsupported modality on image/video | text-only model (QwenText) → switch model or send text |
| First non-text request slow | lazy OpenVINO conversion/compile → expected once |
422 on /embeddings |
malformed input union → check shapes in docs/user-guide/api-reference.md |
| Port 9777 busy | stop the conflicting service (EMBEDDINGSERVERPORT is hardcoded by setup.sh) |