Skip to content

Tutorial: Using the Codegen Tool

Ty mascot with book

This tutorial covers the executeCesiumCode tool provided by the @cesium-ai/codegen-cesium package. The tool lets users describe what they want to see on the globe in plain English. The backend translates that description into verified CesiumJS JavaScript, which the browser then executes against the live Viewer. This tutorial explains how to use the tool, what you can configure, and how to tune its behaviour.

For a deep-dive into the internal generation pipeline, see the companion How Codegen Works guide.


1. How it works end to end

Type a natural-language intent in the chat panel — for example, "add a polygon over France" or "draw a red point at the Eiffel Tower". Here is what happens:

  1. The chat panel sends the message to /api/chat.
  2. The model decides to call executeCesiumCode and fills in the intent field with your request.
  3. Before the backend runs, the browser shows an approval prompt — the raw intent is displayed so you can confirm or reject it.
  4. On approval, the backend runs the full generation pipeline (domain matching → prompt building → LLM generation → AST verification → optional retry).
  5. Verified code is streamed back to the browser, which executes it against the live Viewer.

Codegen tool adding 3D buildings over New York

For the full request lifecycle sequence diagram, see Codegen Architecture — Request lifecycle.


2. Enabling the tool

The executeCesiumCode tool is enabled like any other tool in this starter — by adding its name to ENABLED_CESIUM_TOOLS in shared/src/enabled-tools.ts:

// shared/src/enabled-tools.ts
import { CODEGEN_CESIUM_TOOL_NAMES } from "@cesium-ai/codegen-cesium/names";

export const ENABLED_CESIUM_TOOLS = [
  CESIUM_TOOL_NAMES.flyTo,
  CODEGEN_CESIUM_TOOL_NAMES.executeCesiumCode, // ← add
] as const satisfies readonly (CesiumToolName | CodegenCesiumToolName)[];

The backend also requires a model to be configured — at least one of OPENAI_API_KEY, ANTHROPIC_API_KEY, or GOOGLE_GENERATIVE_AI_API_KEY must be set. If no model is available, executeCesiumCode is silently omitted from the registry even if listed in ENABLED_CESIUM_TOOLS.

To disable the tool, remove its name from the array. The backend immediately stops registering it and the frontend gate rejects any stale call.


3. Human-in-the-loop approval

executeCesiumCode runs with toolApproval: "user-approval". Before the backend generates any code, the browser shows the raw intent string and waits for an explicit click. This approval step exists because code generation is irreversible from the model's perspective — once the LLM runs, tokens are spent and code may be applied to the live globe.

The approval check is wired in backend/src/app.ts:

// backend/src/app.ts (simplified)
const toolApproval = {
  [CODEGEN_CESIUM_TOOL_NAMES.executeCesiumCode]: "user-approval",
};

If the user rejects, the agent loop receives a rejection result and may ask the user to clarify or abandon the request. No code is generated or executed.


4. Configuration options

Environment variables

These are validated through Zod in backend/src/utils/env.ts and read at startup.

Variable Default Description
CODEGEN_MAX_SKILLS 1 Number of top BM25-matched skill domains injected into the generation prompt as context. Increasing this gives the model broader CesiumJS API coverage at the cost of a larger prompt.
CODEGEN_MAX_ATTEMPTS 3 How many times to retry generation if AST verification fails. Each retry feeds the violation list back to the model as a correction prompt.
CODEGEN_MAX_CODE_LENGTH 4000 Maximum generated source length (characters) allowed by static verification (verifyCesiumCode maxLength).
CODEGEN_MAX_CODE_LINES 100 Maximum generated line count allowed by static verification (verifyCesiumCode maxLines).
CODEGEN_ALLOWED_SYMBOLS (unset) Optional comma-separated free-identifier allowlist passed to static verification (verifyCesiumCode allowedSymbols). Leave unset to disable allowlist enforcement.
CODEGEN_EXTRA_INSTRUCTIONS (unset) Optional operator-supplied instructions appended to the generation prompt output rules (for app-specific constraints or style preferences).

Set them in your .env file:

CODEGEN_MAX_SKILLS=2
CODEGEN_MAX_ATTEMPTS=5
CODEGEN_MAX_CODE_LENGTH=6000
CODEGEN_MAX_CODE_LINES=140
CODEGEN_ALLOWED_SYMBOLS=viewer,Cesium,scene
CODEGEN_EXTRA_INSTRUCTIONS=Prefer deterministic output and avoid unnecessary comments.

Programmatic options

createExecuteCesiumCodeTool (used in backend/src/app.ts) accepts the same options programmatically, letting you override env defaults without changing environment configuration:

// backend/src/app.ts
import { createExecuteCesiumCodeTool } from "./tools/execute-cesium-code-tool";

