Fyb3roptik commited on
Commit
3772c41
·
verified ·
1 Parent(s): 8154f44

real API examples, plug-and-play quickstart

Browse files
Files changed (1) hide show
  1. SETUP.md +190 -104
SETUP.md CHANGED
@@ -1,183 +1,269 @@
1
- # Running Marquee on your own server
2
 
3
- **Marquee-4B v1.0**
4
 
5
- Complete setup, start to finish. Roughly an hour, most of it unattended.
6
-
7
- ## What you are building
8
 
 
 
9
  ```
10
- your media server
11
- | exports {id, tmdb_id, media_type}
12
- v
13
- marquee ingest ------> library index (titles, cast, crew, embeddings)
14
- |
15
- v
16
- marquee serve <-----> Ollama (the model)
17
- | HTTP
18
- v
19
- your media server UI
20
  ```
21
 
22
- The model decides which rows to show and names them. The **retriever** — the index —
23
- decides what goes in them. Both matter; the model on its own is not useful.
24
 
25
- ## 1. Install
26
 
27
  ```bash
28
- pip install marquee-ai
29
- curl -fsSL https://ollama.com/install.sh | sh # if you do not have it
30
  ```
31
 
32
- ## 2. Get the model
 
 
33
 
34
  ```bash
35
- huggingface-cli download KernelMedia/marquee-ai --local-dir marquee-model
36
- cd marquee-model && ollama create marquee -f Modelfile && cd ..
 
37
  ```
38
 
39
- Do not edit the `Modelfile`'s `TEMPLATE` block or raise `temperature`. Both are
40
- load-bearing — see Troubleshooting.
 
 
 
 
41
 
42
- ## 3. Get a TMDB key
43
 
44
- Free, two minutes: https://www.themoviedb.org/settings/api — choose Developer,
45
- non-commercial.
 
 
 
 
46
 
47
  ```bash
48
- echo 'TMDB_API_KEY=your_key' >> .env
 
 
49
  ```
50
 
51
- TMDB is free for non-commercial use and requires attribution. **Commercial use needs a
52
- written agreement with TMDB** — if you charge for access to this, talk to them first.
53
 
54
- ## 4. Harvest metadata
55
 
56
  ```bash
57
- marquee harvest --out tmdb_full.jsonl
 
58
  ```
59
 
60
- ~55 minutes, ~120,000 titles, ~210 MB. It caches every response, so re-runs are free
61
- and an interrupted run resumes.
62
-
63
- This keeps titles with 10+ votes (plus all anime regardless, which skews to low vote
64
- counts). That drops TMDB's 1.4M entries to ~120k by removing shorts, unreleased stubs
65
- and uploads with no credits — none of which can form a recommendation row anyway.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66
 
67
- `--min-votes 0` takes everything: 1.4M titles, ~10 hours, ~108 GB of cache, and about
68
- 27 GB of RAM to load. Not advised.
69
 
70
- ## 5. Export your library
 
71
 
72
- One JSON object per line. Only `tmdb_id` and `media_type` are required:
73
 
74
  ```json
75
- {"id": "myserver-04471", "tmdb_id": 335984, "media_type": "movie"}
76
- {"id": "myserver-04472", "tmdb_id": 1396, "media_type": "tv"}
 
 
 
 
77
  ```
78
 
79
- Include `id` and recommendations come back keyed the way your server already refers to
80
- things, so you need no lookup table.
81
 
82
- **Jellyfin**: `/Items?Recursive=true&Fields=ProviderIds` gives `ProviderIds.Tmdb`.
83
- **Plex**: `/library/sections/{id}/all` — the `guid` field carries the TMDB id on
84
- recent agents.
85
 
86
- ## 6. Build the library index
87
 
88
  ```bash
89
- marquee ingest --server library.jsonl --catalog tmdb_full.jsonl \
90
- --out lib.jsonl --mode requestable
91
  ```
92
 
