Skip to main content
Ponder is a first-party Plot Plugin that runs a language model for every input row and returns a typed SQL value. Use it for classification, extraction, and other row-wise reasoning that is awkward to express with ordinary SQL.
Every row that reaches ponder() creates a model call. Filter the source and keep a LIMIT while developing. Pipelines should also set external-call and cost budgets.

Classify rows

Named arguments use =>. Supply model coordinates inline or leave them out to use the default model, make the instruction and output type constant strings, and label every input with named_struct.
Classify Linear issues

Arguments

Do not mix named and positional arguments. An argument ponder() refuses, such as an unknown argument name, a Struct output without schema, or an output type it does not support, fails while the query is planned, before any row is sent. The query returns gRPC INVALID_ARGUMENT with error code QL-PL-013 (INVALID_FUNCTION_ARGUMENT). The message names the argument and what it must be, never the value you gave, for example Missing required ponder() argument 'schema' for Struct output.

Choose the model

A call that names a model, inline or through model, runs that model. A call that names none runs the Workspace’s default model for ponder(), which a Workspace administrator sets in the Workspace’s AI policy. Without a Workspace default it runs Parable’s default, Gemini 3.5 Flash-Lite on Vertex (model id gemini-3.5-flash-lite). Vertex serves that model from its global endpoint, so Google chooses the region that processes each call.
Use the default model
Every model runs on an AI processor, named by its server. server => 'vertex' runs on Vertex, in the Workspace’s own Vertex project when it has one. The Workspace’s AI policy lists the processors ponder() may use. Parable allows only vertex. A call whose processor the policy does not allow, a call that names no model when no default’s processor is allowed, and any call while the Workspace’s policy cannot be read are refused while the query is planned, before any row is sent: A Report run records the same code when the policy refuses it, and the run fails: a refused call never produces NULL. A run whose planning could not read the policy plans again. Parable also refuses a model that it has no price for yet, because the cost limit could not count its calls. This applies to ponder(), embed() and the Jev functions, in a Report run (a scheduled Refresh, a Refresh you start, or a Preview) and in an interactive query (the Plot editor’s Preview, Ponder’s SQL tool and the Labs Pane query()). The call with that model sends nothing.
  • A Report run fails with QR-QU-013. The Plot says which call and which model have no price, for example “This Plot’s ponder() call uses the model gemini-9-ultra on Google Vertex AI, which Parable has no price for yet”. Other AI functions of the run that ran before it are charged as usual.
  • An interactive query fails with QL-SQ-012 and a message that names the function, the model and its processor.
On OpenRouter, each response states its cost. A response that states no cost is priced from Parable’s price table. When the table has no price for the model, the call is refused after that response, and no further call is sent. Parable’s default model has a price. To run a model that has none, ask Parable to add its price.

Choose an output type

Primitive output supports Utf8, Int64, Float64, Boolean, Date32, and Timestamp. A failed call produces NULL for a primitive cell while the rest of the query continues. Use Struct when downstream SQL needs more than one typed field:
Extract structured output
All declared Struct fields are nullable in Arrow. Ponder appends a reserved meta Struct; do not declare meta in the supplied schema. partial means the response parsed but omitted at least one required field. error means the model call failed, or the answer broke a constraint of the schema. Struct output keeps the metadata alongside the row so SQL can inspect either state.

Constrain a Struct answer

The schema gives more than the field types. ponder() keeps these keywords of each field and sends them to the model: The response schema keeps only the keywords that every route (vertex, anthropic and openrouter) accepts. A keyword that one route refuses would fail every row on that route. ponder() then checks each answer against the whole schema. An answer that breaks a constraint, such as a category that is not in its enum, returns meta.status = 'error'. meta.error names the field and the keyword, for example category breaks the output schema's 'enum' constraint. The row keeps the answer’s fields and is charged for the call. ponder() does not send the call again. The check does not change these cases:
  • An absent or null field makes the row partial.
  • A value of another JSON type, such as "5" for an integer field, is converted to the field’s type. A bound such as maximum applies only to a JSON number, so a number sent as a string is not checked against it.
  • A field that the schema does not declare is dropped.
  • format is not checked.
A schema that gives valid Arrow fields but is not a valid JSON Schema, such as one with "minimum": "0", still runs. ponder() then sends none of its constraints and checks no answer, as before the check existed. A Report run reuses the answer it logged for a row whose inputs did not change. An answer logged before the check existed keeps its status. A change to the call’s instruction or schema makes the next run answer every row again.
Classify into a fixed list

Execution and limits

Interactive queries call the model while the query runs. Report runs pin the output-affecting model and instruction content, and account provider calls and cost before completing the result. A Report run pins the model it resolved, including a Workspace default, and the AI policy it planned under: a later policy change reaches the next run, never one already planned. A new default is a new model, so the Plot’s next run recomputes its results. A scheduled run returns the same values as a query. Every row gets the same prompt, with its inputs labelled as named_struct names them, and a Report run stores the value a Preview returns: every output type, NULL for a failed primitive call, and meta for a Struct. The run records each column with its output type, so the Report reads it back as the Preview returns it. If a Report cannot read a Plot’s ponder() output, run the Plot’s Report again. A scheduled run retries a failed call before it stores the failure, and is charged one external call for each request that reached the provider. A call that cannot reach its provider at all, such as a route without a configured credential, fails the query with its cause instead of returning a NULL per row. A scheduled run fails with QR-QU-010 and is charged nothing, and its Report shows that the Plot’s AI functions could not reach their AI processor. When every input is constant, such as inputs => named_struct('topic', 'billing'), every row has the same prompt. A query sends it once for each batch of rows the engine passes to ponder(), at most 8 rows by default, and every row in the batch reads that answer. A scheduled run asks it once. The service caps input rows and concurrency. A longer per-call timeout can lower the effective row cap because it consumes more of the query timeout budget. Run ponder() with the same SDK query method as any other Parable SQL. See Query with the SDKs.