Text Generation
Transformers
GGUF
English
recommendation
media-server
plex
jellyfin
structured-output
qwen3
conversational
Instructions to use KernelMedia/marquee-ai with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use KernelMedia/marquee-ai with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="KernelMedia/marquee-ai")# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("KernelMedia/marquee-ai", device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- llama.cpp
How to use KernelMedia/marquee-ai with llama.cpp:
Install (macOS, Linux)
curl -LsSf https://llama.app/install.sh | sh # Start a local OpenAI-compatible server with a web UI: llama serve -hf KernelMedia/marquee-ai:Q6_K # Run inference directly in the terminal: llama cli -hf KernelMedia/marquee-ai:Q6_K
Install from WinGet (Windows)
winget install llama.cpp # Start a local OpenAI-compatible server with a web UI: llama serve -hf KernelMedia/marquee-ai:Q6_K # Run inference directly in the terminal: llama cli -hf KernelMedia/marquee-ai:Q6_K
Build from source code
git clone https://github.com/ggml-org/llama.cpp.git cd llama.cpp cmake -B build cmake --build build -j --target llama-app # Start a local OpenAI-compatible server with a web UI: ./build/bin/llama serve -hf KernelMedia/marquee-ai:Q6_K # Run inference directly in the terminal: ./build/bin/llama cli -hf KernelMedia/marquee-ai:Q6_K
Use Docker
docker model run hf.co/KernelMedia/marquee-ai:Q6_K
- Llama new
- LM Studio
- Jan
- vLLM
How to use KernelMedia/marquee-ai with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "KernelMedia/marquee-ai" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "KernelMedia/marquee-ai", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/KernelMedia/marquee-ai:Q6_K
- SGLang
How to use KernelMedia/marquee-ai with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "KernelMedia/marquee-ai" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "KernelMedia/marquee-ai", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "KernelMedia/marquee-ai" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "KernelMedia/marquee-ai", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Ollama
How to use KernelMedia/marquee-ai with Ollama:
ollama run hf.co/KernelMedia/marquee-ai:Q6_K
- Unsloth Desktop
- Pi
How to use KernelMedia/marquee-ai with Pi:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf KernelMedia/marquee-ai:Q6_K
Configure the model in Pi
# Install Pi: npm install -g @earendil-works/pi-coding-agent # Add to ~/.pi/agent/models.json: { "providers": { "llama-cpp": { "baseUrl": "http://localhost:8080/v1", "api": "openai-completions", "apiKey": "none", "models": [ { "id": "KernelMedia/marquee-ai:Q6_K" } ] } } }Run Pi
# Start Pi in your project directory: pi
- Docker Model Runner
How to use KernelMedia/marquee-ai with Docker Model Runner:
docker model run hf.co/KernelMedia/marquee-ai:Q6_K
- Lemonade
How to use KernelMedia/marquee-ai with Lemonade:
Pull the model
# Download Lemonade from https://lemonade-server.ai/ lemonade pull KernelMedia/marquee-ai:Q6_K
Run and chat with the model
lemonade run user.marquee-ai-Q6_K
List all available models
lemonade list
- Hermes Agent
How to use KernelMedia/marquee-ai with Hermes Agent:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf KernelMedia/marquee-ai:Q6_K
Configure Hermes
# Install Hermes: curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash hermes setup # Point Hermes at the local server: hermes config set model.provider custom hermes config set model.base_url http://127.0.0.1:8080/v1 hermes config set model.default KernelMedia/marquee-ai:Q6_K
Run Hermes
hermes
- Atomic Chat
- OpenClaw
How to use KernelMedia/marquee-ai with OpenClaw:
Start the llama.cpp server
# Install llama.cpp: brew install llama.cpp # Start a local OpenAI-compatible server: llama serve -hf KernelMedia/marquee-ai:Q6_K
Configure OpenClaw
# Install OpenClaw: npm install -g openclaw@latest # Register the local server and set it as the default model: openclaw onboard --non-interactive --mode local \ --auth-choice custom-api-key \ --custom-base-url http://127.0.0.1:8080/v1 \ --custom-model-id "KernelMedia/marquee-ai:Q6_K" \ --custom-provider-id llama-cpp \ --custom-compatibility openai \ --custom-text-input \ --accept-risk \ --skip-health
Run OpenClaw
openclaw agent --local --agent main --message "Hello from Hugging Face"
Update model card: Q6_K (fixes tool_call corruption), full-precision training, corrected specs
e6d5daf verified |
Download README.md from KernelMedia/marquee-ai: direct link, hf CLI and curl.
- Browser
- Download file 10.8 kB
-
https://huggingface.co/KernelMedia/marquee-ai/resolve/main/README.md
- Command line
-
hf download hf://KernelMedia/marquee-ai/README.md
-
curl -L -o README.md https://huggingface.co/KernelMedia/marquee-ai/resolve/main/README.md
10.8 kB
| license: apache-2.0 | |
| base_model: unsloth/Qwen3-4B-Instruct-2507 | |
| tags: [recommendation, media-server, plex, jellyfin, structured-output, qwen3] | |
| language: [en] | |
| library_name: transformers | |
| pipeline_tag: text-generation | |
| # Marquee | |
| **v1.1** · contract v1 · base Qwen3-4B-Instruct-2507 | |
| Turns one item in a media library into Plex-style rows of other items in **that same | |
| library**. Built for self-hosted media servers. | |
| ``` | |
| IN: "The Sopranos" | |
| OUT: | |
| Shows like The Sopranos Gomorrah, Boardwalk Empire, The Wire, ZeroZeroZero | |
| More from Tim Van Patten Game of Thrones, Black Mirror, Boardwalk Empire | |
| More with Edie Falco Nurse Jackie, Oz | |
| Shot by Alik Sakharov Game of Thrones, House of Cards | |
| ``` | |
| ## Read this before downloading the weights | |
| **The model alone does nothing useful.** It decides *which* rows to show, their order, | |
| and what to call them. It does not choose what goes in them -- a retriever does, by | |
| joining your library on shared cast, crew and franchise, and by embedding similarity. | |
| The model never even sees item ids. It answers with **index references** into | |
| candidates it was handed, so it cannot invent a title that isn't in your library. | |
| Hallucination isn't low here, it's structurally impossible. | |
| ```bash | |
| pip install marquee-ai | |
| ``` | |
| ## Download | |
| ```bash | |
| hf download KernelMedia/marquee-ai --local-dir marquee | |
| cd marquee && ollama create marquee -f Modelfile | |
| ``` | |
| ~3.6 GB: the model plus a catalog of ~195,000 films and shows with full cast and crew. | |
| ## Requirements | |
| | | | | |
| |---|---| | |
| | VRAM | **~4.7 GB** (Q6_K at 6144 context). Runs on a 6GB card, or CPU. | | |
| | RAM | ~2.5 GB for the 195k-title catalog index | | |
| | Disk | ~3.3 GB model + ~240 MB catalog + ~300 MB embeddings | | |
| | Latency | ~1s per request on an RTX 3060; cached responses are instant | | |
| ## Quickstart | |
| ```bash | |
| marquee serve --library catalog.jsonl --port 8080 | |
| ``` | |
| That's the whole setup -- catalog and embeddings ship precomputed. | |
| ## API | |
| ### `POST /recommend` | |
| ```bash | |
| curl -X POST localhost:8080/recommend -H 'Content-Type: application/json' \ | |
| -d '{"title": "The Sopranos", "row_size": 20}' | |
| ``` | |
| ```json | |
| { | |
| "seed": {"id": "wd:Q23628", "title": "The Sopranos", "year": 1999, | |
| "media_type": "tv", "rating": 0.0, "owned": true}, | |
| "rows": [ | |
| {"type": "thematic", "title": "Shows like The Sopranos", | |
| "items": [ | |
| {"id": "wd:Q...", "title": "Gomorrah", "year": 2014, | |
| "media_type": "tv", "owned": true}, | |
| {"id": "wd:Q...", "title": "Boardwalk Empire", "year": 2010, | |
| "media_type": "tv", "owned": true} | |
| ]}, | |
| {"type": "director", "title": "More from Tim Van Patten", | |
| "items": [ | |
| {"id": "wd:Q...", "title": "Game of Thrones", "year": 2011, | |
| "media_type": "tv", "owned": true} | |
| ]} | |
| ] | |
| } | |
| ``` | |
| Body: `seed_id` **or** `title`, plus optional `year`, `media_type`, `row_size`, | |
| `owned_only`, `semantic_k`. Rows arrive in a fixed order -- thematic, franchise, | |
| director, cast, crew. | |
| An ambiguous title returns **409** with the candidates rather than guessing. A catalog | |
| this size holds many remakes sharing a bare title: | |
| ```json | |
| {"error": "22 items match 'Macbeth'; pass seed_id, or narrow with year/media_type", | |
| "matches": [ | |
| {"id": "wd:Q15934383", "title": "Macbeth", "year": 2015, "media_type": "movie"}, | |
| {"id": "wd:Q2573008", "title": "Macbeth", "year": 1951, "media_type": "movie"} | |
| ]} | |
| ``` | |
| `{"title": "Macbeth", "year": 2015}` or `{"title": "Macbeth (2015)"}` resolves it. | |
| ### `GET /metadata` and `POST /metadata/batch` | |
| Full catalog record for one item, or up to 500 in one call. Pure index lookups -- no | |
| model, no GPU. Look up by `id`, `tmdb_id`, `imdb_id` (all exact), or `title` (fuzzy, | |
| can be ambiguous like `/recommend`). | |
| ```bash | |
| curl 'localhost:8080/metadata?tmdb_id=335984' | |
| ``` | |
| ```json | |
| { | |
| "id": "wd:Q21500755", "title": "Blade Runner 2049", "media_type": "movie", | |
| "year": 2017, "genres": ["cyberpunk", "dystopian film", "neo-noir"], | |
| "people": [{"name": "Denis Villeneuve", "role": "director"}, | |
| {"name": "Roger Deakins", "role": "cinematographer"}], | |
| "summary": "Blade Runner 2049 is a 2017 American science fiction film...", | |
| "franchise": "Blade Runner", "runtime_min": 164, | |
| "country": "United Kingdom", "language": "English", | |
| "external": {"wikidata": "Q21500755", "tmdb": "335984", "imdb": "tt1856101"} | |
| } | |
| ``` | |
| ```bash | |
| curl -X POST localhost:8080/metadata/batch -H 'Content-Type: application/json' -d '{ | |
| "queries": [{"tmdb_id": 335984}, {"imdb_id": "tt0141842"}] | |
| }' | |
| ``` | |
| Every query gets a result in the order sent -- found, ambiguous with candidates, or | |
| not found -- so you can zip the response against your input. | |
| ### `GET /search?q=` and `GET /person?name=` | |
| `/search` matches titles **and** people in one call -- "Scorsese" isn't a title but has | |
| 109 credits. `/person` returns everything someone worked on, films and shows together, | |
| ranked by a popularity-weighted score, deduplicated across roles. Both are index | |
| lookups, no model call. | |
| ```bash | |
| curl 'localhost:8080/person?name=Roger%20Deakins&limit=3' | |
| ``` | |
| ```json | |
| {"person": "Roger Deakins", "count": 3, "credits": [ | |
| {"id": "wd:...", "title": "The Shawshank Redemption", "year": 1994, | |
| "score": 8.66, "roles": ["cinematographer"]} | |
| ]} | |
| ``` | |
| `score` is what ranking actually uses -- popularity-weighted, so a well-known film | |
| outranks an obscure one even without a raw rating to sort by. | |
| ### `GET /health` and `DELETE /cache` | |
| ```json | |
| {"status": "ok", "contract_version": 1, | |
| "model_self_test": {"passed": true, "detail": "contract v1 OK (3/3 clean)"}, | |
| "library_items": 194509, "owned": 194509, "requestable": 0} | |
| ``` | |
| `status` is `degraded` if the self-test fails -- model and package disagree, or the | |
| chat template is wrong. Recommendations are cached per seed; call `DELETE /cache` | |
| after a library rescan or you'll serve rows referring to items you no longer have. | |
| ## Restricting results to your own library (optional) | |
| By default it recommends from the whole catalog. To mark what's actually on your | |
| server, so a UI can show a Request button on the rest: | |
| ```bash | |
| marquee ingest --server library.jsonl --catalog catalog.jsonl \ | |
| --out lib.jsonl --mode requestable | |
| marquee serve --library lib.jsonl | |
| ``` | |
| Your export needs one line per item; `tmdb_id` and `media_type` are enough: | |
| ```json | |
| {"id": "myserver-04471", "tmdb_id": 335984, "media_type": "movie"} | |
| ``` | |
| ## Evaluation | |
| 50 unseen holdout examples, run through plain `transformers.generate` with **no | |
| grammar constraint**, on the unmerged adapter -- so this measures the weights, not a | |
| decoder forcing shape, and not the quantization the GGUF ships with. | |
| | Metric | Base Qwen3-4B | **Marquee** | | |
| |---|---|---| | |
| | JSON parses | -- | **100%** | | |
| | Fully contract-clean | -- | **100%** | | |
| | Hallucinated ids | -- | **0.0%** | | |
| | Row-order violations | -- | **0%** | | |
| That number does not carry over unchanged to the shipped GGUF: merging a LoRA adapter | |
| and quantizing it are both lossy steps this harness doesn't exercise. Measured | |
| end-to-end through actual Ollama serving (20 held-out seeds, real greedy decoding) -- | |
| **Q6_K: 19/20 clean.** See Limitations below for what pushed us to Q6_K over a smaller | |
| quant. | |
| **What this is, honestly:** the fine-tune buys format discipline, not taste. The | |
| retriever picks every item; the model decides which rows to show and writes one row | |
| title. It does not make recommendations smarter on its own -- that lives in the | |
| retriever's embeddings and joins. | |
| ## Training | |
| - **Base**: Qwen3-4B-Instruct-2507, full-precision LoRA (bf16 base, not QLoRA) r=16, | |
| lr 2e-4, 1 epoch, 3,684 examples | |
| - **Data**: generated by Qwen3-8B-AWQ over the catalog below, normalised by a | |
| deterministic repair pass. 0.4% rejected. | |
| - **Hardware**: one RTX 3060 12GB, ~2h48m, 10.2 GB peak VRAM | |
| - **Targets are ~30 tokens**: the model emits index references, not item lists | |
| - **Why full precision, not QLoRA**: a QLoRA adapter is calibrated against its 4-bit | |
| base's numerical behaviour. Merging it into full-precision weights for GGUF export | |
| bakes in a dequantized representation the adapter never saw, which destabilized this | |
| model badly enough to periodically emit `<tool_call>` instead of JSON. Training | |
| directly on real bf16 weights removes that mismatch. | |
| ## Limitations | |
| - **Thematic rows are the weakest part.** Built from `bge-small` embeddings blended | |
| with keyword and genre overlap; ~31% of the catalog has no plot summary to embed, | |
| so those titles lean on genre/subject tags alone. | |
| - **Franchise coverage is thin (~2%)** compared to a TMDB-sourced catalog -- series | |
| membership is sparse in the source data. | |
| - **Same-medium only.** A show returns shows, a film returns films. | |
| - **Library-size sensitive.** A director with one other credit present gets no row. | |
| Deliberate -- no padding. | |
| - **Movies and TV only.** English metadata. | |
| - **No poster/image data.** The catalog is text and structured facts only; nothing in | |
| it or the API returns image URLs. Pull artwork from your media server, or from | |
| TMDB/Fanart yourself using the `external.tmdb`/`external.imdb` ids in `/metadata`. | |
| ## ⚠️ GGUF: the chat template is not optional | |
| Ollama will not use the Jinja template embedded in GGUF metadata and falls back to a | |
| bare `{{ .Prompt }}` passthrough, so the model never receives the `<|im_start|>` | |
| markers it was trained on and reverts to base-model behaviour. The supplied | |
| `Modelfile` carries an explicit ChatML `TEMPLATE` block. **Do not remove it.** | |
| Two things degrade output independently, and both are already handled in the shipped | |
| Modelfile/GGUF: | |
| - `temperature` must stay low -- 0.1 gave 8/8 clean over 8 test seeds without a | |
| grammar, 0.3 gave 5/8. The Modelfile ships 0.1. | |
| - **Don't requantize below Q6_K.** llama.cpp's Q4_K_M rounding alone -- independent of | |
| temperature, independent of the LoRA-merge issue above -- was enough to collapse | |
| this model into repeating `<tool_call>` tokens instead of JSON on most requests | |
| (1/20 clean vs Q6_K's 19/20, same weights, same greedy decoding, only the quant | |
| level changed). Q6_K is the smallest quant confirmed clean. | |
| Verify any deployment by calling it **without** a JSON schema. A working fine-tune | |
| emits valid contract JSON unprompted. `marquee serve` runs this check at startup. | |
| ## Data | |
| The catalog is built entirely from **Wikidata** (CC0) and **Wikipedia** (plot | |
| summaries, embedded then discarded -- never redistributed as text, so CC BY-SA's | |
| share-alike terms are never triggered). No TMDB data is shipped or was used in | |
| training. | |
| **Not used:** TMDB (API terms prohibit ML training and third-party redistribution of | |
| cached content), IMDb (non-transferable, non-sublicensable licence), Rotten Tomatoes | |
| (no public API). | |
| ## Licence | |
| Apache-2.0, inherited from Qwen3-4B-Instruct-2507. Catalog: CC0. | |