This topic describes how to use the ML_PREDICT function to call AI models in Flink, covering syntax and parameters, per-call configuration, content type configuration, column-level parameters, and examples for text, image, and multimodal inference.
Quick start
Prerequisites
-
A Flink workspace has been created. For more information, see Activate Realtime Compute for Apache Flink.
-
Flink AI Service has been activated. For more information, see Flink AI Service (built-in models).
-
Flink AI Service (built-in models) requires VVR engine version 11.7 or later.
The following example demonstrates how to use ML_PREDICT to call a Flink built-in model. Go to , create a job, paste the code, and click Debug.
CREATE TEMPORARY TABLE text_source (
user_input STRING
) WITH ('connector' = 'datagen');
CREATE TEMPORARY TABLE result_sink (
user_input STRING,
ai_analysis STRING
) WITH ('connector' = 'print');
CREATE TEMPORARY MODEL text_model
INPUT (user_input STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.6-flash',
'task' = 'chat/completions',
'system-prompt' = 'Rate the gibberish level of the input on a scale of 0 to 100'
);
INSERT INTO result_sink
SELECT user_input, content as ai_analysis FROM
ML_PREDICT(
TABLE text_source,
MODEL text_model,
DESCRIPTOR(user_input)
);
Limits
-
Realtime Compute engine VVR 11.1 or later is required.
-
Some parameters are only supported when using Flink AI Service (built-in models) and require VVR 11.8 or later.
-
The throughput of the ML_PREDICT operator is subject to rate limiting by Model Studio. When the rate limit is reached, backpressure occurs at the ML_PREDICT operator, which may cause timeout errors and job restarts. For more information, see Rate limits on Model Studio.
-
The number of types in
content-typesand the number of columns in DESCRIPTOR must match the number of INPUT columns defined in CREATE MODEL. -
Columns of type
image_urlmust be STRING. Columns of typemulti_image_urlsmust beARRAY<STRING>. -
base64 images must include the
data:image/<format>;base64,prefix. Raw base64 strings and local file paths are not supported.
Syntax
ML_PREDICT(TABLE <table_name>, MODEL <model_name>, DESCRIPTOR(<input_columns>) [, CONFIG => MAP[...]])
Parameters
|
Parameter |
Data type |
Description |
|
TABLE |
TABLE |
The input data stream for model inference. You can specify a physical table or a view. |
|
MODEL |
MODEL |
The name of a registered model. For more information, see Model settings. |
|
DESCRIPTOR() |
— |
The input columns for model inference. Note
VVR 11.8 and later supports multiple input columns. This is only available when using Flink AI Service (built-in models). The number of DESCRIPTOR columns must match the number of CREATE MODEL INPUT columns. |
|
CONFIG => MAP[...] |
MAP |
Optional. For more information, see Per-call configuration. Note
Only supported in VVR 11.8 and later with Flink AI Service (built-in models). |
Per-call configuration
You can specify per-call configuration when calling ML_PREDICT. If a parameter is already set in CREATE MODEL, the per-call value takes precedence but does not persist to the MODEL definition.
|
Parameter |
Description |
Example |
|
|
Specifies the user prompt. Pass an empty string to bypass the MODEL-level value. |
|
|
|
Specifies the content type for single-column input. |
|
|
|
Specifies the content types for multi-column input. |
|
|
|
Specifies column-level parameters. |
|
|
|
Specifies additional parameters as a JSON string. |
|
|
|
The bundle size. |
Note
Bundle processing is supported only in VVR 11.9 and later. |
|
|
The allowed bundle latency. |
Content type parameters
-
For single-column input, use
content-typeto specify the content type. Supported values:text,image_url. -
For multi-column input, use
content-typesto specify the content type for each column. The supported values and their mapping to Flink SQL column types are listed below:
|
content-types value |
Flink SQL type |
Description |
|
|
|
- |
|
|
|
- |
|
|
|
Supported in VVR 11.9 and later. |
|
|
|
Supported in VVR 11.9 and later. |
|
|
|
Supported in VVR 11.8 and later. |
|
|
|
Supported in VVR 11.9 and later. |
|
|
|
Supported in VVR 11.9 and later. |
-
We recommend that you specify
content-typesin the parameters. If you set bothcontent-typeandcontent-types, the framework reports an error. Note: thecontent-typeandcontent-typessettings in model parameters and per-call settings affect each other:
|
CREATE MODEL setting |
Allowed per-call option |
Not allowed per-call option |
Description |
|
|
|
|
You can switch between |
|
|
|
|
You can change the type combination, e.g. |
|
Neither |
|
— |
Same as above |
Column-level parameters
|
Parameter |
Description |
Values |
Example |
|
|
Sets the minimum pixel threshold for input images or video frames. Images with fewer pixels than |
|
|
|
|
Sets the maximum pixel threshold for input images or video frames. Images within |
|
|
|
|
Limits the total pixels across all frames extracted from a video (single frame pixels x total frames). If the video exceeds this limit, frames are scaled down while keeping each frame within |
|
|
|
|
Enables explicit caching. |
|
|
|
|
Sets the frame sampling rate for video understanding. |
Valid values: [0.1, 10]. Default value: 2.0. |
|
Bundle processing
-
Processing: Bundle processing first aggregates multiple input records into a batch and then calls the AI model for the batch. This reduces the number of model calls but increases the model inference time for each request. When records can be effectively aggregated, bundle processing can significantly improve operator throughput.
-
Parameter description:
bundle-sizespecifies the maximum number of records accumulated in a batch.bundle-allow-latencyspecifies the maximum wait time when a batch has not reached thebundle-sizevalue. Processing starts when either the batch-size or wait-time condition is met. -
Requirements: Bundle processing takes effect based on the input and output schema of the model.
-
Text generation: For models whose
taskischat/completions, the input contains exactly oneSTRINGcolumn of text, and the output contains exactly oneSTRINGorVARIANTcolumn. Multi-column inputs and multimodal inputs such as images, videos, and audio are not supported. -
Text embedding: For models whose
taskisembeddings, the input contains exactly oneSTRINGcolumn, and the output contains exactly oneARRAY<FLOAT>column.
-
-
Supported functions:
ML_PREDICT, text classification (AI_CLASSIFY), sentiment analysis (AI_SENTIMENT), information extraction (AI_EXTRACT), text summarization (AI_SUMMARIZE), text translation (AI_TRANSLATE), data masking (AI_MASK), and text embedding (AI_EMBED). Among them,ML_PREDICTsupports bundle processing only when the registered model meets the preceding requirements. -
Global configuration: This configuration applies to the
ML_PREDICTcalls and the calls to the listed domain-specific AI functions in the job that meet the preceding requirements. Use the following global parameters to enable bundle processing:-- Bundle processing is supported only in VVR 11.9 and later. SET 'table.exec.ml-predict.bundle-size' = '10'; SET 'table.exec.ml-predict.bundle-allow-latency' = '5s'; -
Per-call configuration: These parameters apply only to the function call in which they are specified.
ML_PREDICTand the listed domain-specific AI functions use the same configuration method. For parameters and examples, see Per-call configuration.
Error handling
Limits
-
Supported only in VVR 11.9 and later.
-
You cannot enable error handling when model calls run in synchronous mode.
-
Error handling cannot be used together with Vectorization result cache (
CACHE_TABLE) at this time.
Enable error handling
Add the FAIL_ON_ERROR => FALSE parameter when you call an AI function.
Prerequisite: Keep the default retry settings of the model: error-handling-strategy='retry' and retry-fallback-strategy='failover'. For more information about model parameters, see Model settings.
|
Parameter |
Description |
Required |
Default value |
|
FAIL_ON_ERROR |
Specifies whether the job fails when a call ultimately fails. If you set this parameter to |
No |
|
FAIL_ON_ERROR defaults to TRUE, in which case the job fails after all retries are exhausted. Setting this parameter to FALSE changes only how failed records are handled, not the retry behavior.
Output changes
After you enable error handling, the system appends an error column err_msg of the nullable VARIANT type to the end of the original output fields. This column is generated by the system. You do not need to declare it in the input table or in the OUTPUT clause of CREATE MODEL.
-
Call succeeds:
err_msgisNULL. -
Call ultimately fails: all model result fields are
NULL, anderr_msgcontains the error information.
If you use SELECT *, the result includes the new error column. If you select fields explicitly, you must specify the column manually. The default column name is err_msg. If the name conflicts with an existing output column, the system appends a numeric suffix, such as err_msg0. Use the actual column name to reference the column.
Use the error column to determine whether a call failed: a value that is not NULL indicates a failure. Model result fields that are NULL do not necessarily indicate a failed call.
|
Field in the object |
Content |
SQL read pattern |
|
message |
The cause of the error. Check this field first when you troubleshoot issues. |
CAST(err_msg['message'] AS STRING) |
|
stack_trace |
More detailed diagnostic information. Check this field when you need to locate the root cause. |
CAST(err_msg['stack_trace'] AS STRING) |
|
details (optional field) |
Present only if the underlying error carries additional information. |
err_msg['details'] returns NULL if the field does not exist, or a VARIANT value if the field exists. |
Examples
AI_SENTIMENT keeps failed records
Prepare a sentiment analysis model named ai_sentiment_model, which is registered and supports asynchronous calls.
CREATE TEMPORARY VIEW input_data AS
SELECT *
FROM (VALUES
(1, CAST('xxxx (a description of violent conflict that violates humanity)' AS STRING), 'negative'),
(2, CAST('The customer service of this store has always been among the best' AS STRING), 'positive')
) AS t(id, input_text, expected_label);
CREATE TEMPORARY VIEW ai_results AS
SELECT i.id, i.input_text, i.expected_label,
r.score, r.label, r.confidence, r.err_msg
FROM input_data AS i,
LATERAL TABLE(AI_SENTIMENT(
MODEL => MODEL ai_sentiment_model,
INPUT => i.input_text,
FAIL_ON_ERROR => FALSE,
CONFIG => MAP['async', 'true']
)) AS r(score, label, confidence, err_msg);
SELECT * FROM ai_results;
The query returns the following columns in order.
|
Output column |
Data type |
Source and description |
|
id |
INT |
The ID of the input record. |
|
input_text |
STRING |
The original text to be processed. |
|
score / label / confidence |
STRING |
Returned by AI_SENTIMENT. |
|
err_msg |
VARIANT, can be |
Added after error handling is enabled. |
The query returns the following result. The summaries and error messages are examples. Actual results may vary.
|
id |
input_text |
score / label / confidence |
err_msg |
|
1 |
xxxx (a description of violent conflict that violates humanity) |
|
{"message":"com.openai.errors.BadRequestException: 400: <400> InternalError.Algo.DataInspectionFailed: Input text data may contain inappropriate content..... "stack_trace":"org.apache.flink.table.api.TableException: Failed to execute asynchronously prediction with input row xxx} |
|
2 |
The customer service of this store has always been among the best |
1.0 / positive / 0.99 |
|
Other error examples
The following example shows a timeout error in general invocation.
message:
java.util.concurrent.TimeoutException: AsyncPredictFunction did not complete within the configured timeout. Increase the ML_PREDICT timeout or check external service connectivity.
stack_trace:
java.util.concurrent.TimeoutException: AsyncPredictFunction did not complete within the configured timeout. Increase the ML_PREDICT timeout or check external service connectivity.
at org.apache.flink.table.runtime.operators.predict.AsyncMLPredictRunner.timeout(AsyncMLPredictRunner.java:153)
at org.apache.flink.table.runtime.operators.predict.AsyncMLPredictRunner.timeout(AsyncMLPredictRunner.java:51)
at org.apache.flink.table.runtime.operators.TableKeyedAsyncWaitOperator$KeyedResultHandler.timerTriggered(TableKeyedAsyncWaitOperator.java:462)
at org.apache.flink.table.runtime.operators.TableKeyedAsyncWaitOperator$KeyedResultHandler.lambda$registerTimeout$1(TableKeyedAsyncWaitOperator.java:457)
at org.apache.flink.table.runtime.operators.TableKeyedAsyncWaitOperator.lambda$registerTimer$4(TableKeyedAsyncWaitOperator.java:486)
at org.apache.flink.streaming.runtime.tasks.StreamTask.invokeProcessingTimeCallback(StreamTask.java:2285)
at org.apache.flink.streaming.runtime.tasks.StreamTask.lambda$deferCallbackToMailbox$28(StreamTask.java:2276)
at org.apache.flink.streaming.runtime.tasks.StreamTaskActionExecutor$1.runThrowing(StreamTaskActionExecutor.java:50)
at org.apache.flink.streaming.runtime.tasks.mailbox.Mail.run(Mail.java:115)
at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.runMail(MailboxProcessor.java:415)
at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.processMailsWhenDefaultActionUnavailable(MailboxProcessor.java:384)
at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.processMail(MailboxProcessor.java:369)
at org.apache.flink.streaming.runtime.tasks.mailbox.MailboxProcessor.runMailboxLoop(MailboxProcessor.java:229)
at org.apache.flink.streaming.runtime.tasks.StreamTask.runMailboxLoop(StreamTask.java:1273)
at org.apache.flink.streaming.runtime.tasks.StreamTask.invoke(StreamTask.java:1217)
at org.apache.flink.runtime.taskmanager.Task.runWithSystemExitMonitoring(Task.java:1133)
at org.apache.flink.runtime.taskmanager.Task.restoreAndInvoke(Task.java:1110)
at org.apache.flink.runtime.taskmanager.Task.doRun(Task.java:914)
at org.apache.flink.runtime.taskmanager.Task.run(Task.java:716)
at java.base/java.lang.Thread.run(Thread.java:991)
How each error type is handled
The following behavior applies when error handling is enabled and the default retry settings are kept. An error row is a row that retains the input, returns empty model results, and records the failure reason in err_msg.
|
Error type or cause |
System behavior |
Final output |
|
Network connection failures, throttling, or transient service errors (429, 500, 502, 503) |
Waits for the configured period and then retries. |
Normal output if a retry succeeds. An error row if the call ultimately fails. |
|
Invalid parameters, input that is too long, invalid key, permission denied, or model not found (400, 401, 403, 404, 422) |
Typically returns a failure directly without retries. |
An error row that records the reason returned by the service. |
|
Content moderation rejection, or insufficient balance or quota |
Decides whether to retry based on the error returned by the service. |
Normal output if the error recovers. An error row if the call ultimately fails. |
|
A JSON result is required, but the model response cannot be parsed |
Sends the request again. |
Normal output if the request succeeds. An error row if the call ultimately fails. |
|
The configured waiting period is exceeded |
Ends the current wait. |
An error row that indicates a timeout. |
|
A call ultimately fails or times out during bundle processing |
Records the failure for each input in the batch. |
One error row per input, each recording the same failure reason. |
Examples
Text
The following example registers and uses a Flink AI Service built-in model to classify input text by sentiment. Example 1 uses MODEL-level parameters. Example 2 overrides user-prompt at call time.
-- Register a built-in model. When content-type is not specified, it defaults to text
CREATE MODEL sentiment_model
INPUT (prompt STRING)
OUTPUT (response STRING)
WITH (
'provider' = 'openai-compat',
'task' = 'chat/completions',
'model' = 'qwen3.6-flash',
'system-prompt' = 'You are a sentiment classifier. Output one label: negative, positive, or neutral'
);
-- Create a source table: simulated product review data
CREATE TEMPORARY VIEW input_table(id, content)
AS VALUES
(1, 'Great quality, soft fabric, fits perfectly'),
(2, 'Had loose threads on arrival, faded badly after one wash'),
(3, 'Received the item, looks as pictured'),
(4, 'Started pilling after two weeks, customer service refused returns'),
(5, 'Flattering fit, color is even better than the photo, already ordered a third one');
-- Create a result table
CREATE TEMPORARY TABLE output_table (
id INT,
content STRING,
sentiment STRING
) WITH (
'connector' = 'print'
);
-- Use ML_PREDICT for real-time inference
-- Example 1: Use MODEL-level parameters
INSERT INTO output_table
SELECT
id,
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content));
-- Example 2: Specify per-call parameters
INSERT INTO output_table
SELECT
id,
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content),
MAP['user-prompt', 'Reply in Chinese']);
Example 1 output:
|
id |
content |
sentiment |
|
1 |
Great quality, soft fabric, fits perfectly |
positive |
|
2 |
Had loose threads on arrival, faded badly after one wash |
negative |
|
3 |
Received the item, looks as pictured |
positive |
|
4 |
Started pilling after two weeks, customer service refused returns |
negative |
|
5 |
Flattering fit, color is even better than the photo, already ordered a third one |
positive |
Example 2 output:
|
id |
content |
sentiment |
|
1 |
Great quality, soft fabric, fits perfectly |
Positive |
|
2 |
Had loose threads on arrival, faded badly after one wash |
Negative |
|
3 |
Received the item, looks as pictured |
Positive |
|
4 |
Started pilling after two weeks, customer service refused returns |
Negative |
|
5 |
Flattering fit, color is even better than the photo, already ordered a third one |
Positive |
Image
The following example registers a multimodal model and classifies input images.
The image input column (when content-type is image_url) is of the STRING type and supports the following formats:
|
Input format |
Usage |
Limits |
|
Public URL |
Pass an HTTP or HTTPS URL string that the model service can access over the Internet. The model service pulls the image. |
The URL must be accessible over the Internet from the environment in which the model service runs. If the URL is time-limited, its validity period must cover the duration of the model call. Image formats and sizes are limited by the model service. |
|
Base64 data URL |
Pass a string that is prefixed with |
The image bytes are transmitted by the Flink job, which increases the network overhead of the job. Base64 encoding also increases the data size. The data volume is limited by the input limits of the model service. |
If images are stored in Blob columns of a Paimon Catalog, you can use the Paimon Blob presigned URL feature to generate a public URL as input. For more information, see Apache Paimon.
-- Register a built-in model with content-type set to image_url
CREATE TEMPORARY MODEL sentiment_model
INPUT (prompt STRING)
OUTPUT (response STRING)
WITH (
'provider' = 'openai-compat',
'task' = 'chat/completions',
'model' = 'qwen3.6-flash',
'content-type' = 'image_url'
);
-- Use ML_PREDICT for real-time inference
INSERT INTO output_table
SELECT
id,
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content));
Video
The following example registers a multimodal model to understand an input video.
CREATE TEMPORARY MODEL sentiment_model
INPUT (video_url STRING)
OUTPUT (response STRING)
WITH (
'provider' = 'openai-compat',
'task' = 'chat/completions',
'model' = 'qwen3.6-plus',
'content-types' = 'video_url',
'system-prompt' = 'Briefly describe the video content'
);
CREATE TEMPORARY VIEW input_table(content)
AS VALUES ('https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251111/ckgcxt/%E5%8D%95%E4%BA%BA%E8%AF%B4%E8%AF%9D-1.mp4');
SELECT
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content));
Text + single image
The following example registers a multimodal model for chat-based inference on text and image inputs.
CREATE MODEL vl_model
INPUT (text_input STRING, image_input STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions',
'content-types' = 'text;image_url'
);
INSERT INTO result_sink
SELECT content FROM TABLE(ML_PREDICT(
TABLE image_source,
MODEL vl_model,
DESCRIPTOR(text_input, image_input)
));
Text + multiple images (multiple columns)
Pass each image through a separate INPUT column. Specify image_url for each image column in content-types.
CREATE MODEL vl_model_multi
INPUT (prompt STRING, img1 STRING, img2 STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions',
'content-types' = 'text;image_url;image_url'
);
SELECT content FROM TABLE(ML_PREDICT(
TABLE my_source,
MODEL vl_model_multi,
DESCRIPTOR(prompt, img1, img2)
));
Text + multiple images (array)
Pass multiple images in an ARRAY<STRING> column. Use multi_image_urls in content-types.
CREATE MODEL vl_model_array
INPUT (prompt STRING, images ARRAY<STRING>)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions',
'content-types' = 'text;multi_image_urls'
);
SELECT content FROM TABLE(ML_PREDICT(
TABLE my_source,
MODEL vl_model_array,
DESCRIPTOR(prompt, images)
));
Text + video
CREATE TEMPORARY MODEL sentiment_model
INPUT (video_url STRING, question STRING)
OUTPUT (response STRING)
WITH (
'provider' = 'openai-compat',
'task' = 'chat/completions',
'model' = 'qwen3.6-plus',
'content-types' = 'video_url;text'
);
CREATE TEMPORARY VIEW input_table(content, question)
AS VALUES ('https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20251111/ckgcxt/%E5%8D%95%E4%BA%BA%E8%AF%B4%E8%AF%9D-1.mp4', 'How many people are there?');
SELECT
content,
response AS sentiment
FROM ML_PREDICT(
TABLE input_table,
MODEL sentiment_model,
DESCRIPTOR(content, question));
Per-call parameter examples
Example 1: Specify content type parameters
-- When content-type / content-types are not specified in CREATE MODEL, the default is content-type = text
CREATE MODEL model_single
INPUT (input STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions'
);
-- Call 1: Use MODEL parameters, content type is text
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_text,
MODEL model_single,
DESCRIPTOR(input)
));
-- Call 2: Override content type to image at call time
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_img,
MODEL model_single,
DESCRIPTOR(input),
MAP['content-type', 'image_url']
));
Example 2: Specify user-prompt and column-level parameters
-- Set default configuration in CREATE MODEL (multi-column model)
CREATE MODEL vl_model
INPUT (text_input STRING, image_input STRING)
OUTPUT (content STRING)
WITH (
'provider' = 'openai-compat',
'model' = 'qwen3.5-plus',
'task' = 'chat/completions',
'content-types' = 'text;image_url',
'user-prompt' = 'Describe the image'
);
-- Call 1: Use default configuration
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_a, MODEL vl_model, DESCRIPTOR(text_input, image_input)
));
-- Call 2: Override user-prompt and column-level parameters
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_b, MODEL vl_model, DESCRIPTOR(text_input, image_input),
MAP[
'user-prompt', 'Answer in English',
'image_input.min_pixels', '100',
'image_input.max_pixels', '5000'
]
));
-- Call 3: Override content-types to change the type combination (treat both columns as text)
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_c, MODEL vl_model, DESCRIPTOR(text_input, image_input),
MAP['content-types', 'text;text']
));
-- Call 4: Override content-types to change the type combination (reverse order)
SELECT content FROM TABLE(ML_PREDICT(
TABLE source_c, MODEL vl_model, DESCRIPTOR(text_input, image_input),
MAP['content-types', 'image_url;text']
));