Skip to main content
PreProcessor runs your code on the end user’s messages before the model sees them, and returns whether the turn proceeds, proceeds with different messages, or stops with a reply of its own. Register instances on LuaAgent under preProcessors. Inside execute, the runtime objects (User, Data, Lua, env) are available as in a tool. For where a preprocessor sits in a turn, see processors. Verified against lua-cli 3.33.0.

Quick example

A preprocessor that blocks a refund request until it carries an order number:
src/preprocessors/OrderIdGuard.ts

Constructor

Creates a preprocessor from a PreProcessorConfig. The class is also exported under the alias LuaPreprocessor.
ErrorsPreProcessor requires a non-empty \name` (used as the server-side identifier).whenname` is missing or blank.

Configuration

string
required
Server-side identifier, and the name lua push preprocessor --name and lua preprocessors address. Kebab-case, for example order-id-guard.
string
required
Short description shown in listings. Not seen by the model.
(user, messages, channel) => Promise<PreProcessorResult>
required
Called once per turn with the end user’s UserDataInstance, the turn’s ChatMessage[], and the channel identifier ('web', 'whatsapp', 'email', …). Must resolve to a PreProcessorResult.
number
default:100
Execution order. Lower numbers run first; preprocessors with the same priority run in the order the server stores them, not the order of the preProcessors array.
boolean
default:false
Stored with the pushed version. The deployed runtime runs every preprocessor in sequence and waits for each result regardless of this flag.

Result

execute returns one of two shapes; there is no 'allow' or 'modify' action.

Proceed

{ action: 'proceed' } hands the current messages to the next preprocessor, or to the model when this is the last one. Set modifiedMessage to replace them; the replacement is what every later preprocessor and the model receive.

Block

{ action: 'block', response } stops the chain. The model is not called, later preprocessors do not run, and response is sent to the end user as the agent’s reply.

Empty response

{ action: 'block', response: '' } stops the chain, but what the end user sees depends on the path the turn came in on. The web widget and other UI-stream channels show the default text User not eligible for agent response; a caller of the generate endpoint or Agents.invoke receives a preprocessor_blocked result with empty text; only the legacy streaming path sends no text. There is no channel-independent way to block silently.

Message types

messages is a ChatMessage[]; every member carries a type discriminator.
image and data hold a URL or base64 content; mediaType is the MIME type, for example image/png or application/pdf.

Instance methods

Execution order

  • Only preprocessors that are pushed and active run (lua preprocessors activate). They run in ascending priority, one at a time; each receives the messages the previous one returned.
  • The first block ends the chain. A proceed without modifiedMessage passes the current messages through unchanged.
  • A preprocessor that throws or times out is logged (lua logs --type preprocessor) and skipped, and the chain continues with the current messages. On a voice channel the same failure blocks the turn instead, with the spoken reply Sorry, I'm having trouble right now. Could you try that again? and metadata: { failedPreprocessor: '<name>', failClosed: true }.
  • execute has 180 seconds to return.
  • lua chat in the sandbox sends a PreProcessorOverride for every preprocessor listed in lua.skill.yaml, so the sandbox turn runs your sandbox version in place of the production one. You never construct overrides yourself.

CLI commands

The lua test input object accepts message and channel; the message is wrapped as a single TextMessage.

Types

All of these are exported from 'lua-cli'.
interface
{ name; description; async?; priority?; execute } as documented under Configuration.
PreProcessorBlockResponse | PreProcessorProceedResponse
The union execute returns. PreProcessorAction is 'proceed' | 'block'.
type
{ action: 'block'; response: string; metadata?: Record<string, any> }.
type
{ action: 'proceed'; modifiedMessage?: ChatMessage[]; metadata?: Record<string, any> }.
type
The message shapes under Message types.
interface
{ preprocessorId: string; sandboxId: string }. Sent by lua chat to run a sandbox version.
alias
The same class as PreProcessor.

See also