尚未偵測到更新。GitHub 發布版本後會顯示在這裡。
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Treatments grounded in what the audience loves, built on Qloo's taste graph
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗itsgiving is an agentic pre-production assistant for music-video and short-film directors. Give it an artist (or a few reference films), a shoot city and a budget, and it builds a treatment grounded in what that audience actually loves: reference films, wardrobe brands and real shoot locations, each with the evidence behind it.
The name is Gen Z slang: "it's giving…" means "this has the vibe of…", which is what a treatment tells a client.
Status: built for the Qloo Agentic Hackathon and live at https://itsgiving-two.vercel.app. The treatment builder runs end to end on live Qloo: the Qloo tools, a lean LLM agent (DeepSeek first, free models as fallbacks), the board UI, five pre-built featured treatments, a director's taste imported from a TMDB list or IMDb links, and accounts for saving and sharing.
More in docs/media/. Film stills come from TMDB and the maps' streets from OpenStreetMap on the live
site; in these captures the maps show only Qloo's contours.
A treatment is the document a director writes to win a job: concept, references, wardrobe, locations. Today it reflects the director's own reference memory, while the client cares whether it lands with their audience. A general-purpose LLM doesn't fix that: it suggests the same canon for every brief, invents locations and has no idea who the fans are.
itsgiving uses Qloo's taste graph as the source of truth:
Code runs every Qloo call; the LLM is called at most three times per treatment (normally two), which keeps a treatment fast and cheap, and within the free fallback models' limits.
Intake is a form, so no LLM is spent parsing it.
Resolve the artist, touchstone films and the director's own films (resolve_entities). A director can also import
their taste: a public TMDB list or pasted IMDb links become up to 25 films that Qloo looks up by IMDb ID
(import_external_ids), so nothing about them is guessed. Names that match more
than one title go to a picker in the UI instead of being guessed; only Qloo's own candidates can be picked. A year
in brackets, like "Rockstar (2011)", settles it without asking, and so does one match being far better known than
the rest (the treatment notes which one was used). When no title has the name as typed, the picker says so and
offers only near misses (a small typo, or a word in common) plus "None of these"; Qloo's search always returns its
closest names, and for an artist it doesn't have those are other artists entirely. An artist with no exact match
is also looked for among Qloo's people, where it files some musicians (Raftaar, MC Stan). For an artist Qloo
doesn't have at all, the director can name up to three stand-in artists with a similar audience: their fans
carry the audience, the treatment keeps the artist's name, and the model is told to say nothing about the artist
beyond it. The shoot city is resolved too, as a Qloo locality: a state or county that shares its name with a city
("New York") is read as the city.
Read the audience (read_audience), map where the fans are (audience_heatmap) and, when the director
lists films, compare their taste with the audience (taste_overlap) and score each of their films for it
(score_for_audience, which asks Qloo to rank exactly that list). The calls run at once. Beside them, from the
moment the names are resolved, run two searches that need nothing else: the actors this audience knows
(casting_references: actors only, leaving out people Qloo also tags as influencers, YouTubers, reality-TV names,
athletes or musicians, within the budget's reach), each with the two parts TMDB says they're best known for (and
TMDB's photo, for a face Qloo has no picture of), and the artists it plays (music_references, each with a few of its notable songs). The writer is the first to wait
for them, and a re-plan doesn't ask again. The heatmap scores
every tile of the city for the audience; itsgiving groups the hottest tenth into the three strongest areas, 2 km in
radius, and names each after the neighbourhoods Qloo lists around its centre. Tags that name who people are
(sexual orientation, gender identity, religion, ethnicity, health, party politics) are dropped here: Qloo has some
as film themes, but a treatment never describes its audience by who they are. The audience read also asks Qloo
which film subgenres this audience loves; when one of them is a film tradition an era look borrows from (Bollywood
for Bombay '75, say), the title card suggests that era, with the rank Qloo gave it.
Iterate chips (darker, more 90s, deeper cuts, cheaper locations) re-run plan and write; Qloo lookups come from cache.
/examples) lists the featured treatments as cards (still, title in its face, logline, tones and
palette), filterable by format. Opening one, from here or the homepage, goes through the camera cut: the frame goes
dark, the camera's reels run up to speed and the shot pushes through a reel window into the board./studio) is the tool. A music video starts from its artist and a city; a short film from the films
it's after and a city. The rest waits under "Make it yours": a video's reference films and stand-in artists, a
short's "music its audience loves" (an artist whose fans it's for, which shapes the audience, not the story), your
own films, the budget and the thinking depth. "Your treatment" above the form says what the brief adds up to so far.
While a treatment builds, the brief folds into one line and the run takes the page: a countdown, the step it's on,
what Qloo found rolling in as opening credits, a strip with a cell for each step, and the tab's title counting the
seconds (the run log stays off this screen; the board's credits keep it). When it's ready, a slate claps, is pulled
up out of frame as on set, and the camera cuts through its window to the board. Back from a board returns to the
brief, and "Back to" the treatment's title opens it again, even after a visit to the homepage./t/<slug>) are pages of their own, built with the site, so Back, reload and links work as
anywhere. A rework chip there hands the treatment to the studio, which opens it at once and re-runs it with the
change. On every board, the section bar jumps within the page and puts the section in the address without adding
history, so Back always leaves the page (or, in the studio, returns to the brief)./about) says who itsgiving is for first (music-video directors, short-film makers, and the labels,
managers and producers who commission them), then what it is, what Qloo is, and how each step of a run uses Qloo.Four example briefs (src/lib/agent/examples.ts) are pre-built with deep thinking, so the homepage opens with
finished treatments and each one's page shows it at once, whatever the live rate limits are doing.
npm run featured builds them from live Qloo and DeepSeek. A run only counts if every Qloo call worked, no step fell
back to the template, and DeepSeek wrote it (when its key is set). Each brief gets two tries, and a failed rebuild
keeps the previous treatment..cache/featured/featured.json, which is gitignored, because Qloo data is never committed. Each
deploy builds its own set: prebuild runs npm run featured -- --if-missing before next build. Without live keys it
skips: the homepage then shows no posters, and the featured pages offer their briefs to the studio instead./t/<slug>) are built from the file, and the treatment API answers a featured
brief like a cache hit that never expires, so the studio reopens one at once. A server running on mocks ignores them,
and a featured page then offers to fill its brief into the studio. outputFileTracingIncludes puts the file in each
of those routes' server bundles.style:qloo tags that read like cinematography notes ("Stark close ups", "Domestic scale
cinematography"); itsgiving shows those first and hands them to the writer, so "what to borrow" is grounded in Qloo.tourist_attraction is on hotels and golf clubs, so it is not a venue kind. Places carry Qloo's own
ambience_description, shown as evidence on each location. Chain branches ("Starbucks - Juhu") are collapsed to
the best-ranked one.filter.location.query=New York matches New York State, and one treatment got two
venues in Hudson, NY, 200 km upstate. itsgiving now resolves the city to a locality ID first.output.heatmap.boundary=urn:entity:locality) ships every locality's
polygon: 5 MB for Mumbai, over 5 MiB for New York, and take doesn't trim it. The tile heatmap takes 0.5-1.1 s, and
a /search for localities near a point (sorted by distance, no query) names an area in about 0.3 s. Live Qloo tags
Manhattan as a "suburb" and returns Mumbai's wards with no kind, so names come from neighbourhood-sized localities
that no other result lists as its parent./entities by IMDb ID first echoed the IDs as a top-level external list of [{id}] (2026-10-24), then under
properties.external as {id, user_rating, ...} (2026-10-25). itsgiving reads both, since the taste import matches each
film back by its echoed ID.affinity and popularity are percentiles across the city's tiles and track each other closely, but both
are the audience's own: five very different artists' hottest tiles in New York overlapped 43% on average. itsgiving
ranks tiles by affinity times popularity, which favours where the fans gather, and where there are places to shoot.src/lib/llm)generateJson({ system, prompt, schema, mock }) is provider-neutral:
deepseek-flash when its (paid)
key is set (JSON mode with the schema in the prompt, thinking at the brief's depth), then the free tiers: Gemini
gemini-3.5-flash-lite (Google AI Studio, thinking level low for planning and medium for writing), Groq
openai/gpt-oss-120b (strict JSON schema, low reasoning effort, compact prompts for its 8K tokens/minute limit),
and gemini-3.8-flash as a last resort.TIMING in src/lib/agent/pipeline.ts): deep allows 20 s to plan, 75 s to write and 135 s in all, quick 15, 45 and
90 s, each leaving room for one fallback.
While a model thinks, the timeline counts the seconds, which also keeps the stream alive.LLM_MODE=mock, a spent daily cap, or every provider down, a deterministic template plans and writes.
Its output is always labelled Mock LLM, and the result records which model produced each step.All three providers are called over REST: DeepSeek's chat completions in JSON mode (the schema rides in the prompt),
Gemini's generateContent with responseJsonSchema, and Groq's OpenAI-compatible chat completions with
response_format: json_schema. There are no SDK dependencies.
Each tool is a typed, provider-neutral definition (zod schema exported as JSON Schema) and returns a uniform result: status, a one-line summary, compact data, warnings and the provenance of every Qloo request it made.
| Tool | What it does | Qloo endpoint(s) |
|---|---|---|
resolve_entities | Names and cities to Qloo IDs; asks the user about ambiguous names | GET /search |
import_external_ids | IMDb / Spotify IDs to Qloo entities (e.g. a director's watch history) | GET /entities |
find_tags | Mood or style words to tag IDs | GET /v2/tags (semantic search) |
read_audience | Taste vocabulary, age/gender skew and the film subgenres an audience loves | GET /v2/insights (urn:tag twice, urn:demographics) |
taste_overlap | Shared traits between the director's taste and the artist | GET /v2/analysis/compare |
audience_heatmap | Where in a city an audience over-indexes, as named areas to base a shoot in | GET /v2/insights (urn:heatmap), GET /search (localities near a point) |
score_for_audience | Ranks a given list (the director's own films) for an audience | GET /v2/insights (filter.results.entities) |
reference_films | Films/shows the audience loves, with era and deep-cut controls and explanations | GET /v2/insights (movie, tv_show) |
casting_references | Actors the audience knows, as faces for the story's characters, within a popularity ceiling | GET /v2/insights (person, urn:tag:genre:person:actor) |
music_references | Artists the audience plays, with their genres and notable songs | GET /v2/insights (artist) |
styling_brands | Fashion and lifestyle brands for wardrobe, with each brand's style and price range | GET /v2/insights (brand) |
scout_locations | Real places in the shoot city, or near a point, by ambience, budget and photo, with hours and contact | GET /v2/insights (place) |
Requires Node 22.12 or newer (.nvmrc pins 24).
nvm use
npm install
cp .env.example .env.local # optional: add QLOO_API_KEY, DEEPSEEK_API_KEY, GEMINI_API_KEY, GROQ_API_KEY, TMDB_API_KEY
npm run dev # http://localhost:3000
With no keys at all the app still runs end to end on the synthetic Qloo mock and the mock LLM, both clearly labelled.
Accounts (saving and sharing) are optional and run on Supabase. Without its keys every
account control hides itself. To try them locally (needs Docker), start a local Supabase and put the printed API URL,
publishable key and secret key in .env.local:
npx supabase start # applies supabase/migrations; the ports are in supabase/config.toml
npx supabase status -o env # API_URL, PUBLISHABLE_KEY, SECRET_KEY
For the hosted project: keep the Data API on and serving public when you create it ("Automatically expose new
tables" can stay off: the migration grants the server's role itself). Apply supabase/migrations (with
npx supabase db push, or by pasting the file into the SQL editor), turn on the Google and GitHub providers (their
callback is https://<ref>.supabase.co/auth/v1/callback) and turn off the Email provider. Set the Site URL to the
site, which lets sign-ins come back to its /auth/callback. To sign in from npm run dev against the hosted project,
also add http://localhost:3000/** to the redirect URLs: an exact /auth/callback entry won't match the ?next= the
app adds. A project created with automatic RLS gets a function, public.rls_auto_enable(), that the Security Advisor
flags because the browser roles may call it: revoke execute on function public.rls_auto_enable() from public, anon, authenticated; in the SQL editor clears that, and new tables still get RLS.
| Command | Purpose |
|---|---|
npm run dev | Dev server: the homepage at /, the treatment builder at /studio, the tool playground at /playground |
npm test | Unit tests (always against the mocks and fake LLM providers, never live APIs) |
npm run typecheck / npm run lint | Static checks |
npm run smoke | Day-1 checks of the live Qloo data itsgiving depends on (about 30 requests) |
npm run smoke:mock | Same script against the mock, to test the script itself |
npm run featured | Pre-build the featured treatments (live keys; -- --only=<slug> rebuilds one) |
All server-side only, except the two NEXT_PUBLIC_ ones, which are meant for the browser.
| Variable | Description |
|---|---|
QLOO_API_KEY | Qloo hackathon key |
QLOO_BASE_URL | Defaults to https://hackathon.api.qloo.com, the only host hackathon keys work on |
QLOO_MODE | live or mock; defaults to live when a key is set |
QLOO_CACHE_DIR | Optional on-disk response cache (gitignored) |
GEMINI_API_KEY | Google AI Studio key (free tier) |
GROQ_API_KEY | Groq key (free tier), a fallback |
LLM_MODE | live or mock; defaults to live when any LLM key is set |
DEEPSEEK_API_KEY | Optional, paid: when set, DeepSeek plans and writes first (under a cent a treatment) and the free tiers become fallbacks |
DEEPSEEK_MODEL / GEMINI_MODEL / GROQ_MODEL / GEMINI_FALLBACK_MODEL | Models in chain order (defaults deepseek-flash, gemini-3.5-flash-lite, openai/gpt-oss-120b, gemini-3.8-flash); none drops a Gemini one |
LLM_DAILY_CAP | Real LLM calls per UTC day per instance before the mock takes over (default 300) |
TMDB_BASE_URL | Optional: another address for TMDB's API; defaults to https://api.themoviedb.org |
ITSGIVING_TOOL_PLAYGROUND | 1 enables /playground and /api/tools/* in production builds (set at build time) |
TMDB_API_KEY | Optional: TMDB's "API Read Access Token" (or v3 API key), for importing a director's TMDB list, the reference films' stills and characters, and actors' roles and photos. IMDb links work without it; without stills, covers show the palette alone, and characters aren't drawn from any |
NEXT_PUBLIC_SUPABASE_URL / NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | Optional: the Supabase project's URL and publishable key, which turn accounts on (read at build time) |
POST /api/treatment: takes a brief, streams progress as server-sent events (start, step, needs_input,
treatment, error, done). Guarded by a per-IP rate limit (6 fresh treatments a minute), a daily LLM-call cap, a
run budget (135 s deep, 90 s quick, under a 165 s maxDuration) and a 24-hour cache keyed by the normalized brief.POST /api/taste: takes { "source": "<TMDB list link or IMDb links>" } and returns the films Qloo knows, by IMDb
ID, plus the ones it left out. At most 30 TMDB calls (5 list pages, then 25 lookups) and one Qloo call; 8 imports a
minute per IP.POST /api/saved: saves { treatment, seal } to the signed-in person's account. The seal must be the server's
own (an HMAC it attached when it built the treatment), at most 100 per person; saving the same take again returns
the same id.PATCH /api/saved/{id} with { "shared": true | false }, and DELETE /api/saved/{id}: the owner's only; anyone
else gets a 404.DELETE /api/account: deletes the signed-in person's account and every treatment they saved, which ends their
share links. Only from the site's own pages (same-origin), and only ever the caller's own account.GET /auth/callback: where Google or GitHub sends someone back after signing in (through Supabase); it only ever
returns them to a path on this site.GET /api/health: makes a real Qloo call and reports mode, host and provenance, plus the LLM chain (never called).GET /api/tools: tool definitions (JSON Schema) with an example input each.POST /api/tools/{name}: runs a tool (development only unless ITSGIVING_TOOL_PLAYGROUND=1).src/lib/qloo: a small Qloo client. GET only, X-Api-Key header, paced under Qloo's 10 calls a second, bounded
retries (a 429 waits out the rest of Qloo's one-second window), response caching, clear error kinds (a 403 for an
unsupported entity type is not reported as a bad key), warnings for parameters Qloo would silently ignore, and
provenance for every request.src/lib/qloo/mock: a fake Qloo server over a small synthetic catalog, so the app and tests run without a key.
Mock responses are labelled; names are real but every score and tag assignment is invented.src/lib/tools: the agent tools described above.src/lib/llm: the provider-neutral JSON generator and fallback chain.src/lib/agent: the brief schema, plan and write prompts (with their deterministic mocks), the pipeline, and the
rate limit, cache and SSE service behind /api/treatment.src/app/studio: the treatment builder UI: intake, live agent timeline with every Qloo request, ambiguity picker,
and the board with "why" popovers that keep Qloo's evidence apart from itsgiving's interpretation.src/lib/supabase, src/lib/saved, supabase/migrations: accounts. Supabase Auth signs people in with Google or
GitHub; the browser reads the session itself, so the static pages stay static, and src/proxy.ts keeps it fresh on
the pages that read it on the server. Saved treatments sit in one table that grants browsers nothing: only the
server reads and writes it, naming the owner in every query./saved) removes the person's sign-in record and everything they saved.x-forwarded-for, which is right on Vercel but spoofable behind other proxies, and identical cold briefs arriving
at once each run their own pipeline.Plan (LLM call 1). The model picks the creative direction, the tone tags, how deep the cuts go, a few plain
style words for wardrobe and ambience, and which of the strongest areas to base the shoot in. It hears which of the
director's films this audience likes most. Tags and areas are
shown to it as short refs (T1, A1, ...) and the output schema only accepts those refs, so it cannot invent a
tag ID or a neighbourhood.
Execute in code. The style words go through find_tags (preferring tags named like the word, never
adult-themed ones). Three Qloo queries run in parallel; reference films start straight away, since they don't
use the tags:
reference_films: the artist's audience, with a popularity ceiling for deep cuts and an era only when the
director asks for "more 90s"; once for films and once, beside it, for series.styling_brands: fashion brands only (urn:tag:genre:brand:fashion), ranked by the wardrobe style tags.scout_locations: within 2 km of the chosen base, the venue kinds the plan chose (cafe, bar, bookstore, park
and so on, mapped to Qloo place genres), ranked by the ambience tags, within the budget's price level, with photos.Empty or failed results are relaxed deterministically (widen from the base to the whole city when fewer than three places match near it, widen the venue kinds, allow places without photos, lift the price ceiling, drop style tags, lift the deep-cut ceiling), and the treatment says what was relaxed.
Re-plan (optional LLM call 2) only when a whole set of style words matched no Qloo tag.
Write (last LLM call). The model writes the treatment as JSON, citing films, series, actors, brands and
places only by ref (R1, S1, P1, B1, L1), and for a short film its music (M1); refs not returned by a
tool fail validation. It sketches 2-4 characters (who they are, what they want and hide, how they move and
speak, their look, where we meet and leave them, the beats they're in), each drawn from a character TMDB credits in
one of the first films or series (C1, listed under each title with Qloo's archetypes and traits for it), and each
given a different actor as a reference for the part, written about the part, never the person. A song may be named
only from Qloo's list of that artist's notable songs, and code drops any other. A brand whose pieces start above
what the budget buys is written as a reference to rent or match, and a place's opening hours can set when a scene
is shot. Code maps refs back to Qloo items and attaches each
item's evidence (rank within its call, affinity, popularity, the input entities that drove it) and the model that
wrote it. A ref that slips into the prose is swapped for its name, and lighting or shot notes sent as objects are
flattened, so neither costs a repair call. TMDB gives each reference its still (its backdrop, by IMDb ID) and its
top-billed characters, looked up as soon as Qloo's films land: the cover and the reference cards show film frames,
not posters, whose own lettering would fight the treatment's title. A TMDB failure costs a still, a character, a
role or a photo, never the treatment.
/s/<id>), with accounts set up: Save on a studio board keeps the treatment in the director's
account (signing in with Google or GitHub first if needed, then coming back to the same board, saved). Its page is
private until its owner turns on the share link; then anyone with the link reads it (one address can open 60 saved
treatments a minute), like an example, and its rework
chips take it to the studio as their own take. Your treatments (/saved) lists them, and at its foot deletes
the account along with everything saved in it.SUPABASE_SECRET_KEY | With the two above: the project's secret key, used only on the server, to read and write saved treatments and to seal each treatment it builds so only those can be saved |