93
- | mode | contents |
94
- |---|---|
95
- | `owned` | only what is on your server — every recommendation is playable |
96
- | `requestable` | your items **plus** unowned titles flagged `owned: false`, so the UI can offer a Request button |
 
 
 
 
 
 
 
 
 
 
97
 
98
- `requestable` plus `owned_only` at query time is the flexible setup: one index serves
99
- both, and you can flip per user.
100
 
101
- Anything whose `tmdb_id` is not in the harvest is fetched from TMDB on the spot, so
102
- coverage is complete however you filtered. `--no-fetch` disables that.
103
 
104
- ## 7. Serve
105
 
106
  ```bash
107
- marquee serve --library lib.jsonl --model marquee --port 8080
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  ```
109
 
110
- First start builds embeddings (~3 min for 120k titles) and caches them next to the
111
- library. Later starts take ~15 s.
 
112
 
113
- Watch for the startup line:
114
 
115
- ```
116
- [marquee] contract v1 self-test: PASS - contract v1 OK (3/3 clean)
 
 
 
 
 
 
 
 
 
 
 
 
117
  ```
118
 
119
- FAIL means the model and the package disagree, or the chat template is wrong. It will
120
- say which.
121
 
122
- ## API
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
  ```bash
125
- curl -X POST localhost:8080/recommend \
126
- -H 'Content-Type: application/json' \
127
- -d '{"seed_id": "myserver-04471", "row_size": 20}'
 
 
 
128
 
129
- curl 'localhost:8080/search?q=Scorsese' # titles and people
130
- curl 'localhost:8080/person?name=Roger+Deakins'
131
- curl localhost:8080/health
132
- curl -X DELETE localhost:8080/cache # after a library rescan
133
  ```
134
 
135
- `/recommend` takes `seed_id` (exact) or `title`. Ambiguous titles return **409 with the
136
- candidates** rather than guessing — `Godzilla` matches three films. Narrow with `year`
137
- and `media_type`, or write `"Top Gun (1986)"` directly.
138
 
139
- `/search` and `/person` are index lookups with no model call, so they are instant. Only
140
- `/recommend` uses the GPU.
 
 
 
 
141
 
142
- ### Caching
 
 
143
 
144
- Recommendations for an item are stable until the library changes, so they are cached.
145
- `/health` reports hits and misses. **Call `DELETE /cache` after a rescan** or you will
146
- serve rows referring to items you no longer have.
147
 
148
- ## Hardware
149
 
150
  | | |
151
  |---|---|
152
- | VRAM | ~3.5 GB. A 4 GB card is fine. CPU works but is slow — cache matters more there. |
153
- | RAM | ~2.2 GB for a 120k catalog |
154
- | Disk | 2.4 GB model, ~210 MB catalog, ~190 MB embeddings, ~2.6 GB API cache |
 
155
 
156
  ## Troubleshooting
157
 
158
  **Every row comes back typed `director`, titles look like item names.**
159
- The chat template is missing. Ollama ignores the Jinja template inside GGUF and falls
160
- back to a raw passthrough, so the model never sees the `<|im_start|>` markers it was
161
- trained on. Confirm your Modelfile has the `TEMPLATE` block:
162
  ```bash
163
  ollama show --template marquee # must NOT be just "{{ .Prompt }}"
164
  ```
 
165
 
166
  **Output is valid sometimes and not others.**
167
  `temperature` is too high. Structured output needs near-greedy decoding: 0.1 gave 8/8
168
- clean in testing, 0.3 gave 5/8.
169
 
170
  **Self-test fails at startup.**
171
- Model and package are on different contract versions. Both must be contract v1.
 
 
 
 
 
 
172
 
173
- **Server items are being dropped at ingest.**
174
- They have no TMDB id, or the id is wrong. The ingest reports the count and samples. On-
175
- demand fetch handles anything merely missing from the harvest.
176
 
