AI and prompts
AI providers
Every way CourseForge can reach a model - Anthropic, OpenAI, Gemini, OpenRouter, sixteen presets, any OpenAI-compatible endpoint or a local server - and what each one does differently.
CourseForge reaches a model through an AI account on a profile. There are five types: four native adapters and one for everything else that speaks OpenAI's /chat/completions.
| Type | Endpoint | Batch queue | Credential |
|---|---|---|---|
| Anthropic API | POST /v1/messages |
yes, Message Batches | sk-ant- key |
| OpenAI | POST /v1/chat/completions |
yes, file upload | API key |
| Google Gemini (beta) | POST /v1beta/models/{model}:generateContent |
yes, paid tier only | Gemini API key |
| OpenRouter | POST /api/v1/chat/completions |
yes, /api/beta/batches |
OpenRouter key |
| A preset or a custom endpoint | POST {base}/chat/completions |
if the endpoint has one - asked, not assumed | API key, or none for a local server |
All of them work on an ordinary web host.
Why four native adapters#
Because all four providers report at least one kind of failure with an HTTP 200, and in every case the body would otherwise be stored as a course page. A blank page, or one that stops mid-sentence, looks like work that succeeded. Each adapter knows its provider's version of:
- A refusal or a safety block - Anthropic answers one at 200; Gemini returns no candidates and a
blockReason. - An answer cut off by the output limit -
stop_reason,finishReason: MAX_TOKENS(orRECITATION, frequent for long educational prose),finish_reason: length. Every one arrives with most of a lesson attached. - An upstream error reported at 200 by a gateway that fans out.
- A routed provider that failed after it started - OpenRouter commits the 200 as soon as the model starts producing.
Every one of those raises an error with the provider's reason instead of storing half a page, on the live path and the batch path alike.
Anthropic#
- Speaks API version
2026-10-21, which refusestemperature,top_pandtop_kon every model, so none is sent: the profile's temperature does not apply to Claude. - What differs between models is read from the models endpoint's
capabilitiesobject - whether the batch queue takes a model and how long an answer it can produce - never from the model's name. The list is cached for a day. When the endpoint cannot be reached, the picker falls back toclaude-opus-5-5,claude-sonnet-5-5andclaude-haiku-5-5. - The shared part of every page prompt carries cache breakpoints, so pages after the first read it from the prompt cache. A one-off request (an outline, a scope plan) gets none, because a cache write costs a quarter more and would never be read. Batches use the one-hour cache. See Tokens and caching.
- Results of large batches are streamed line by line, so a gigabyte of prose never has to fit in PHP's memory.
OpenAI#
- Reasoning models - the GPT-5 and GPT-6 families (
gpt-6-sol,gpt-6-luna,gpt-6-astra,gpt-6.1-sol,gpt-6.1-terra…) and the o-series - takemax_completion_tokensand refusetemperature,top_p, the penalties,logit_biasandlogprobs. CourseForge decides that from the model id, because OpenAI's model list carries no capability data; a model released after that rule was written costs one retry, because a 400 that blames a parameter is retried once without it. - The model picker is curated -
/v1/modelsmixes embeddings, audio, images and every fine-tune in with the chat models - but only when the address really isapi.openai.com. - Prompt caching:
prompt_cache_keykeeps a course's pages where its cache is warm. Ongpt-5.6and later, which bill cache writes, CourseForge sets explicit cache points behind each shared part so only the shared part is written.
Google Gemini#
Warning
Gemini is offered as beta because Google is migrating API keys. Unrestricted standard keys are already rejected, standard keys were expected to stop working altogether in September 2026, and their replacement - the service-account-bound key with an AQ. prefix - has an open cluster of reports of answering 401 ACCESS_TOKEN_TYPE_UNSUPPORTED. So the adapter never collapses a Gemini error into "authentication failed": it shows Google's own error envelope verbatim, which is the only way to tell a wrong key from a key type the endpoint will not take.
- Not OpenAI-shaped in any particular: the model is in the URL path, authentication is an
x-goog-api-keyheader, the system prompt is a top-levelsystemInstruction, and answers come back incandidates[]. - The batch queue is paid-tier only and accepts a published list of models, including
gemini-4-argonandgemini-3.8-flash. A batch still running after 48 hours expires with no results at all. - Caching is implicit: the shared part of every prompt opens the system instruction, which is what Gemini's cache keys on (4,096 tokens minimum on current models).
OpenRouter#
- One key for every vendor OpenRouter fronts. Model ids carry a vendor prefix (
anthropic/claude-opus-5); routing suffixes such as:freeor:nitroare typed in by hand rather than offered in the picker. - Sixty-odd real OpenRouter slugs end in
:batch, which collides with CourseForge's marker. The adapter splits a slug on its last colon and always sends the plain one upstream, so both readings agree. - Batches go to the beta endpoint, which has no cancel route: a stopped run there stays open until the provider is done.
- Caching:
cache_controlmarkers for Claude, Gemini and Qwen upstreams, andprompt_cache_keyfor every model, which OpenRouter also uses to keep a course on one upstream provider.
The presets#
Many gateways claim OpenAI compatibility, and the claim is true about the chat body. Everything around it differs - Groq hangs its API under /openai/v1 and rejects logprobs, DeepInfra lives at /v1/openai, Z.ai has no /v1 at all, Ollama takes any key or none. That is configuration, so it is one class and a table: picking a preset fills in the base URL, the parameters that gateway refuses, what its output limit is called and whether it might have a queue.
| Preset | Base URL | Batch queue | Key | Notes |
|---|---|---|---|---|
Groq (GroqCloud)groq |
https://api.groq.com/openai/v1 |
yes | yes | Fastest inference; 50% batch discount, 7-day window. Model ids churn - never hardcode. |
DeepSeekdeepseek |
https://api.deepseek.com/v1 |
probed per key | yes | Very cheap long-context drafting. Off-peak pricing; check the queue probe result. |
Together AItogether |
https://api.together.ai/v1 |
probed per key | yes | Large open-weight catalogue. Its queue takes 50,000 requests and a 100 MB file, and uploads to its own address; the probe still decides whether this key can reach it. |
Fireworks AIfireworks |
https://api.fireworks.ai/inference/v1 |
probed per key | yes | Open-weight hosting; model ids contain slashes. |
xAI (Grok)xai |
https://api.x.ai/v1 |
probed per key | yes | Grok models via an OpenAI-shaped endpoint. |
Mistral AImistral |
https://api.mistral.ai/v1 |
no | yes | Sync only in 4.0 - Mistral has a queue, but not an OpenAI-shaped one. |
Cerebrascerebras |
https://api.cerebras.ai/v1 |
probed per key | yes | Very high token throughput, small catalogue. |
DeepInfradeepinfra |
https://api.deepinfra.com/v1/openai |
probed per key | yes | Cheap open-weight hosting. |
Nebius AI Studionebius |
https://api.studio.nebius.com/v1 |
probed per key | yes | EU-hosted open-weight models. |
Moonshot (Kimi)moonshot |
https://api.moonshot.ai/v1 |
probed per key | yes | Kimi K-series; very large single-response output budgets. |
Z.ai (GLM)zai |
https://api.z.ai/api/paas/v4 |
probed per key | yes | GLM family at 1M context, aggressive pricing. |
Ollama (local)ollama |
http://localhost:11434/v1 |
no | no | Local models. WATCH THE CONTEXT WINDOW: the OpenAI shim cannot set num_ctx, so a long course prompt can be silently truncated by the server default - raise OLLAMA_CONTEXT_LENGTH on the host. |
LM Studio (local)lmstudio |
http://localhost:1234/v1 |
no | no | Local desktop server. Start the server in LM Studio first; first request may block while the model loads - use a long cURL timeout. |
vLLM (self-hosted)vllm |
http://localhost:8000/v1 |
no | no | Self-hosted GPU serving. Model id must match the server exactly. |
llama.cpp server (local)llamacpp |
http://localhost:8080/v1 |
no | no | Bare-metal local inference. One model per server process. |
LiteLLM proxy (self-hosted)litellm |
http://localhost:4000/v1 |
probed per key | no | Your own gateway in front of many providers. Probe finds the queue. |
Custom OpenAI-compatible endpointcustom |
you type it | probed per key | yes | Paste any base URL ending in /v1. CourseForge will probe it for a model list and a batch queue without spending a cent. |
The local presets never ask for a key and always let you type a model id, because what such a server lists is only advisory: vLLM serves one model and the id must match --served-model-name, LM Studio reports local file names, llama.cpp often reports a placeholder, and a LiteLLM proxy reports the aliases in its own config.
Caution
Ollama's OpenAI endpoint cannot set the context length, so a long course prompt can be silently truncated to the server's default. Raise OLLAMA_CONTEXT_LENGTH on the machine running Ollama.
Custom is the escape hatch: any base URL, with nothing assumed. Only the Groq row was verified against live documentation; for the rest, the probe below confirms what an endpoint really does before any badge claims it.
Asking an endpoint whether it has a queue#
Rather than keep a list of which gateways have a batch API, CourseForge asks: three GETs, and where the answer is still ambiguous one POST of an empty object that every real queue rejects with a validation error. Nothing is created and no chat request is made - which matters for local servers, where naming a model that is not loaded can block for minutes while gigabytes load.
The answer belongs to the account, not the preset, because the same address behaves differently per key. It is stored on the account and taken again when the key or address changes, when it is a month old, or when a real submission proves it wrong. Two verdicts earn the feature its keep: a queue with no upload lane (Gemini's compatibility layer answers /batches and 404s /files), and forbidden, as distinct from absent (Gemini's queue is paid-tier only, and a 403 must not disable batching for an account that only needs an upgrade).
Choosing models#
- Outline slot: your strongest model. It is one request per course, and every page inherits its quality through the scopes.
- Page slot: a capable mid-range model is usually the best trade. Try three pages in the tab before starting a run of three hundred.
- Batch: if you can wait a day, the batch switch halves the price of the page slot on every provider that has a queue.
- Local: a local model is free per token and fine for drafting outlines; make sure its context window holds the course map.
Not supported#
- AWS Bedrock, Google Vertex AI and Azure have no account type: each uses an authentication scheme of its own rather than a bearer key. Put a LiteLLM proxy in front of them and use the
litellmpreset. - Streaming and webhooks - see Runs.
- Mistral's own batch queue, which is not OpenAI-shaped. Mistral is live-only.
- A Claude Pro/Max subscription as an AI account. Releases 4.0-5.0 offered one, which ran the
claudeCLI on the same machine; it only ever worked on a laptop and was retired in 5.1. To use a subscription with a hosted CourseForge, turn it around: connect the Claude app or Claude Code over MCP and let it do the writing.