const executeCesiumCodeTool = createExecuteCesiumCodeTool({
  model,
  maxSkills: 2, // override CODEGEN_MAX_SKILLS
  maxAttempts: 5, // override CODEGEN_MAX_ATTEMPTS
  maxLength: 6000, // override CODEGEN_MAX_CODE_LENGTH
  maxLines: 140, // override CODEGEN_MAX_CODE_LINES
  allowedSymbols: ["viewer", "Cesium", "scene"], // override CODEGEN_ALLOWED_SYMBOLS
  extraInstructions: "Prefer deterministic output and avoid unnecessary comments.", // override CODEGEN_EXTRA_INSTRUCTIONS
});

The function signature for the underlying pipeline entry point is:

generateVerifiedCesiumCode({
  intent: string, // natural-language description from the user
  model: LanguageModel, // AI SDK LanguageModel — caller supplies this
  maxSkills?: number, // default 1
  maxAttempts?: number, // default 3
  maxLength?: number, // default 4000
  maxLines?: number, // default 100
  allowedSymbols?: readonly string[], // default unset
  extraInstructions?: string, // default unset
}): Promise<{ verified: true; code: string } | { verified: false; error: string; violations?: string[] }>

5. AST verifier rules

The verifier (verifyCesiumCode in packages/codegen-cesium/src/pipeline/ast-verifier.ts) runs parse-only static analysis — it never executes the candidate code. All violations are collected before the result is returned, so a single failing code block reports every problem at once.

Size limits

Limit Default Override
Maximum source length 4 000 characters options.maxLength
Maximum line count 100 lines options.maxLines

Parse check

The code is parsed with acorn (ecmaVersion: "latest", sourceType: "script"). A parse error is a violation. If parsing fails entirely, remaining checks are skipped.

Banned constructs

Construct Why
eval(...) / bare eval reference Dynamic code execution
Function(...) / new Function(...) / bare Function reference Dynamic code execution
Dynamic import(...) Dynamic module loading
Computed member access obj[expr] Bypasses static API allowlists

Banned browser globals

The following identifiers are rejected whether referenced directly or as the root of a member chain (e.g., window.fetch is also rejected):

fetch · XMLHttpRequest · WebSocket · window · document · localStorage · sessionStorage · indexedDB · navigator · Worker · SharedWorker · postMessage

Free-identifier allowlist (optional)

verifyCesiumCode accepts an optional allowedSymbols set. When provided, any free identifier not in the set and not in the built-in safe globals list (Math, console, Array, Object, etc.) is a violation. The default pipeline does not pass allowedSymbols, so only the denylist above is enforced. To enable positive allowlisting, call the verifier directly:

import { verifyCesiumCode } from "@cesium-ai/codegen-cesium";

const result = verifyCesiumCode(code, {
  allowedSymbols: new Set(["viewer", "Cesium", "scene"]),
});

Unbounded loop heuristic

while (true) { }, for (;;) { }, and do { } while (true) are rejected if the loop body contains no break statement anywhere in its subtree. This is a heuristic — it prevents the most obvious infinite loops but is not a termination proof.


6. How results flow back to the browser

The tool's execute handler in backend/src/tools/execute-cesium-code-tool.ts returns either:

{
  code: string;
} // generation succeeded and passed verification
{
  error: string;
} // generation failed or all retries were exhausted

The browser receives this as a tool result in the SSE stream. The starter app validates the result shape and then executes the verified code after user approval, isolated inside a fresh QuickJS-wasm interpreter provided by @cesium-ai/codegen-sandbox (see the Security Considerations document for the full sandbox architecture).


7. Quick reference

I want to… Where to look
Enable the tool shared/src/enabled-tools.ts — add CODEGEN_CESIUM_TOOL_NAMES.executeCesiumCode
Disable the tool Same file — remove the name
Change the number of skills in the prompt CODEGEN_MAX_SKILLS env var or maxSkills in createExecuteCesiumCodeTool
Change how many retries are allowed CODEGEN_MAX_ATTEMPTS env var or maxAttempts in createExecuteCesiumCodeTool
Change max generated source length CODEGEN_MAX_CODE_LENGTH env var or maxLength in createExecuteCesiumCodeTool
Change max generated line count CODEGEN_MAX_CODE_LINES env var or maxLines in createExecuteCesiumCodeTool
Restrict free identifiers CODEGEN_ALLOWED_SYMBOLS env var or allowedSymbols in createExecuteCesiumCodeTool
Append operator prompt rules CODEGEN_EXTRA_INSTRUCTIONS env var or extraInstructions in createExecuteCesiumCodeTool
Understand how the pipeline works How Codegen Works
Review the security threat model Security Considerations
Understand the architecture Codegen Architecture