177
- **Rows are thin, or missing entirely.**
178
- The library is small. A director with one other credit present gets no row — that is
179
- deliberate, the model is trained not to pad. Harvest more, or use `requestable` mode.
180
 
181
- ## Attribution
182
 
183
- This product uses the TMDB API but is not endorsed or certified by TMDB.
 
1
+ # Marquee
2
 
3
+ Recommendation rows for a media server.
4
 
5
+ ## Install
 
 
6
 
7
+ ```bash
8
+ pip install marquee-ai
9
  ```
10
+
11
+ You also need [Ollama](https://ollama.com) to run the model.
12
+
13
+ ## Download
14
+
15
+ ```bash
16
+ hf download KernelMedia/marquee-ai --local-dir marquee
17
+ cd marquee
18
+ ollama create marquee -f Modelfile
 
19
  ```
20
 
21
+ ~2.8 GB: the model, plus a catalog of ~122,000 films and shows with full cast and crew.
 
22
 
23
+ ## Run
24
 
25
  ```bash
26
+ marquee serve --library catalog.jsonl --port 8080
 
27
  ```
28
 
29
+ Loads in about 15 seconds.
30
+
31
+ ## Use it
32
 
33
  ```bash
34
+ curl -X POST localhost:8080/recommend \
35
+ -H 'Content-Type: application/json' \
36
+ -d '{"title": "The Sopranos", "row_size": 20}'
37
  ```
38
 
39
+ ```
40
+ Shows like The Sopranos Gomorrah, Boardwalk Empire, The Wire, ZeroZeroZero
41
+ More from Tim Van Patten Game of Thrones, Black Mirror, Boardwalk Empire
42
+ More with Edie Falco Nurse Jackie, Oz
43
+ Shot by Alik Sakharov Game of Thrones, House of Cards
44
+ ```
45
 
46
+ Or from the command line:
47
 
48
+ ```bash
49
+ marquee recommend --library catalog.jsonl --seed "Blade Runner 2049" --row-size 20
50
+ marquee person --library catalog.jsonl --name "Roger Deakins"
51
+ ```
52
+
53
+ Set `MARQUEE_LIBRARY` once and drop the flag:
54
 
55
  ```bash
56
+ export MARQUEE_LIBRARY=$PWD/catalog.jsonl
57
+ export MARQUEE_MODEL=marquee
58
+ marquee recommend --seed "Dune (2021)"
59
  ```
60
 
61
+ ## API
 
62
 
63
+ ### `POST /recommend`
64
 
65
  ```bash
66
+ curl -X POST localhost:8080/recommend -H 'Content-Type: application/json' \
67
+ -d '{"title": "The Sopranos", "row_size": 20}'
68
  ```
69
 
70
+ ```json
71
+ {
72
+ "seed": {"id": "tmdb:tv:1398", "title": "The Sopranos", "year": 1999,
73
+ "media_type": "tv", "rating": 8.7, "owned": true},
74
+ "rows": [
75
+ {"type": "thematic", "title": "Shows like The Sopranos",
76
+ "items": [
77
+ {"id": "tmdb:tv:31050", "title": "The Last Don", "year": 1997,
78
+ "media_type": "tv", "rating": 7.1, "owned": true},
79
+ {"id": "tmdb:tv:12822", "title": "The Octopus", "year": 1984,
80
+ "media_type": "tv", "rating": 7.8, "owned": true}
81
+ ]},
82
+ {"type": "director", "title": "More from Tim Van Patten",
83
+ "items": [
84
+ {"id": "tmdb:tv:1399", "title": "Game of Thrones", "year": 2011,
85
+ "media_type": "tv", "rating": 8.5, "owned": true},
86
+ {"id": "tmdb:tv:1438", "title": "The Wire", "year": 2002,
87
+ "media_type": "tv", "rating": 8.6, "owned": true}
88
+ ]}
89
+ ]
90
+ }
91
+ ```
92
 
93
+ Body fields: `seed_id` **or** `title`, plus optional `year`, `media_type`, `row_size`
94
+ (items per row), `owned_only`, `semantic_k`.
95
 
96
+ Rows arrive in a fixed order — thematic, franchise, director, cast, crew — so a UI can
97
+ render them top to bottom without sorting.
98
 
99
+ An ambiguous title returns **409** with the candidates rather than guessing:
100
 
101
  ```json
102
+ {"error": "5 items match 'Godzilla'; pass seed_id, or narrow with year/media_type",
103
+ "matches": [
104
+ {"id": "tmdb:movie:124905", "title": "Godzilla", "year": 2014, "media_type": "movie"},
105
+ {"id": "tmdb:movie:929", "title": "Godzilla", "year": 1998, "media_type": "movie"},
106
+ {"id": "tmdb:movie:1678", "title": "Godzilla", "year": 1954, "media_type": "movie"}
107
+ ]}
108
  ```
109
 
110
+ `{"title": "Godzilla", "year": 2014}` or `{"title": "Godzilla (2014)"}` resolves it.
 
111
 
112
+ ### `GET /search?q=`
 
 
113
 
114
+ Matches titles **and** people, because "Scorsese" is not a title but has 109 credits.
115
 
116
  ```bash
117
+ curl 'localhost:8080/search?q=Scorsese&limit=3'
 
118
  ```
119
 
120
+ ```json
121
+ {
122
+ "query": "Scorsese",
123
+ "titles": [
124
+ {"id": "tmdb:tv:292130", "title": "Mr. Scorsese", "year": 2025,
125
+ "media_type": "tv", "rating": 8.1, "owned": true}
126
+ ],
127
+ "people": [
128
+ {"key": "tmdb:person:1032", "name": "Martin Scorsese",
129
+ "roles": {"director": 47, "actor": 50, "writer": 12}, "credits": 109}
130
+ ],
131
+ "count": 6
132
+ }
133
+ ```
134
 
135
+ `kind=title` or `kind=person` narrows it. Names are matched loosely: `samuel l jackson`,
136
+ `Jackson Samuel` and `Inarritu` all resolve.
137
 
138
+ ### `GET /person?name=`
 
139
 
140
+ Everything someone is credited on, films and shows in one list, best first.
141
 
142
  ```bash
143
+ curl 'localhost:8080/person?name=Roger%20Deakins&limit=3'
144
+ ```
145
+
146
+ ```json
147
+ {
148
+ "person": "Roger Deakins",
149
+ "count": 3,
150
+ "credits": [
151
+ {"id": "tmdb:movie:278", "title": "The Shawshank Redemption", "year": 1994,
152
+ "media_type": "movie", "rating": 8.7, "score": 8.66, "votes": 31061,
153
+ "owned": true, "roles": ["cinematographer"]},
154
+ {"id": "tmdb:movie:146233", "title": "Prisoners", "year": 2013,
155
+ "media_type": "movie", "rating": 8.1, "score": 8.04, "votes": 13352,
156
+ "owned": true, "roles": ["cinematographer"]}
157
+ ]
158
+ }
159
  ```
160
 
161
+ `score` is what the ranking uses: the raw rating damped toward the catalog mean by vote
162
+ count, so an 8.2 from 600 votes sits below a 7.6 from 4,300. Someone who both directed
163
+ and wrote a title appears once with both roles.
164
 
165
+ Options: `media_type`, `owned_only`, `limit`, `by_role=true` for per-role shelves.
166
 
167
+ ### `GET /health`
168
+
169
+ ```json
170
+ {
171
+ "status": "ok",
172
+ "contract_version": 1,
173
+ "model_self_test": {"passed": true, "detail": "contract v1 OK (3/3 clean)"},
174
+ "cache": {"entries": 0, "hits": 0, "misses": 0, "max": 2048},
175
+ "library_items": 121727,
176
+ "owned": 121727,
177
+ "requestable": 0,
178
+ "model": "marquee",
179
+ "backend": "http://localhost:11434/v1"
180
+ }
181
  ```
182
 
183
+ `status` is `degraded` if the self-test fails — the model and package disagree, or the
184
+ chat template is wrong.
185
 
186
+ ### `DELETE /cache`
187
+
188
+ Responses are cached per seed. Call this after a library rescan or you will serve rows
189
+ referring to items you no longer have.
190
+
191
+ `/search` and `/person` are index lookups with no model call, so they are instant. Only
192
+ `/recommend` uses the GPU.
193
+
194
+ ## Restricting results to your own library (optional)
195
+
196
+ By default it recommends from the whole 122k catalog, whether or not you own the title.
197
+
198
+ To mark what is actually on your server -- so a UI can show a Request button on the rest
199
+ -- export your library and join it:
200
 
201
  ```bash
202
+ marquee ingest --server library.jsonl --catalog catalog.jsonl \
203
+ --out lib.jsonl --mode requestable
204
+ marquee serve --library lib.jsonl
205
+ ```
206
+
207
+ Your export needs one JSON object per line; `tmdb_id` and `media_type` are enough:
208
 
209
+ ```json
210
+ {"id": "myserver-04471", "tmdb_id": 335984, "media_type": "movie"}
 
 
211
  ```
212
 
213
+ Every item then carries `"owned": true|false`. Pass `"owned_only": true` on a request to
214
+ drop the ones you do not have:
 
215
 
216
+ ```bash
217
+ curl -X POST localhost:8080/recommend -H 'Content-Type: application/json' \
218
+ -d '{"seed_id": "myserver-04471", "row_size": 20, "owned_only": true}'
219
+ ```
220
+
221
+ `/health` reports the split:
222
 
223
+ ```json
224
+ {"library_items": 121727, "owned": 1670, "requestable": 120057}
225
+ ```
226
 
227
+ **Jellyfin**: `/Items?Recursive=true&Fields=ProviderIds` gives `ProviderIds.Tmdb`.
228
+ **Plex**: `/library/sections/{id}/all` -- the `guid` carries the TMDB id on recent agents.
 
229
 
230
+ ## Requirements
231
 
232
  | | |
233
  |---|---|
234
+ | VRAM | ~3.5 GB. A 4 GB card is fine; CPU works but is slower. |
235
+ | RAM | ~2.2 GB for the 122k catalog |
236
+ | Disk | ~2.8 GB |
237
+ | Latency | ~1 s per request on an RTX 3060; cached responses are instant |
238
 
239
  ## Troubleshooting
240
 
241
  **Every row comes back typed `director`, titles look like item names.**
242
+ The chat template is missing. Ollama ignores the template inside GGUF metadata and falls
243
+ back to a raw passthrough, so the model never sees the tokens it was trained on:
 
244
  ```bash
245
  ollama show --template marquee # must NOT be just "{{ .Prompt }}"
246
  ```
247
+ Recreate from the supplied `Modelfile`, and do not remove its `TEMPLATE` block.
248
 
249
  **Output is valid sometimes and not others.**
250
  `temperature` is too high. Structured output needs near-greedy decoding: 0.1 gave 8/8
251
+ clean in testing, 0.3 gave 5/8. The Modelfile ships 0.1.
252
 
253
  **Self-test fails at startup.**
254
+ The model and the package are built against different contract versions. Both must be
255
+ contract v1.
256
+
257
+ **A title I own is missing.**
258
+ The catalog covers ~122k titles with 10+ TMDB votes, plus all anime. Genuinely obscure
259
+ releases may not be there. `marquee harvest --min-votes 0` rebuilds a wider catalog with
260
+ your own free TMDB key.
261
 
262
+ ## Data
 
 
263
 
264
+ Catalog metadata is from TMDB. This product uses the TMDB API but is not endorsed or
265
+ certified by TMDB.
 
266
 
267
+ ## Licence
268
 
269
+ Apache-2.0.