builtin-llm-models
Reference for Manus's built-in LLM catalog and how to call it correctly. Read this BEFORE writing any script that calls LLMs in bulk (summarization, translation, classification, extraction, deep analy
SKILL.mdskills/builtin-llm-models/SKILL.md
GitHubBuilt-in LLM Models
For the commands below, replace <skill-dir> with the absolute directory containing this loaded SKILL.md, using its actual read path or the current runtime Skill catalog locator. Keep the quotes, including when the path contains spaces. Use a path accessible on the computer executing the command; do not derive it from the shell working directory or assume an installation root. Bundled documentation links resolve from the file containing the link.
Manus ships an OpenAI-compatible LLM proxy. These entry points share the same model catalog:
| Context | How to call | Credentials |
|---|---|---|
| Scripts on any Manus computer (Sandbox, local computer, Cloud Computer) | manus-node with the bundled lib/llm.mjs and scripts/*.mjs |
Automatic |
| Sandbox only: Python | openai Python SDK with OpenAI() |
OPENAI_API_KEY, OPENAI_API_BASE (preconfigured) |
| Webdev project (server-side) | invokeLLM / listLLMModels from server/_core/llm |
BUILT_IN_FORGE_API_URL, BUILT_IN_FORGE_API_KEY |
All speak the OpenAI Chat Completions schema. Do NOT hardcode the model list from memory — it changes. Always pull the live catalog (Step 1) before relying on any specific ID, price, or capability.
Scripts on any Manus computer. manus-node is supplied by Manus on the local computer, Cloud Computer and Sandbox. It requires no npm install or user Node setup. Authentication is automatic; never read, print, copy or manually pass tokens. If manus-node or the runtime is unavailable, report that the Manus runtime needs updating. Run scripts with a literal absolute script path (keep the quotes; not a shell variable) or an explicit execution directory, so Manus can inspect the script before it runs.
On a local computer or Cloud Computer, use only the manus-node path. Python, the openai package and curl may be missing there, and any OPENAI_API_KEY / OPENAI_API_BASE variables belong to the user's own provider account, not to Manus — do not use them. LLM calls require network access: when using exec on a local computer, set network: true so Manus can request the normal network permission.
Step 1 — Always fetch the live catalog first
The catalog (IDs, pricing, capabilities.thinking_param, capabilities.thinking_example)
is the source of truth. Snapshots in this skill go stale; the API does not.
Use only model IDs listed by the live catalog. Unlisted IDs may still answer for
backward compatibility, but they are retired and must not be chosen.
Any Manus computer:
manus-node "<skill-dir>/scripts/models.mjs"
Sandbox alternative:
curl -s "$OPENAI_API_BASE/models" -H "Authorization: Bearer $OPENAI_API_KEY" | python3 -m json.tool
Webdev (project shell, or listLLMModels() at runtime):
curl "$BUILT_IN_FORGE_API_URL/v1/models" -H "Authorization: Bearer $BUILT_IN_FORGE_API_KEY"
Model Catalog Snapshot (2026-10-12)
This is a snapshot. Always re-verify against the live /models endpoint because IDs, prices, and capabilities change over time. All models below support tools, vision, JSON-schema structured output, and thinking/reasoning; none expose streaming on the proxy (supports_streaming: false). Pricing is USD per 1M tokens (input / output); the catalog also reports input_credits_per_1m_tokens / output_credits_per_1m_tokens.
| Model ID | Owner | In / Out ($/1M) | thinking_param | Best for |
|---|---|---|---|---|
gpt-5-nano |
openai | 0.05 / 0.40 | reasoning |
Cheapest/fastest; huge-volume lightweight tasks |
gpt-5-mini |
openai | 0.25 / 2.00 | reasoning |
Default cheap+fast workhorse for most bulk jobs |
gpt-5 |
openai | 1.25 / 10.00 | reasoning |
Flagship reasoning + coding |
gpt-5.5 |
openai | 5.00 / 30.00 | reasoning |
Hardest reasoning/generation workloads |
gemini-3-flash-preview |
0.50 / 3.00 | thinking |
Fast long-context + multimodal at low cost | |
gemini-3.1-pro-preview |
2.00 / 12.00 | thinking |
Flagship long-context + multimodal reasoning |
Claude models are retired from the live catalog at this snapshot; do not use them. The Claude request shapes below apply only if the live catalog lists Claude IDs again.
Step 2 — Pick a model
Decision order for typical Manus tasks:
- Translation / summarization / classification / tagging / simple extraction at scale →
gpt-5-mini. Drop togpt-5-nanoonly if volume is very large and per-item quality tolerance is loose. Skip thinking for these (adds cost and latency with little gain). - Nuanced analysis, code generation, structured reasoning over each item →
gpt-5, optionally with a modest reasoning effort. - LLM-as-judge / evaluation, multi-step agentic reasoning, hardest problems →
gpt-5.5orgemini-3.1-pro-preview. Use real reasoning here. - Very long inputs or image/PDF/audio/video inputs at low cost →
gemini-3-flash-preview(large context, multimodal, cheap), upgrade togemini-3.1-pro-previewwhen quality matters.
If the live catalog lists Claude models, claude-sonnet-* fits item 2 and claude-opus-* fits item 3.
Cost-aware batching pattern
For large batches, run a cheap model first (e.g. gpt-5-nano/gpt-5-mini), apply a programmatic quality gate (schema validation, length checks, confidence field), then re-run only the failures on a stronger model. This typically cuts spend by an order of magnitude versus running everything on a premium model.
Mapping models to the map tool (wide / parallel research)
When using Manus's map tool for homogeneous parallel subtasks, the subtasks themselves run as full agents; you generally do not pick a proxy model there. Use this skill's scripts/llm_batch.mjs (or direct client calls) when YOU are writing a script that fans out raw LLM calls and needs explicit model/cost control.
Step 3 — Call it with the correct request shape
The proxy mirrors OpenAI Chat Completions, but provider-specific behaviors (like extended thinking, reasoning effort, and strict JSON schemas) differ by family.
🚨 The Max-Token Trap (Crucial — differs by family)
When a model reasons/thinks, it consumes tokens before generating the final answer.
- GPT: Use
max_completion_tokensto cap visible output while leaving reasoning unconstrained. - Gemini: Use
max_tokens(e.g.max_tokens: 16384). Do NOT usemax_completion_tokens— on the current proxy it causescontent: nullwithfinish_reason: "length"regardless of budget size. Omitting the parameter entirely also works (the model will use its default limit). - Claude (when listed): Use
max_tokens, and it must be strictly greater thanbudget_tokens(e.g.budget_tokens: 2048requiresmax_tokens: 2049or higher).
A. JavaScript with manus-node (every Manus computer)
Write a .mjs script, import the bundled client by absolute path with pathToFileURL (portable, including Windows drive paths), and run it with manus-node "/absolute/path/script.mjs". The client sends the request body as-is: provider-specific keys (reasoning, thinking, reasoning_effort) go at the top level of the body.
A1. Basic chat
import { pathToFileURL } from 'node:url'
const { LlmClient, messageText } = await import(pathToFileURL('<skill-dir>/lib/llm.mjs').href)
const llm = new LlmClient()
const resp = await llm.chat({
model: 'gpt-5-mini',
messages: [
{ role: 'system', content: 'You are a precise translator.' },
{ role: 'user', content: 'Translate to French: Good morning.' },
],
})
console.log(messageText(resp))
console.log(resp.usage.prompt_tokens, resp.usage.completion_tokens)
new LlmClient({ timeoutMs, maxRetries }) defaults to a 300 s timeout and 3 retries. The client retries only transient failures (network errors, HTTP 408/409/429/5xx, proxy rate limits). Proxy errors can arrive as HTTP 200 with an error field; the client throws them as LlmError. error.fatal (runtime not ready, invalid session, insufficient credits, unsupported model, blocked user) means stop the whole batch and report it — retrying will not help.
A2. Thinking / reasoning (family-specific)
The catalog field capabilities.thinking_param tells you which key to send; the field capabilities.thinking_example gives the exact shape.
1. OpenAI GPT-5 Series (gpt-5-nano, gpt-5-mini, gpt-5, gpt-5.5)
Controls reasoning via the reasoning.effort parameter. If you pass Anthropic/Google thinking syntax, it is silently ignored.
await llm.chat({
model: 'gpt-5',
messages: [{ role: 'user', content: 'Prove sqrt(2) is irrational.' }],
max_completion_tokens: 2000, // Prevents reasoning from eating the output budget
reasoning: { effort: 'high' }, // "minimal" | "low" | "medium" | "high"
})
2. Google Gemini Series (gemini-3.1-pro-preview, gemini-3-flash-preview)
Gemini always reasons by default (no explicit param needed). Optionally use reasoning_effort: "low"|"medium"|"high" to adjust reasoning intensity.
await llm.chat({
model: 'gemini-3.1-pro-preview',
messages: [{ role: 'user', content: 'Prove sqrt(2) is irrational.' }],
max_tokens: 16384, // MUST use max_tokens for Gemini (NOT max_completion_tokens)
})
3. Anthropic Claude Series (only when listed in the live catalog) Sonnet/Opus 4.6-generation models require an explicit thinking budget; Opus 4.7 uses adaptive thinking and does not require thinking parameters.
await llm.chat({
model: '<claude model id from the live catalog>',
messages: [{ role: 'user', content: 'Prove sqrt(2) is irrational.' }],
max_tokens: 4000, // MUST be strictly > budget_tokens
thinking: { type: 'enabled', budget_tokens: 2048 },
})
A3. Structured output (JSON schema) — preferred for extraction/analysis
All proxy models fully support OpenAI's response_format JSON Schema specification. Required: strict: true and additionalProperties: false.
const resp = await llm.chat({
model: 'gpt-5-mini',
messages: [
{ role: 'system', content: 'Output JSON only.' },
{ role: 'user', content: 'My name is Alice and I am 30.' },
],
response_format: {
type: 'json_schema',
json_schema: {
name: 'person',
strict: true,
schema: {
type: 'object',
properties: { name: { type: 'string' }, age: { type: 'integer' } },
required: ['name', 'age'],
additionalProperties: false,
},
},
},
})
const data = JSON.parse(messageText(resp))
A4. Vision (image input)
import { readFile } from 'node:fs/promises'
const image = (await readFile('/absolute/path/photo.jpg')).toString('base64')
await llm.chat({
model: 'gemini-3-flash-preview',
messages: [{
role: 'user',
content: [
{ type: 'text', text: 'Describe this image.' },
{ type: 'image_url', image_url: { url: `data:image/jpeg;base64,${image}`, detail: 'auto' } },
],
}],
})
For remote images, pass the https:// URL directly.
A5. Tool / function calling
const resp = await llm.chat({
model: 'gpt-5-mini',
messages: [{ role: 'user', content: 'Weather in Tokyo?' }],
tools: [{
type: 'function',
function: {
name: 'get_weather',
description: 'Get weather for a city',
parameters: { type: 'object', properties: { city: { type: 'string' } }, required: ['city'] },
},
}],
tool_choice: 'auto',
})
// resp.choices[0].message.tool_calls -> execute -> append role:"tool" result -> call again
A6. Concurrent batch (the common case for bulk summarize/translate/analyze)
A ready-to-run batch driver is bundled. It reads lines or a JSON array, keeps output in input order, writes JSONL (index, input, output, error, usage), applies the family rules above, and stops early on fatal errors:
manus-node "<skill-dir>/scripts/llm_batch.mjs" --help
manus-node "<skill-dir>/scripts/llm_batch.mjs" --input items.txt --out out.jsonl \
--model gpt-5-mini --workers 8 --template "Summarize in one sentence: {input}"
Options: --system, --schema schema.json (strict structured output), --reasoning-effort minimal|low|medium|high, --thinking-budget N, --max-retries N, --timeout-seconds N.
Minimal inline version:
import { pathToFileURL } from 'node:url'
const { LlmClient, mapLimit, messageText } = await import(pathToFileURL('<skill-dir>/lib/llm.mjs').href)
const llm = new LlmClient()
const items = [/* inputs */]
let fatal
const results = await mapLimit(items, 8, async text => {
if (fatal) return { text, output: null, error: 'skipped' }
try {
const resp = await llm.chat({
model: 'gpt-5-mini',
messages: [{ role: 'user', content: `Summarize in one sentence:\n${text}` }],
})
return { text, output: messageText(resp), error: null }
} catch (error) {
if (error.fatal) fatal = error
return { text, output: null, error: error.message }
}
})
if (fatal) throw fatal
Keep concurrency modest (4–10); the proxy rate-limits each user and the client already retries transient failures. Save large results to files in the working directory instead of printing them.
B. Sandbox Python (optional, Sandbox only)
The SDK auto-reads OPENAI_API_KEY and OPENAI_API_BASE; just call OpenAI(). Request bodies are the same as section A, except that non-OpenAI keys (reasoning, thinking) go in extra_body; reasoning_effort, max_tokens, max_completion_tokens, response_format and tools are regular keyword arguments.
from openai import OpenAI
client = OpenAI()
resp = client.chat.completions.create(
model="gpt-5",
messages=[{"role": "user", "content": "Prove sqrt(2) is irrational."}],
max_completion_tokens=2000,
extra_body={"reasoning": {"effort": "high"}},
)
print(resp.choices[0].message.content)
print(resp.usage.prompt_tokens, resp.usage.completion_tokens)
The Python batch driver accepts the same options as llm_batch.mjs except --timeout-seconds:
python3 "<skill-dir>/scripts/llm_batch.py" --help
With a thread pool, keep max_workers modest (4–10); the proxy rate-limits and the SDK already retries.
C. Webdev project (TypeScript, server-side only)
Import from the scaffold's core helper. Credentials are injected by the platform (BUILT_IN_FORGE_API_URL / BUILT_IN_FORGE_API_KEY); never call from the client. Calls deduct project credits.
C1. Basic + list models
import { invokeLLM, listLLMModels } from "./server/_core/llm";
const { data } = await listLLMModels(); // discover IDs at runtime
const model = data.find(m => m.id.startsWith("gemini-"))?.id;
const res = await invokeLLM({
model, // optional; omit for default
messages: [
{ role: "system", content: "You are helpful." },
{ role: "user", content: "Hello" },
],
});
const text = res.choices[0].message.content;
C2. Thinking / reasoning (forwarded unchanged)
await invokeLLM({
model: "gpt-5",
messages: [...],
reasoning: { effort: "low" },
});
await invokeLLM({
model: "<claude model id from the live catalog>",
messages: [...],
thinking: { type: "enabled", budget_tokens: 2048 },
});
C3. Structured output
const r = await invokeLLM({
messages: [
{ role: "system", content: "Output JSON." },
{ role: "user", content: "name=Alice age=30" },
],
response_format: {
type: "json_schema",
json_schema: {
name: "person",
strict: true,
schema: {
type: "object",
properties: { name: { type: "string" }, age: { type: "integer" } },
required: ["name", "age"],
additionalProperties: false,
},
},
},
});
// JSON.parse(r.choices[0].message.content)
The helper also accepts outputSchema/output_schema as a shorthand, plus tools, toolChoice, maxTokens. It retries non-2xx/network errors with exponential backoff internally.
C4. Streaming (not exposed by default)
invokeLLM() does not expose stream. If you truly need it, modify the helper to send stream: true and parse the SSE response. When proxying SSE, listen on the response object's close event (not the request) and guard with a finished flag, or the upstream gets aborted after the first event. Render markdown with <Streamdown>{content}</Streamdown> from streamdown.
D. Raw HTTP (Sandbox only)
curl -s "$OPENAI_API_BASE/chat/completions" \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gpt-5-mini","messages":[{"role":"user","content":"ping"}]}'
Webdev path is "$BUILT_IN_FORGE_API_URL/v1/chat/completions" with BUILT_IN_FORGE_API_KEY. On a local computer or Cloud Computer, use the JavaScript client instead of raw HTTP.
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
Built-in LLM runtime is not ready |
Script not run with manus-node, or the Manus connection/runtime is outdated |
Run with manus-node and an absolute script path; if it persists, report that the Manus runtime needs updating. Never supply credentials manually |
LLM proxy: Insufficient credits |
Account balance too low | Stop the batch and tell the user; do not retry |
LLM proxy: Unsupported model ... |
Model ID not in the live catalog | Re-run Step 1 and pick a listed ID |
LLM proxy: Rate limit exceeded after retries |
Too many concurrent requests | Lower concurrency (--workers) and re-run failed items |
Gemini returns content: null + finish_reason: "length" |
Used max_completion_tokens |
Switch to max_tokens or omit the parameter |
| Claude returns 400 Bad Request | max_tokens <= budget_tokens |
Set max_tokens strictly greater than budget_tokens |
| GPT output truncated with reasoning on | Used max_tokens with reasoning |
Switch to max_completion_tokens for GPT |
Files in this skill
lib/llm.mjs—LlmClient(models(),chat()),messageText(),mapLimit(); runtime resolution, retries and credential redaction for every Manus computer.scripts/models.mjs— prints the live catalog.scripts/llm_batch.mjs— concurrent, retrying batch driver for every Manus computer.scripts/llm_batch.py— the same batch driver for Sandbox Python.