Showing models from the providers you've connected in
AI Keys Settings . Add more providers there to unlock more options.
Free-form guest input on a free-tier Gemini key
Google may use guest prompts to train their models. For free-form guest input, consider switching to a paid-tier Gemini key, OpenAI, or Anthropic. You can also change this app to Preset only .
Language Model Off
Image Model Off Anthropic doesn't offer image generation.
Daily Spending Cap $ per day · resets at midnight UTC
When the cap is hit, the app returns a "limit reached" error to visitors until the next day.
How this cap is calculated
Teacher Hive gives you an estimate . Your provider has the final word on actual cost, especially if their prices have changed since we last checked.
See your actual usage at:
Tip: You can also set a spending limit at the provider. Teacher Hive's cap protects this one app; the provider's cap covers your whole account.
Advanced settings
Test AI with this configuration
I confirm this app follows Teacher Hive guidelines and does not collect personally identifiable information. I understand prompts are sent to the selected provider under their privacy policy.
Configuring AI here doesn't put AI into your app's code. Paste these instructions into your AI chat (ChatGPT, Claude, or Gemini) before asking it to build the app, so the code it writes actually uses Teacher Hive's AI calls.
Copy AI Instructions
--- TEACHER HIVE AI CONTEXT ---
You are building a Teacher Hive app that uses AI. Follow these rules exactly.
REFERENCE APP
=============
A complete reference app (Reading Level Analyzer) that uses every best
practice below is at https://teacherhive.app/ai-reference.html — view
source and pattern-match to it when in doubt. The reference is intentionally
minimal to highlight API patterns; your app's UI can be as rich as needed.
SETUP
=====
Put this in the HTML head. Without it, THive is undefined and nothing works:
<script src="https://teacherhive.app/sdk-v1.js"></script>
All AI calls must go inside THive.onReady() and use await. Never use fetch()
or third-party AI libraries. Never embed API keys — the platform handles auth.
CANONICAL EXAMPLE (copy this shape, tune the values)
===================================================
setLoading(true); // AI calls take 3–30s. Apps without visible progress feel broken.
try {
const r = await THive.ai.generateText({
system: "You are a reading-level classifier. Return only valid JSON.",
prompt: "Passage: " + passage,
responseFormat: 'json', // Provider enforces JSON at the API level. No markdown fences, no prose.
temperature: 0.3, // Lower = more deterministic. See cheat sheet below.
maxTokens: 500, // Smallest that reliably fits your output. Don't oversize.
});
if (r.warnings?.length) console.warn('[App]', r.warnings); // surface platform hints in dev.
const data = JSON.parse(r.text);
// VALIDATE SHAPE — responseFormat: 'json' guarantees syntax, NOT that the
// model returned the fields you expected. Check every required field.
if (typeof data.gradeLevel !== 'number' || !Array.isArray(data.summary)) {
throw new Error('Model returned unexpected shape');
}
render(data);
} catch (err) {
showFriendlyMessage(err); // see ERROR HANDLING below
} finally {
setLoading(false);
}
RESPONSE FORMAT
===============
- responseFormat: 'json' — structured output (extraction, classification, scoring, multi-field results).
- responseFormat: 'text' — free-form output (story starters, feedback prose). Also the default — you can omit.
TEMPERATURE CHEAT SHEET
=======================
- 0.2 to 0.3 Extraction, classification, JSON, scoring
- 0.5 to 0.6 Tutoring feedback, explanations
- 0.7 to 0.9 Creative games, writing, titles, silly stories
If unsure and using responseFormat: 'json', pick 0.3.
MAX TOKENS
==========
Size to your expected output. Undersizing truncates mid-response; for
responseFormat: 'json' that's fatal (JSON is invalid, parse fails).
Anchors:
- Short answers / single values: 150
- Paragraph explanations: 800
- Multi-field JSON (typical): 2000
- Long multi-section JSON: 3000+ (may need a higher app ceiling)
For JSON arrays, budget ~100 tokens per item for typical schemas.
The platform surfaces problems: hit the limit and you get a 'truncated'
error (JSON) or r.warnings (text). Clamped by app ceiling → also r.warnings.
EFFICIENCY
==========
- Don't fire generateText on every keystroke. Use a Submit button (or
debounce ~500ms after typing stops) so a typing student makes one AI
call per question, not one per letter.
- Cache identical-input responses on the client so a repeat doesn't
burn a second call:
const cache = new Map();
async function ask(prompt) {
if (cache.has(prompt)) return cache.get(prompt);
const r = await THive.ai.generateText({ prompt });
cache.set(prompt, r);
return r;
}
- Ask AI for multiple things in one call when possible. "Three definitions
and three examples" is one call. Six separate prompts is six.
IMAGE INPUT (vision)
====================
Pass photos, scans, diagrams, or handwriting to generateText for the AI to
describe, count, transcribe, classify, etc. Same call as text — just add
an images array.
// <input type="file" id="photo" accept="image/*"> in your HTML
const file = document.getElementById('photo').files[0];
const r = await THive.ai.generateText({
system: "Transcribe handwritten text exactly as written. Return only the transcription.",
prompt: "Transcribe this student's writing.",
images: [file], // pass the File directly — SDK auto-compresses
});
Rules:
- images is an array of File / Blob / "data:image/..." URI. Max 2 per call.
- Pass the original File from <input type="file">, NOT a URL or a canvas
rendering. The SDK shrinks each image client-side so big phone photos
still work.
- Counting tasks: ask for ranges, not exact counts, when there may be
more than ~10-15 items in dense scenes. Vision models are imprecise
past that threshold.
- Handwriting OCR works best on clear print or neat cursive. Messy or
faded student work returns partial results — handle that gracefully.
IMAGE GENERATION
================
const r = await THive.ai.generateImage({ prompt: "a cartoon bumblebee" });
// r.url is a URL you can use as <img src>. Optional size:
// "1024x1024" (default), "1792x1024", "1024x1792".
// Same error taxonomy as generateText. Prompts are moderated the same way —
// student-generated prompts should be treated as untrusted input.
ERROR HANDLING
==============
try {
const r = await THive.ai.generateText({ /* ... */ });
} catch (err) {
// err.code is one of:
// cap_reached — USER action (daily limit hit; try again tomorrow)
// moderated — USER action (input was flagged; try different text)
// no_key — USER action (teacher needs to connect a provider key in AI Settings)
// truncated — developer (raise maxTokens; JSON response didn't fit)
// invalid_config — developer (app AI config missing or wrong)
// bad_request — developer (invalid arguments; check the call)
// vision_unavailable — developer (sent images to a non-vision model; pick a vision model in AI Settings)
// image_too_large — developer (image exceeded size cap even after compression; pre-shrink before passing)
// rate_limited — transient (brief wait then retry)
// provider_error — transient (network/provider hiccup; one retry is enough)
// Show a friendly message tailored to err.code. Keep the fallback short
// and tell the teacher what to do next, not what went wrong.
}
STUDENT PRIVACY
===============
Teacher Hive apps run in real classrooms with real student data. Do NOT send
personally identifiable student information — names, IDs, IEP details,
grades tied to a named student, contact info — in prompt or system content.
AI providers may log requests. If the app needs per-student features, use a
local alias ("Student A", "Student B") in the prompt and map it back to the
real name client-side.
ANTI-PATTERNS (specifically for AI chatbots writing this code)
==============================================================
DON'T call provider REST APIs directly with fetch(). The teacher's key is
never in your JavaScript — there is no apiKey variable for you to fill.
Patterns to recognize and AVOID:
fetch('https://api.openai.com/v1/chat/completions', ...)
fetch('https://api.openai.com/v1/images/generations', ...)
fetch('https://api.anthropic.com/v1/messages', ...)
fetch('https://generativelanguage.googleapis.com/...?key=' + apiKey, ...)
fetch('https://generativelanguage.googleapis.com/.../imagen-...:predict', ...)
If you are tempted to write any of those, STOP and use THive.ai.* instead.
DO use THive.ai.generateText for text + vision input.
DO use THive.ai.generateImage for image generation.
DON'T regex-clean markdown fences from responses.
DO use responseFormat: 'json' and call JSON.parse directly.
DON'T stuff role instructions into the user prompt.
DO put stable instructions in system.
DON'T write your own retry loop or exponential backoff.
DO trust the SDK's internal retries; at most one extra attempt, for 'internal' / 'provider_error' / 'rate_limited' only. Never retry cap_reached or moderated.
DON'T leave temperature unset for extraction / scoring / JSON.
DO set 0.3 for any structured output.
DON'T rely on partial responses.
DO set generous maxTokens (usually 1500 to 3000) so JSON isn't truncated.
DON'T skip shape validation because JSON mode "guarantees JSON."
DO check every required field after JSON.parse — JSON mode enforces syntax, not schema.
DON'T ship bare await calls with no loading state.
DO show a spinner or "Working…" message. AI calls take seconds, not milliseconds.
DON'T add "is THive loaded?" defensive checks.
DO trust that THive is ready inside THive.onReady().
DON'T list a loose category in the system (e.g. "past-tense verb") and filter
responses against a stricter set (only "verb"). The model will match the
looser system description and your filter will drop everything.
DO list the EXACT allowed values in system, and validate incoming responses.
DON'T fetch a remote image into an <img> and pass the rendered canvas to
images. Canvas re-encoding loses detail and triples the byte size.
DO pass the original File from <input type="file"> straight through.
The SDK shrinks it for you.
OUTPUT FORMAT
=============
Output a COMPLETE standalone HTML file: <!DOCTYPE html> through </html>,
in ONE response. If the app is getting too long, simplify features rather
than truncate. Never split across replies or say "continued".
--- END CONTEXT ---
These instructions are tailored for Use Their AI Key apps. They teach your AI chat to ask each user for their own key and to write prompts that work on Anthropic, OpenAI, or Gemini.
Copy AI Instructions
--- TEACHER HIVE AI CONTEXT (USE THEIR AI KEY) ---
You are building a Teacher Hive app where each user provides their own
AI key (Anthropic, OpenAI, or Gemini). Teacher Hive's SDK shows the key
entry dialog, validates the key, and stores it locally on the user's
device. You don't handle keys directly — you just call the AI helpers.
Follow these rules exactly.
SETUP
=====
Put this in the HTML head. Without it, THive is undefined and nothing works:
<script src="https://teacherhive.app/sdk-v1.js"></script>
THE PLATFORM CALLS THE PROVIDER, NOT YOUR CODE. There is NO apiKey
variable in your code. There is NO fetch() to api.openai.com,
api.anthropic.com, or generativelanguage.googleapis.com. Every AI
request goes through THive.ai.* methods (generateText, generateImage).
The SDK reads the user's stored key, attaches it server-side, and
returns the result. If you write fetch() to a provider URL, the call
WILL fail with auth errors because the user's key never reaches your
JavaScript — by design, for safety.
HOW THE KEY DIALOG WORKS
========================
Just call THive.ai.generateText() (or generateImage) when the user clicks
a button. The first time it runs without a saved key, the SDK opens a
dialog that lets the user pick a provider and paste a key. Once they do,
the SDK automatically retries the original call. Returning users with a
saved key see no dialog — the call goes straight through.
DO NOT prompt the user for their AI key on app load. Show your app's UI
first so they know what they're getting into. The key dialog should appear
only when the user actually triggers an AI action.
(Advanced: If you want to add a "Connect AI" button somewhere in your UI
that opens the dialog explicitly, call THive.ai.requireGuestKey(). Most
apps don't need this — the auto-prompt on first generateText is enough.)
CANONICAL EXAMPLE (copy this shape, tune the values)
===================================================
THive.onReady(async () => {
// ...your app's UI renders normally, no AI dialog yet...
document.querySelector('#analyze').addEventListener('click', async () => {
setLoading(true);
try {
const r = await THive.ai.generateText({
system: "You are a reading-level classifier. Return only valid JSON.",
prompt: "Passage: " + passage,
responseFormat: 'json', // Provider enforces JSON. No markdown fences.
temperature: 0.3, // Lower = more deterministic. See cheat sheet.
maxTokens: 500, // Smallest that reliably fits your output.
});
if (r.warnings?.length) console.warn('[App]', r.warnings);
const data = JSON.parse(r.text);
if (typeof data.gradeLevel !== 'number') {
throw new Error('Model returned unexpected shape');
}
render(data);
} catch (err) {
showFriendlyMessage(err);
} finally {
setLoading(false);
}
});
});
PROVIDER PORTABILITY (important)
================================
Each user of this app picks their own provider, so your prompts run on
Anthropic, OpenAI, AND Gemini. Write portable prompts:
- Use plain English instructions in system. Do not use Claude-style XML
tags, OpenAI-specific function-calling, or Gemini-specific function args.
- Output schemas: stick to JSON via responseFormat: 'json' (works on all
three) and validate field shape after parse. Don't rely on quirks like
Claude's markdown indentation or GPT's exact word-choice.
- Don't depend on context-window length. Anthropic Claude Haiku has a
200K window; GPT-4o-mini has 128K; Gemini Flash has 1M. Stick well under
100K to be safe across all three.
- Keep prompts under ~2000 input tokens unless you genuinely need more.
Some users will be on cheaper plans with stricter rate limits.
OFFER A "MANAGE AI KEY" CONTROL
===============================
Optionally, add a small "Settings" or "AI key" button somewhere in your
UI that calls THive.ai.openKeyManager(). This lets the user replace or
remove their key without leaving your app. Not required, but nice.
<button onclick="THive.ai.openKeyManager()">AI key…</button>
IMAGE INPUT (vision)
====================
You CAN pass photos, scans, diagrams, or handwriting to generateText. The
user's provider does the vision work using their own key — no extra setup.
// <input type="file" id="photo" accept="image/*"> in your HTML
const file = document.getElementById('photo').files[0];
const r = await THive.ai.generateText({
system: "Transcribe handwritten text exactly as written.",
prompt: "Transcribe this writing.",
images: [file], // pass the File directly — SDK auto-compresses
});
Rules:
- images is an array of File / Blob / "data:image/..." URI. Max 2 per call.
- All three providers (Anthropic, OpenAI, Gemini) support vision on the
default models the SDK picks, so vision works regardless of which key
the user pasted.
- Counting tasks lose accuracy past ~10-15 items. Ask for ranges instead
of exact counts in dense scenes.
- Handwriting OCR works best on clear print or neat cursive.
IMAGE GENERATION (provider-dependent)
=====================================
THive.ai.generateImage({ prompt }) works for users on OpenAI (DALL-E)
and Gemini paid (Imagen). It does NOT work for Anthropic / Claude —
Claude has no image-generation API.
CANONICAL IMAGE GEN EXAMPLE (copy this shape, do not invent your own)
====================================================================
THive.onReady(async () => {
// Optional gate so the button is obviously disabled on Claude keys.
// Without this, a Claude user clicking the button gets the SDK's
// "switch to OpenAI or Gemini" toast — also fine, just less polished.
await THive.ai.requireGuestKey();
if (THive.ai.connectedProvider() === 'anthropic') {
makeBtn.disabled = true;
makeBtn.title = "Image creation needs an OpenAI or Gemini key";
}
makeBtn.addEventListener('click', async () => {
setLoading(true);
try {
const r = await THive.ai.generateImage({
prompt: "a cartoon bumblebee",
// size: "1024x1024", // optional. Also "1792x1024" or "1024x1792".
});
// r.url is a URL or "data:image/..." URI — drop it straight into <img src>.
imgEl.src = r.url;
} catch (err) {
showFriendlyMessage(err); // see ERROR HANDLING below
} finally {
setLoading(false);
}
});
});
There is no fetch() in this example. There is no apiKey variable. If
your code has either of those, you are not using the platform — you are
working around it, and your code will fail with auth errors.
Free-tier Gemini keys can't make images either (Imagen requires a billed
Google account). Those calls return err.code === 'provider_error' with
Google's own upgrade message.
RESPONSE FORMAT
===============
- responseFormat: 'json' — structured output (extraction, classification, scoring, multi-field).
- responseFormat: 'text' — free-form output (story starters, feedback prose). Default.
TEMPERATURE CHEAT SHEET
=======================
- 0.2 to 0.3 Extraction, classification, JSON, scoring
- 0.5 to 0.6 Tutoring feedback, explanations
- 0.7 to 0.9 Creative games, writing, titles, silly stories
If unsure and using responseFormat: 'json', pick 0.3.
MAX TOKENS
==========
Size to your expected output. Undersizing truncates mid-response; for
responseFormat: 'json' that's fatal. Anchors:
- Short answers / single values: 150
- Paragraph explanations: 800
- Multi-field JSON (typical): 2000
- Long multi-section JSON: 3000+
EFFICIENCY
==========
- Don't fire generateText on every keystroke. Use a Submit button (or
debounce ~500ms after typing stops) so a typing student makes one AI
call per question, not one per letter.
- Cache identical-input responses on the client so a repeat doesn't
burn the user a second call:
const cache = new Map();
async function ask(prompt) {
if (cache.has(prompt)) return cache.get(prompt);
const r = await THive.ai.generateText({ prompt });
cache.set(prompt, r);
return r;
}
- Ask AI for multiple things in one call when possible. "Three definitions
and three examples" is one call. Six separate prompts is six.
ERROR HANDLING
==============
try {
const r = await THive.ai.generateText({ /* ... */ });
} catch (err) {
// err.code is one of:
// user_cancelled — user closed the connect dialog without entering a key.
// Show a friendly "AI not connected" message; the
// dialog reopens automatically on their next click.
// moderated — USER action (input was flagged; try different text)
// truncated — developer (raise maxTokens; JSON response didn't fit)
// invalid_config — developer (app AI config missing or wrong)
// bad_request — developer (invalid arguments; check the call)
// image_too_large — developer (image exceeded size cap; pre-shrink before passing)
// image_gen_unavailable — USER action (their key's provider doesn't support image generation;
// switch from Anthropic to OpenAI or Gemini paid). Only from generateImage.
// rate_limited — transient (brief wait then retry)
// provider_error — transient (network/provider hiccup; or free-tier Gemini for image gen)
}
USER PRIVACY
============
Apps in this mode are typically used by other teachers, not students. Even
so, do not send personally identifiable student or staff information in the
prompt or system content. AI providers log requests under the user's own
account.
ANTI-PATTERNS (specifically for AI chatbots writing this code)
==============================================================
DON'T call provider REST APIs directly with fetch(). The user's key is
never in your JavaScript — there is no apiKey variable for you to fill.
Patterns to recognize and AVOID:
fetch('https://api.openai.com/v1/chat/completions', ...)
fetch('https://api.openai.com/v1/images/generations', ...)
fetch('https://api.anthropic.com/v1/messages', ...)
fetch('https://generativelanguage.googleapis.com/...?key=' + apiKey, ...)
fetch('https://generativelanguage.googleapis.com/.../imagen-...:predict', ...)
If you are tempted to write any of those, STOP and use THive.ai.* instead.
DO use THive.ai.generateText for text + vision input.
DO use THive.ai.generateImage for image generation.
DON'T build your own key input form or store keys in localStorage.
DO let the SDK show its built-in connect dialog when generateText runs.
DON'T prompt for the key at app load.
DO let the user see your app first; the dialog appears on the first AI click.
DON'T regex-clean markdown fences.
DO use responseFormat: 'json' and JSON.parse directly.
DON'T stuff role instructions into the user prompt.
DO put stable instructions in system.
DON'T write your own retry loop.
DO trust the SDK's retries; at most one manual retry for 'rate_limited' or 'provider_error'.
DON'T leave temperature unset for extraction / scoring / JSON.
DO set 0.3 for structured output.
DON'T skip shape validation because JSON mode "guarantees JSON."
DO check every required field after JSON.parse.
DON'T ship bare await calls with no loading state.
DO show a spinner or "Working…" message.
DON'T use Claude-specific XML tags or GPT-specific function calls.
DO write plain-English prompts that work on any provider.
DON'T expose the image-generation button without first calling
THive.ai.connectedProvider() — Claude users will hit a wall.
DO disable / hide the image button when connectedProvider() === 'anthropic'.
OUTPUT FORMAT
=============
Output a COMPLETE standalone HTML file: <!DOCTYPE html> through </html>,
in ONE response. If the app is getting too long, simplify features rather
than truncate. Never split across replies or say "continued".
--- END CONTEXT ---