Quiz - When AI features earn their weight
A teammate ships two “read a document and pull out structure” features in the same sprint: one sorts each expense into one of the four tax categories the accountant defined up front, the other pulls the renewal date and party names out of an arbitrary uploaded vendor contract. One earns an LLM, one doesn’t. What’s the deciding question that separates them?
switch/classifier), but a contract is open-ended text you have to actually read (extraction — Trigger 3).switch or a tiny classifier — a general LLM is wildly overpowered. The contract is genuinely open-ended prose that requires understanding language, which is the extraction trigger. Structured output is true of both, so it can’t be the discriminator.You’re scanning a tutorial to decide whether it’s current for your v5 stack. Which signs tell you it was written against the outdated AI SDK v4? Select all that apply.
.content string instead of a parts array on UIMessage.append and reload to send and retry messages.stopWhen(stepCountIs(n)).useState..content and append/reload are the v4 fingerprints — they were replaced by the parts array and sendMessage/regenerate. The other two (stopWhen and manually managed input) are the current v5 shapes, so seeing them is a sign the material is up to date. You don’t need to know the v5 APIs in depth yet, only to recognize the stale shapes so a v4 tutorial sets off the alarm.Your LLM route already reads usage in onFinish, bumps the per-user counter, and caps maxOutputTokens. Why does the lesson still insist on a pre-call input estimate-and-reject on top of all that?
onFinish doesn’t fire on an aborted stream, and the input cap defends a different attack than the output cap — so the post-call ledger alone leaves a hole an adversary can drive through by aborting mid-stream.usage read once it’s in place.onFinish never fires on an aborted stream, so a “start a huge generation, abort just before it finishes” loop leaks tokens your ledger misses — the pre-call estimate plus maxOutputTokens bound the worst case regardless of how the call ends.A user fires 30 requests a second at the chat box; a different user paces one request every few minutes all day to stay under the radar. Your daily token quota is in place. What does the lesson say you need?
rl:llm vs quota:llm:...:yyyymmdd).You’re setting the daily token allowance for the invoice chat. Where should the number come from?
getEntitlement(orgId)) — sourcing it from the plan makes the cost ceiling and the pricing lever the same number (“Free: 50 questions/day”).getEntitlement(orgId) means Free, Pro, and Enterprise each get their own limit from the single source of truth you already own for plan capabilities — and the same number that guards against abuse becomes a line on the pricing page. A hardcoded ceiling throws away that pricing lever and treats every plan the same.Setting maxOutputTokens: 4000 on a surface that only ever returns a one-word classification — is that a safe default?
maxOutputTokens is sized to the surface’s worst useful case, not a generic ceiling. A 4,000-token cap on a one-word answer is as wrong as no cap — “ignore the question and write 4,000 tokens” now succeeds right up to your headroom. The cap is part of each surface’s spec, decided per surface like its schema, and a missing or oversized cap is a cost bug in the same severity class as a skipped auth check.You centralize every model string into lib/llm/models.ts and export handles named gpt5ForChat and claudeSummarizer. The day you move chat from OpenAI to Anthropic, why is this naming still going to bite you?
gpt5ForChat now points at Claude and is a lie. You either rename it across every import — the grep you were escaping — or leave a misleading name forever.chatModel, summarizerModel, fastModel) — a capability is stable across a swap, so only the right-hand string moves and the name stays honest. Vendor-named handles reintroduce the very codebase-wide grep the central file was meant to delete.Swapping smartModel to a different vendor is a one-line edit in models.ts. Is swapping embeddingModel the same kind of one-line change?
embeddingModel handle is a one-way commitment until you re-embed the whole corpus: same file, same-looking line, wildly different blast radius.Your prototype runs LLM calls through plain 'creator/model' strings — which already route through the AI Gateway by default. When does the lesson say to actually configure the gateway for production (failover, dashboards) rather than leave the bare default?
Quiz complete
Score by topic