A photo feature that costs too much
A team adds "ask about your photo" to an app. In a week the bill is high, a blind user cannot use it, and somebody asks why a face was stored for three months.
Pictures, pages and sound cost more than text, can leave people out, and can hold personal data. Plan for all three.
By the end of this lesson
Note
I can price a day of picture, page and sound inputs and flag the ones that need care.
Key words
| Word | What it means |
|---|---|
| Alternative text | words that stand in for a picture for a person who cannot see it |
| Transcript | the words of a sound, written down |
| Personal data | facts that can identify a person, such as a face or a voice |
| Retention | how long data is kept |
| Budget | the most you plan to spend |
The cells students fill in are left blank in this preview.
Pictures, pages and sound are priced by size
Text is priced by its tokens (see the earlier module on cost and tracing). A picture is priced by its tiles, a page by its pages and a sound by its seconds.
In this lesson every request costs 1. A picture adds 20 and 10 a tile, a page adds 5 each, and sound adds 2 for every 10 seconds. These are invented units. Always read a real price list.
Pricing four inputs
| Input | Size | Working | Cost |
|---|---|---|---|
| Image | 6 tiles | 1 + 20 + 60 | 81 |
| Page | 4 pages | 1 + 20 | 21 |
| Audio | 25 seconds | 1 + 2 x 3 | 7 |
| Audio | 10 seconds | 1 + 2 x 1 | 3 |
The cells students fill in are left blank in this preview.
Access: who does the feature leave out?
A person who cannot see a picture needs alternative text. A person who cannot hear a sound needs a transcript. If a feature gives neither, it leaves those people out.
A model can often write both. But a person should check them, as the last lesson showed. Plan for the words to exist before a picture or a sound is stored.
A common mistake
Warning
Many people think a face or a voice is only personal data if the person is named. A face or a voice can identify a person even with no name beside it. Treat both as personal data, and keep them no longer than you need.
Write the model helper, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' '// The token counts of a reply, or null when the reply carries none.' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > model.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the model helper, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"status \" + res.status);' ' const data = await res.json();' ' const text = data.choices[0].message.content;' ' return { text, inTokens: count(data, \"prompt_tokens\"), outTokens: count(data, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"Recorded sample: no model answered (\" + error.message + \").\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' 'module.exports = { ask };' >> model.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the pricing script, part 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const items = [' ' { id: 1, kind: \"image\", tiles: 6, alt: true, face: false, voice: false, days: 7 },' ' { id: 2, kind: \"image\", tiles: 16, alt: false, face: true, voice: false, days: 90 },' ' { id: 3, kind: \"page\", pages: 4, alt: true, face: false, voice: false, days: 30 },' ' { id: 4, kind: \"audio\", seconds: 25, alt: false, face: false, voice: true, days: 31 },' '];' > cost6.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the pricing script, part 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function price(it) {' ' if (it.kind === \"image\") return 1 + 20 + 10 * it.tiles;' ' if (it.kind === \"page\") return 1 + 5 * it.pages;' ' if (it.kind === \"audio\") return 1 + 2 * Math.ceil(it.seconds / 10);' ' return 1;' '}' >> cost6.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the pricing script, part 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function flags(it) {' ' const out = [];' ' if (it.face) out.push(\"face\");' ' if (it.voice) out.push(\"voice\");' ' if (it.alt === false && it.kind === \"image\") out.push(\"no alternative text\");' ' if (it.alt === false && it.kind === \"audio\") out.push(\"no transcript\");' ' if (it.days > 30) out.push(\"kept too long\");' ' return out;' '}' >> cost6.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Write the pricing script, part 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' let total = 0;' ' for (const it of items) {' ' total += price(it);' ' console.log(it.id + \" \" + it.kind + \": cost \" + price(it) + \", flags \" + JSON.stringify(flags(it)));' ' }' ' console.log(\"total \" + total);' ' const q = \"Write one short notice telling users that their voice notes are deleted after 30 days.\";' ' const reply = await ask([{ role: \"user\", content: q }], \"Your voice notes are deleted after 30 days.\", 40);' ' console.log(\"reply: \" + reply.text);' '}' 'main();' >> cost6.js"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Price a day and flag inputs
docker exec -w /home/node/project agent node cost6.jsShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
The lines it prints
| Line | What it shows |
|---|---|
| 1 image: cost 81, flags [] | A fine input: seen, short-lived |
| 2 image: cost 181 ... | A face, no alternative text and kept 90 days |
| 4 audio: cost 7 ... | A voice, no transcript and kept 31 days |
| total 290 | The four prices added up |
The cells students fill in are left blank in this preview.