بنقرة واحدة
ai-services
Hailo-optimized AI service patterns and best practices
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
Hailo-optimized AI service patterns and best practices
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
| name | ai-services |
| description | Hailo-optimized AI service patterns and best practices |
This skill provides pragmatic patterns for AI service APIs that leverage Hailo-10H on Raspberry Pi 5.
Adopt existing standards: Don't invent new APIs. Use proven protocols and conventions:
GET /health or /api/healthBenefit: Works with existing client tools, familiar to users, reduces code to write and maintain.
These are personal projects and art installations—reuse over reinvention.
For Python-based services: Use isolated virtual environments in /opt/hailo-service-name/venv (see Raspberry Pi skill for detailed rationale and alternatives).
Why it matters for AI services:
/opt/hailo-service-name/ directoryhailo-ollama exception: Wraps Ollama binary (not Python); no venv needed.
The hailo-apps submodule provides 20+ AI applications across vision and GenAI domains. Each can be wrapped as a systemd service.
Architecture Note: Hailo-10H supports concurrent services — multiple models can run simultaneously. Design services to:
Purpose: Expose LLM inference via Ollama API as a systemd service
Pattern:
/api/tags, /api/chat, /api/generate)Available applications ready for service deployment:
Object Detection:
/api/detect endpoint with image uploadPose Estimation:
/api/pose endpoint returning joint coordinatesInstance Segmentation:
/api/segment with mask outputDepth Estimation:
/api/depth returning depth mapsOCR:
/api/ocr following Tesseract conventionsFace Recognition:
/api/faces with recognition resultsOther Vision:
curl -X POST http://localhost:8080/api/detect \
-F "image=@photo.jpg" \
-H "Content-Type: multipart/form-data"
# Returns COCO-format bboxes: [{"label": "person", "confidence": 0.95, "bbox": [x, y, w, h]}]
Pose Estimation services:
curl -X POST http://localhost:8081/api/pose \
-F "image=@person.jpg"
# Returns keypoints: [{"person_id": 0, "keypoints": [{"name": "nose", "x": 120, "y": 80}...]}]
Speech-to-Text services (Whisper API format):
curl -X POST http://localhost:8082/api/transcribe \
-F "audio=@speech.wav" \
-F "language=en"
# Returns: {"text": "Hello world", "language": "en", "duration": 2.5}
Text-to-Speech services (Piper TTS):
curl -X POST http://localhost:8083/api/speak \
-H "Content-Type: application/json" \
-d '{"text": "Hello world", "voice": "en_US-amy-low"}' \
--output speech.wav
# Returns audio file (WAV format)
General principles for all services:
/api/health, /health/api/transcribe with audio uploadVoice Assistant:
/api/voice streaming endpointVision-Language Models:
/api/vlm with image + promptFor each AI capability:
Purpose: Queue-based inference for non-interactive workloads
Characteristics:
Ollama-based services (LLM):
curl http://localhost:11434/api/tags # Standard Ollama endpoint
Object Detection services:
curl -X POST http://localhost:8080/api/detect \
-F "image=@photo.jpg" \
-H "Content-Type: multipart/form-data"
# Returns COCO-format bboxes: [{"label": "person", "confidence": 0.95, "bbox": [x, y, w, h]}]
Pose Estimation services:
curl -X POST http://localhost:8081/api/pose \
-F "image=@person.jpg"
# Returns keypoints: [{"person_id": 0, "keypoints": [{"name": "nose", "x": 120, "y": 80}...]}]
Speech-to-Text services (Whisper API format):
curl -X POST http://localhost:8082/api/transcribe \
-F "audio=@speech.wav" \
-F "language=en"
# Returns: {"text": "Hello world", "language": "en", "duration": 2.5}
Text-to-Speech services (Piper TTS):
curl -X POST http://localhost:8083/api/speak \
-H "Content-Type: application/json" \
-d '{"text": "Hello world", "voice": "en_US-amy-low"}' \
--output speech.wav
# Returns audio file (WAV format)
General principles for all services:
/api/health, /health)Keep it simple:
{
"error": "Model not found",
"detail": "Available models: neural-chat, mistral"
}
No need for custom error codes if standard HTTP status codes work.
For long-running inference:
POST /api/infer
Body: {"model": "llama2", "prompt": "..."}
Response: {"job_id": "abc123", "status": "queued"}
GET /api/infer/abc123
Response: {"status": "complete", "result": "..."}
Persistent Loading (Preferred):
Graceful Unloading (When Needed):
/api/unload endpoint to release model memoryConcurrent Services:
# Default model storage
~hailo/.ollama/models/ # User home
/var/lib/ollama/models/ # System service
# Model structure
/var/lib/ollama/models/manifests/<namespace>/<model>:<tag>
/var/lib/ollama/models/blobs/ # Actual model files
Recommended pattern for persistent services:
# Service startup: Load model into memory (happens once)
# - Model remains resident until service stop or explicit unload
# - First inference after startup: ~10-30s (model loading)
# - Subsequent inferences: <1s (model already loaded)
# List loaded models
curl http://localhost:11434/api/tags
# Returns: {"models": [{"name": "llama2", "size": "4.1GB", "loaded": true}]}
# Inference (uses already-loaded model)
curl -X POST http://localhost:11434/api/chat -d '{"model": "llama2", "messages": [...]}'
# Fast response (model already in memory)
# Graceful unload (optional, for memory management)
curl -X POST http://localhost:11434/api/unload -d '{"model": "llama2"}'
# Releases memory; next inference will reload (slow)
# Health check includes model status
curl http://localhost:11434/api/health
# Returns: {"status": "ok", "models_loaded": ["llama2"], "memory_used_mb": 4200}
Design principle: Services should keep models loaded by default. Only unload when:
/api/unload endpointRecommended models for single-service deployment:
neural-chat (7B, ~4GB) - Good for dedicated LLM servicemistral (7B, ~4GB) - Faster inference, good general useorca-mini (3B, ~2GB) - Lowest memory, good for multi-service setupsFor concurrent multi-service scenarios:
orca-mini (2GB) + Whisper-tiny (512MB) + YOLO (512MB) = ~3GB totalfree -h and /api/health endpointsAvoid:
Total Pi 5 RAM: 8 GB
Reserved for OS: 1 GB
Available for services: ~7 GB
Ollama process overhead: ~300 MB
Model cache (llama2 7B): ~5 GB
Inference workspace: ~1 GB buffer
Action: Set MemoryLimit=6G in systemd unit to prevent swap thrashing.
Hailo-10 offloads: Tensor operations (inference)
CPU still handles: Tokenization, sampling, post-processing
Typical CPU load: 30-50% during active inference
Thermal idle: 35-45°C (with passive cooling)
Thermal active use: 50-65°C (with active fan)
Action: Monitor CPU usage in journalctl logs; if sustained >80%, reduce batch size or model size.
# Monitor service + system temperature
while true; do
echo "$(date): $(vcgencmd measure_temp)"
sleep 5
done
# Log thermal events to journalctl
# Modify service unit:
# ExecStartPost=/usr/local/bin/log-thermal-baseline.sh
Throttle recovery:
hailortcli fw-control identifyhailo:hailols -l /dev/hailo0 shows hailo grouphailosystemd-analyze verifysystemctl start + check logscurl http://localhost:PORT/api/healthcurl ... /api/chat with sample request (fast response)free -h shows adequate headroom if running concurrent servicescurl .../api/unload releases memorysystemctl statusjournalctl -u service-name shows activity# Python service code
import signal
def signal_handler(sig, frame):
print("Shutting down gracefully...")
# Save model state, close Hailo device connection
sys.exit(0)
signal.signal(signal.SIGTERM, signal_handler)
# systemd unit
TimeoutStopSec=30
KillSignal=SIGTERM
# In installer, verify prerequisites:
if ! command -v hailortcli &> /dev/null; then
echo "ERROR: Hailo driver not installed. Run system setup first."
exit 1
fi
if ! [ -e /dev/hailo0 ]; then
echo "ERROR: /dev/hailo0 not found. Reboot after hailo-h10-all install."
exit 1
fi
# Installer: Pre-download and load model to avoid cold-start delays
su - hailo -s /bin/bash -c "/usr/local/bin/ollama pull neural-chat"
# Service startup script loads model into memory:
# - systemd ExecStartPost can trigger initial model load
# - Or service startup code loads default model
# - Subsequent requests use already-loaded model (fast)
# Example multi-service deployment on Pi 5 (8GB RAM):
# - hailo-ollama: 2GB (LLM inference)
# - hailo-whisper: 1GB (STT)
# - hailo-yolo: 512MB (object detection)
# - OS + overhead: 1.5GB
# Total: ~5GB (safe with headroom)
# Monitor memory across services:
free -h
systemctl status hailo-ollama hailo-whisper hailo-yolo
# Each service tracks its own memory via /api/health:
curl http://localhost:11434/api/health # Ollama
curl http://localhost:8082/api/health # Whisper
curl http://localhost:8080/api/health # YOLO
Key principle: Services running under systemd have constrained environments that differ from manual execution. What works when you run application may fail as a service.
Common differences:
WorkingDirectory=, not your shell's $PWDhailo-ollama), not root or your user/usr/local/bin or other custom paths/var/lib/service-name), not /root or /home/youDebugging strategy:
Test as the service user first:
# Become the service user and test manually
sudo -u hailo-ollama bash
cd /var/lib/hailo-ollama
/usr/bin/hailo-ollama # Run exactly as service will
Match the service environment:
# Export the exact environment from unit file
sudo -u hailo-ollama \
XDG_DATA_HOME=/var/lib \
XDG_CONFIG_HOME=/etc/xdg \
bash -c '/usr/bin/hailo-ollama'
Inspect the running service environment:
# Check environment variables
sudo cat /proc/$(pgrep service-name)/environ | tr '\0' '\n'
# Check mount points (for bind mounts)
sudo cat /proc/$(pgrep service-name)/mountinfo | grep service-name
# Check working directory
ls -la /proc/$(pgrep service-name)/cwd
Problem pattern: Application works manually but service can't find resources (models, configs, manifests).
Discovery mechanisms to investigate:
Hardcoded paths:
# Search binary for path strings
strings /usr/bin/application | grep -E "(usr|var|etc|share)"
Environment-based paths (XDG, HOME, etc.):
# Trace file access during startup
strace -e openat,access /usr/bin/application 2>&1 | grep -E "(model|config|manifest)"
Config file references:
# Check installed config files
dpkg -L package-name | grep -E "\.(json|yaml|conf|ini)$"
Common solutions:
Option 1: Configuration (preferred when supported):
# systemd unit
Environment=MODEL_PATH=/usr/share/hailo-models
Option 2: Bind mount (when app searches fixed user paths):
# Mount package resources into service's writable state
BindReadOnlyPaths=/usr/share/package-resources:/var/lib/service-name/resources
Why bind mounts over symlinks:
d_type: Directory scanners using readdir() see DT_LNK for symlinks, DT_DIR for real directories. Many apps skip non-directory entries.Option 3: Copy resources (last resort):
# In installer: copy package resources to service directory
cp -r /usr/share/package-resources/* /var/lib/service-name/resources/
chown -R service-user:service-user /var/lib/service-name/resources/
Downsides: Duplicates data, stale after package upgrades, requires re-copy logic.
When to use: Application fails in service but works manually; need to see what it's actually accessing.
# Trace file operations during startup
sudo systemctl stop service-name
strace -f -e openat,access,stat /usr/bin/application 2>&1 | tee /tmp/strace.log
# Common patterns to look for:
grep -E "ENOENT|EACCES" /tmp/strace.log # File not found or permission denied
grep "manifest" /tmp/strace.log # Resource discovery paths
grep "\.so" /tmp/strace.log # Library loading issues
Attach to running service:
# Trace a running process
pid=$(pgrep service-name)
sudo strace -p $pid -e openat -f 2>&1 | grep -i resource
Progressive verification checklist:
Package resources installed:
dpkg -L package-name | grep -E "models|manifests|configs"
ls -la /usr/share/package-name/
Service user can read resources:
sudo -u service-user ls -la /usr/share/package-resources/
sudo -u service-user cat /usr/share/package-resources/model.hef
Manual execution as service user:
sudo systemctl stop service-name
sudo -u service-user bash -c 'cd /var/lib/service-name && /usr/bin/application'
# Verify application finds resources
Service execution:
sudo systemctl start service-name
sudo systemctl status service-name
sudo journalctl -u service-name -n 50
API validation:
curl http://localhost:PORT/api/health
curl http://localhost:PORT/api/list-resources
Issue: "Resources not found" in service but work manually
Cause: Application searches paths relative to $HOME, $XDG_DATA_HOME, or current directory.
Debug:
# Compare environments
env | grep -E "HOME|XDG|PATH" > /tmp/manual.env
sudo cat /proc/$(pgrep service-name)/environ | tr '\0' '\n' | grep -E "HOME|XDG|PATH" > /tmp/service.env
diff /tmp/manual.env /tmp/service.env
Solutions:
BindReadOnlyPaths to mount resources into expected locationIssue: "Permission denied" accessing /dev/hailo0
Cause: Service user not in Hailo device group.
Fix:
stat -c '%G' /dev/hailo0 # Check device group (usually 'hailo' or 'root')
sudo usermod -aG hailo service-user
sudo systemctl restart service-name
Issue: Models load on first run but not after package upgrade
Cause: Resources copied during install rather than referenced from package location.
Fix: Use bind mounts or config paths pointing to /usr/share instead of copying.
Lesson: Device manager socket errors are transient
Context: When integrating services with the device manager, socket connection errors may appear in logs but resolve automatically as services establish connections.
Action: No intervention needed; these are normal during startup and connection establishment.
Lesson: Model file permissions must be accessible by device_manager
Context: Services that download or manage HEF models must ensure the files have permissions allowing the device manager (running as hailo-device-mgr user) to read them. This includes:
hailo-device-mgr (e.g., chown hailo-depth:hailo-device-mgr model.hef)hailo-depth) is in the hailo-device-mgr group (usermod -aG hailo-depth hailo-device-mgr)drwxr-x---)Additional Integration Note: When adding new model types (e.g., depth), you must update the installed device manager code (e.g., copy updated hailo_device_manager.py to /opt/hailo-device-manager/) so the running service recognizes and supports the new handler. Restart the device manager service after updating.
Lesson: Testing practices for device manager services
Context: When testing new services integrated with device manager, avoid cluttering logs and conversations with raw base64 data.
Action: Uninstall old service (and its config) first. Test against the running system service. Use file payloads or script base64 encoding instead of inline base64 in curl commands or output.
Scenario: Application uses XDG Base Directory specification but only searches XDG_DATA_HOME, not XDG_DATA_DIRS.
Symptoms:
/usr/share/application/XDG_DATA_DIRS includes /usr/share but doesn't helpRoot cause: Application follows XDG spec incompletely—only checks user data location, not system data directories.
Solution: Use BindReadOnlyPaths:
# systemd unit
Environment=XDG_DATA_HOME=/var/lib
BindReadOnlyPaths=/usr/share/application/resources:/var/lib/application/resources
Lesson: Don't assume applications follow specs completely. Verify actual behavior with strace.
See hailo-ollama MANIFEST_DISCOVERY.md for detailed case study.
# Real-time logs
sudo journalctl -u hailo-ollama.service -f
# Debug level logs (if configured)
sudo journalctl -u hailo-ollama.service -p debug
# Continuous output with timestamps
sudo journalctl -u hailo-ollama.service -o short-iso -f
# Health
curl http://localhost:11434/api/health | jq
# List models
curl http://localhost:11434/api/tags | jq
# Chat inference
curl -X POST http://localhost:11434/api/chat \
-H "Content-Type: application/json" \
-d '{
"model": "neural-chat",
"messages": [{"role": "user", "content": "Hello"}],
"stream": false
}' | jq
# During active inference, monitor in separate terminal
watch -n 1 'echo "Temp: $(vcgencmd measure_temp)" && \
free -h && \
ps aux | grep hailo-ollama'
Reference: