Self-hosted playlist generator¶
The playlist generator turns a prompt and explicit music controls into a private, editable draft. The model never writes to a streaming service. Open Playlist Engine resolves model suggestions against a connected target provider, and the user must review and confirm the final list before the durable migration worker creates anything.
Local OpenAI-compatible setup¶
Generation is disabled by default. Configure both an endpoint and model:
OPE_GENERATOR_BACKEND=openai_compatible
OPE_GENERATOR_OPENAI_BASE_URL=http://localhost:11434/v1
OPE_GENERATOR_MODEL=qwen3:8b
OPE_GENERATOR_OPENAI_API_KEY=
The endpoint must implement POST /chat/completions with OpenAI-compatible messages,
response_format={"type":"json_object"}, and max_tokens.
Docker Compose Ollama example¶
The optional generator profile runs Ollama without making it a required dependency:
docker compose --profile generator up -d ollama
docker compose exec ollama ollama pull qwen3:8b
When the backend also runs in Compose, use the service hostname:
OPE_GENERATOR_OPENAI_BASE_URL=http://ollama:11434/v1
OPE_GENERATOR_MODEL=qwen3:8b
Restart the backend after editing .env:
docker compose up -d --force-recreate backend worker
For an endpoint running directly on the host, use http://host.docker.internal:<port>/v1
from Docker Desktop. Linux installations may need an explicit host-gateway mapping.
Optional GitHub Copilot SDK¶
The backend includes github-copilot-sdk. Select it explicitly:
OPE_GENERATOR_BACKEND=copilot_sdk
OPE_GENERATOR_MODEL=auto
OPE_GENERATOR_COPILOT_GITHUB_TOKEN=
The SDK can use an authenticated Copilot CLI environment. In a container or unattended
deployment, set OPE_GENERATOR_COPILOT_GITHUB_TOKEN to an administrator-provided token
that is authorized for GitHub Copilot.
Copilot SDK mode is optional and is not local-only: the bounded generation context is sent to the selected GitHub Copilot model. The default OpenAI-compatible mode does not require a hosted AI account.
Controls¶
The Generator workspace supports:
- Natural-language prompt.
- Genres, moods, eras or decades.
- Energy, duration, and track count.
- Seed artists and tracks.
- Explicit-content preference.
- Familiarity and discovery levels.
- Connected target provider account.
Hard request limits are 2,000 prompt characters and 50 tracks. The administrator can lower the active track limit (25 by default), model output size, output tokens, and timeout:
OPE_GENERATOR_TIMEOUT_S=60
OPE_GENERATOR_MAX_PROMPT_CHARS=2000
OPE_GENERATOR_MAX_OUTPUT_CHARS=32000
OPE_GENERATOR_MAX_OUTPUT_TOKENS=4096
OPE_GENERATOR_MAX_TRACKS=25
Privacy boundaries¶
| Data | Model receives it? | Persisted? | Logged? |
|---|---|---|---|
| User prompt | Yes, only when Generate is pressed | No | No |
| Explicit controls | Yes | Only resolved draft state | No |
| Provider credentials or account IDs | No | Credentials remain encrypted in existing tables | No |
| Raw provider responses | No | No | No |
| Full listening or migration history | No | Existing local cache/job data remains local | No |
| Opt-in preference summary | Yes, capped top artists/genres and count only | Yes, private per user | No |
| Resolved draft tracks | No additional model call | Yes, private until deleted/confirmed | Operational IDs only |
Copilot SDK sessions use empty mode with no tools, file context, memory, skills, configuration discovery, host Git operations, session store, or session telemetry.
Personalization¶
Personalization is off by default. Enabling it derives a bounded aggregate from up to 500 locally cached playlist or migration items. Only top artists, top genres, and the source item count are stored and sent. Track titles and raw history are not included.
Turning personalization off stops using the stored summary. Delete data removes the summary entirely. Enabling it again rebuilds the summary from the current local cache.
Resolution and review¶
The model output is strict JSON containing a playlist name, optional description, and title/artist search intents. Unknown fields, malformed JSON, oversized output, and more tracks than requested are rejected.
Every suggestion is searched through the selected provider adapter and scored by the
existing MatchService:
- High-confidence unique candidates are marked Resolved.
- Low-confidence or live-version candidates are marked Review and require an explicit approval or replacement.
- Missing candidates are marked Unresolved and must be removed or replaced.
- Duplicate provider URIs are removed.
The review workspace can rename the playlist, edit its description, reorder, remove, approve, search replacements, add provider tracks, or regenerate. Added and replacement tracks are revalidated against provider search results.
Confirmation and durable writes¶
The generation request and all draft edits are non-writing operations. Final confirmation:
- Rejects empty, unresolved, unreviewed, duplicate, or no-longer-valid provider URIs.
- Runs target capability and conservative migration preflight checks.
- Requires a second acknowledgement if safe track/day/spacing or same-name warnings apply.
- Snapshots approved URIs into standard
MigrationJobandJobItemrows. - Enqueues the existing worker.
The worker skips model calls and matching. It writes only the reviewed URIs, using the existing playlist reuse, duplicate checks, provider batch limits, operation ledger, progress events, and statistics.
Generated drafts expose a universal Playlist snapshot in the API. Confirmation also
stores migration-history playlist metadata and valid universal track metadata, so the
existing history/export surfaces can reconstruct the generated source without exposing
internal account IDs or prompts.
Failure behavior and limitations¶
- Missing configuration returns a setup message and HTTP 503.
- Unreachable model endpoints return HTTP 503; model timeouts return HTTP 504.
- Invalid structured output returns HTTP 502 without echoing the model response.
- Generation and provider resolution happen in one request. Larger local models or
rate-limited providers can take time; lower
OPE_GENERATOR_MAX_TRACKSwhen necessary. - A model can suggest a nonexistent or incorrect song. It is never treated as a successful item unless the target provider returns a real candidate.
- OpenAI-compatible servers differ in JSON-mode support. Use an endpoint/model that
honors
response_formatand produces the documented schema.