From b2ba62c2e6659b84ec752692ffa5c63192fe24e4 Mon Sep 17 00:00:00 2001 From: samscott89 Date: Thu, 9 Apr 2026 13:11:55 -0500 Subject: [PATCH 1/3] feat(types): add overload-based AI.run() with gateway fallback and gemma-4 Add overload-based run() signatures for better type inference: - Batch request (queueRequest: true) -> AsyncResponse - Raw response (returnRawResponse: true) -> Response - WebSocket (websocket: true) -> Response - Streaming (stream: true in inputs) -> ReadableStream - Normal -> model-specific output type - Unknown model (gateway fallback) -> Record The gateway fallback allows AI Gateway models (e.g. 'google/nano-banana') to be called without type errors while preserving full type safety for known Cloudflare AI models. Also adds gemma-4-26b-a4b-it model types and type-check tests. --- types/defines/ai.d.ts | 69 ++++++++++++++++++++++++++++++------------ types/test/types/ai.ts | 67 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 19 deletions(-) create mode 100644 types/test/types/ai.ts diff --git a/types/defines/ai.d.ts b/types/defines/ai.d.ts index 0000b51d1c9..caaa62f8644 100644 --- a/types/defines/ai.d.ts +++ b/types/defines/ai.d.ts @@ -5827,6 +5827,10 @@ export declare abstract class Base_Ai_Cf_Nvidia_Nemotron_3_120B_A12B { inputs: ChatCompletionsInput; postProcessedOutputs: ChatCompletionsOutput; } +export declare abstract class Base_Ai_Cf_Google_Gemma_4_26B_A4B_IT { + inputs: ChatCompletionsInput; + postProcessedOutputs: ChatCompletionsOutput; +} export interface AiModels { "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; @@ -5979,7 +5983,10 @@ export type ChatCompletionsInput = XOR< export interface InferenceUpstreamError extends Error {} export interface AiInternalError extends Error {} export type AiModelListType = Record; -export declare abstract class Ai { +export type AsyncResponse = { request_id: string }; +export declare abstract class Ai< + AiModelList extends AiModelListType = AiModels, +> { aiGatewayLogId: string | null; gateway(gatewayId: string): AiGateway; @@ -5997,25 +6004,49 @@ export declare abstract class Ai * @param autoragId Instance ID */ autorag(autoragId: string): AutoRAG; - run( + + // Batch request + run( model: Name, - inputs: InputOptions, - options?: Options, - ): Promise< - Options extends - | { - returnRawResponse: true; - } - | { - websocket: true; - } - ? Response - : InputOptions extends { - stream: true; - } - ? ReadableStream - : AiModelList[Name]["postProcessedOutputs"] - >; + inputs: { requests: AiModelList[Name]['inputs'][] }, + options: AiOptions & { queueRequest: true } + ): Promise; + + // Raw response + run( + model: Name, + inputs: AiModelList[Name]['inputs'], + options: AiOptions & { returnRawResponse: true } + ): Promise; + + // WebSocket + run( + model: Name, + inputs: AiModelList[Name]['inputs'], + options: AiOptions & { websocket: true } + ): Promise; + + // Streaming + run( + model: Name, + inputs: AiModelList[Name]['inputs'] & { stream: true }, + options?: AiOptions + ): Promise; + + // Normal (default) - known model + run( + model: Name, + inputs: AiModelList[Name]['inputs'], + options?: AiOptions + ): Promise; + + // Unknown model (gateway fallback) + run( + model: string & {}, + inputs: Record, + options?: AiOptions + ): Promise>; + models(params?: AiModelsSearchParams): Promise; toMarkdown(): ToMarkdownService; toMarkdown( diff --git a/types/test/types/ai.ts b/types/test/types/ai.ts new file mode 100644 index 00000000000..17c7d32d958 --- /dev/null +++ b/types/test/types/ai.ts @@ -0,0 +1,67 @@ +// Copyright (c) 2025 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 + +function expectType(_value: T) {} + +export const handler: ExportedHandler<{ AI: Ai }> = { + async fetch(_request, env) { + // Known model -- normal response + { + const result = await env.AI.run('@cf/meta/llama-3.1-8b-instruct-fp8', { + prompt: 'hello', + }); + expectType(result); + } + + // Known model -- streaming + { + const result = await env.AI.run('@cf/meta/llama-3.1-8b-instruct-fp8', { + prompt: 'hello', + stream: true as const, + }); + expectType(result); + } + + // Known model -- raw response + { + const result = await env.AI.run( + '@cf/meta/llama-3.1-8b-instruct-fp8', + { prompt: 'hello' }, + { returnRawResponse: true as const } + ); + expectType(result); + } + + // Known model -- batch request + { + const result = await env.AI.run( + '@cf/meta/llama-3.1-8b-instruct-fp8', + { requests: [{ prompt: 'hello' }, { prompt: 'world' }] }, + { queueRequest: true as const } + ); + expectType(result); + } + + // Gateway model -- unknown model name, permissive types + { + const result = await env.AI.run('google/nano-banana', { + prompt: 'hello', + aspect_ratio: '16:9', + }); + expectType>(result); + } + + // Gateway model with gateway options + { + const result = await env.AI.run( + 'google/nano-banana', + { prompt: 'hello' }, + { gateway: { id: 'my-gateway' } } + ); + expectType>(result); + } + + return new Response(); + }, +}; From 2635e23321aa82fc1968e8a91f1fc9348ca4875a Mon Sep 17 00:00:00 2001 From: samscott89 Date: Thu, 9 Apr 2026 13:34:50 -0500 Subject: [PATCH 2/3] Update generated snapshots --- .../experimental/index.d.ts | 74 +++++++++++++------ .../generated-snapshot/experimental/index.ts | 74 +++++++++++++------ types/generated-snapshot/latest/index.d.ts | 74 +++++++++++++------ types/generated-snapshot/latest/index.ts | 74 +++++++++++++------ 4 files changed, 208 insertions(+), 88 deletions(-) diff --git a/types/generated-snapshot/experimental/index.d.ts b/types/generated-snapshot/experimental/index.d.ts index 748d43e2ca1..989bf406a1c 100755 --- a/types/generated-snapshot/experimental/index.d.ts +++ b/types/generated-snapshot/experimental/index.d.ts @@ -11023,6 +11023,10 @@ declare abstract class Base_Ai_Cf_Nvidia_Nemotron_3_120B_A12B { inputs: ChatCompletionsInput; postProcessedOutputs: ChatCompletionsOutput; } +declare abstract class Base_Ai_Cf_Google_Gemma_4_26B_A4B_IT { + inputs: ChatCompletionsInput; + postProcessedOutputs: ChatCompletionsOutput; +} interface AiModels { "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; @@ -11178,6 +11182,9 @@ type ChatCompletionsInput = XOR< interface InferenceUpstreamError extends Error {} interface AiInternalError extends Error {} type AiModelListType = Record; +type AsyncResponse = { + request_id: string; +}; declare abstract class Ai { aiGatewayLogId: string | null; gateway(gatewayId: string): AiGateway; @@ -11194,29 +11201,52 @@ declare abstract class Ai { * @param autoragId Instance ID */ autorag(autoragId: string): AutoRAG; - run< - Name extends keyof AiModelList, - Options extends AiOptions, - InputOptions extends AiModelList[Name]["inputs"], - >( + // Batch request + run( model: Name, - inputs: InputOptions, - options?: Options, - ): Promise< - Options extends - | { - returnRawResponse: true; - } - | { - websocket: true; - } - ? Response - : InputOptions extends { - stream: true; - } - ? ReadableStream - : AiModelList[Name]["postProcessedOutputs"] - >; + inputs: { + requests: AiModelList[Name]["inputs"][]; + }, + options: AiOptions & { + queueRequest: true; + }, + ): Promise; + // Raw response + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options: AiOptions & { + returnRawResponse: true; + }, + ): Promise; + // WebSocket + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options: AiOptions & { + websocket: true; + }, + ): Promise; + // Streaming + run( + model: Name, + inputs: AiModelList[Name]["inputs"] & { + stream: true; + }, + options?: AiOptions, + ): Promise; + // Normal (default) - known model + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options?: AiOptions, + ): Promise; + // Unknown model (gateway fallback) + run( + model: string & {}, + inputs: Record, + options?: AiOptions, + ): Promise>; models(params?: AiModelsSearchParams): Promise; toMarkdown(): ToMarkdownService; toMarkdown( diff --git a/types/generated-snapshot/experimental/index.ts b/types/generated-snapshot/experimental/index.ts index 6b79d2ac38e..828cba31af5 100755 --- a/types/generated-snapshot/experimental/index.ts +++ b/types/generated-snapshot/experimental/index.ts @@ -11032,6 +11032,10 @@ export declare abstract class Base_Ai_Cf_Nvidia_Nemotron_3_120B_A12B { inputs: ChatCompletionsInput; postProcessedOutputs: ChatCompletionsOutput; } +export declare abstract class Base_Ai_Cf_Google_Gemma_4_26B_A4B_IT { + inputs: ChatCompletionsInput; + postProcessedOutputs: ChatCompletionsOutput; +} export interface AiModels { "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; @@ -11187,6 +11191,9 @@ export type ChatCompletionsInput = XOR< export interface InferenceUpstreamError extends Error {} export interface AiInternalError extends Error {} export type AiModelListType = Record; +export type AsyncResponse = { + request_id: string; +}; export declare abstract class Ai< AiModelList extends AiModelListType = AiModels, > { @@ -11205,29 +11212,52 @@ export declare abstract class Ai< * @param autoragId Instance ID */ autorag(autoragId: string): AutoRAG; - run< - Name extends keyof AiModelList, - Options extends AiOptions, - InputOptions extends AiModelList[Name]["inputs"], - >( + // Batch request + run( model: Name, - inputs: InputOptions, - options?: Options, - ): Promise< - Options extends - | { - returnRawResponse: true; - } - | { - websocket: true; - } - ? Response - : InputOptions extends { - stream: true; - } - ? ReadableStream - : AiModelList[Name]["postProcessedOutputs"] - >; + inputs: { + requests: AiModelList[Name]["inputs"][]; + }, + options: AiOptions & { + queueRequest: true; + }, + ): Promise; + // Raw response + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options: AiOptions & { + returnRawResponse: true; + }, + ): Promise; + // WebSocket + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options: AiOptions & { + websocket: true; + }, + ): Promise; + // Streaming + run( + model: Name, + inputs: AiModelList[Name]["inputs"] & { + stream: true; + }, + options?: AiOptions, + ): Promise; + // Normal (default) - known model + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options?: AiOptions, + ): Promise; + // Unknown model (gateway fallback) + run( + model: string & {}, + inputs: Record, + options?: AiOptions, + ): Promise>; models(params?: AiModelsSearchParams): Promise; toMarkdown(): ToMarkdownService; toMarkdown( diff --git a/types/generated-snapshot/latest/index.d.ts b/types/generated-snapshot/latest/index.d.ts index 6609b2ac1fc..427ddbfc2b9 100755 --- a/types/generated-snapshot/latest/index.d.ts +++ b/types/generated-snapshot/latest/index.d.ts @@ -10331,6 +10331,10 @@ declare abstract class Base_Ai_Cf_Nvidia_Nemotron_3_120B_A12B { inputs: ChatCompletionsInput; postProcessedOutputs: ChatCompletionsOutput; } +declare abstract class Base_Ai_Cf_Google_Gemma_4_26B_A4B_IT { + inputs: ChatCompletionsInput; + postProcessedOutputs: ChatCompletionsOutput; +} interface AiModels { "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; @@ -10486,6 +10490,9 @@ type ChatCompletionsInput = XOR< interface InferenceUpstreamError extends Error {} interface AiInternalError extends Error {} type AiModelListType = Record; +type AsyncResponse = { + request_id: string; +}; declare abstract class Ai { aiGatewayLogId: string | null; gateway(gatewayId: string): AiGateway; @@ -10502,29 +10509,52 @@ declare abstract class Ai { * @param autoragId Instance ID */ autorag(autoragId: string): AutoRAG; - run< - Name extends keyof AiModelList, - Options extends AiOptions, - InputOptions extends AiModelList[Name]["inputs"], - >( + // Batch request + run( model: Name, - inputs: InputOptions, - options?: Options, - ): Promise< - Options extends - | { - returnRawResponse: true; - } - | { - websocket: true; - } - ? Response - : InputOptions extends { - stream: true; - } - ? ReadableStream - : AiModelList[Name]["postProcessedOutputs"] - >; + inputs: { + requests: AiModelList[Name]["inputs"][]; + }, + options: AiOptions & { + queueRequest: true; + }, + ): Promise; + // Raw response + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options: AiOptions & { + returnRawResponse: true; + }, + ): Promise; + // WebSocket + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options: AiOptions & { + websocket: true; + }, + ): Promise; + // Streaming + run( + model: Name, + inputs: AiModelList[Name]["inputs"] & { + stream: true; + }, + options?: AiOptions, + ): Promise; + // Normal (default) - known model + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options?: AiOptions, + ): Promise; + // Unknown model (gateway fallback) + run( + model: string & {}, + inputs: Record, + options?: AiOptions, + ): Promise>; models(params?: AiModelsSearchParams): Promise; toMarkdown(): ToMarkdownService; toMarkdown( diff --git a/types/generated-snapshot/latest/index.ts b/types/generated-snapshot/latest/index.ts index e1b8fc4c8d6..e4b1992216e 100755 --- a/types/generated-snapshot/latest/index.ts +++ b/types/generated-snapshot/latest/index.ts @@ -10340,6 +10340,10 @@ export declare abstract class Base_Ai_Cf_Nvidia_Nemotron_3_120B_A12B { inputs: ChatCompletionsInput; postProcessedOutputs: ChatCompletionsOutput; } +export declare abstract class Base_Ai_Cf_Google_Gemma_4_26B_A4B_IT { + inputs: ChatCompletionsInput; + postProcessedOutputs: ChatCompletionsOutput; +} export interface AiModels { "@cf/huggingface/distilbert-sst-2-int8": BaseAiTextClassification; "@cf/stabilityai/stable-diffusion-xl-base-1.0": BaseAiTextToImage; @@ -10495,6 +10499,9 @@ export type ChatCompletionsInput = XOR< export interface InferenceUpstreamError extends Error {} export interface AiInternalError extends Error {} export type AiModelListType = Record; +export type AsyncResponse = { + request_id: string; +}; export declare abstract class Ai< AiModelList extends AiModelListType = AiModels, > { @@ -10513,29 +10520,52 @@ export declare abstract class Ai< * @param autoragId Instance ID */ autorag(autoragId: string): AutoRAG; - run< - Name extends keyof AiModelList, - Options extends AiOptions, - InputOptions extends AiModelList[Name]["inputs"], - >( + // Batch request + run( model: Name, - inputs: InputOptions, - options?: Options, - ): Promise< - Options extends - | { - returnRawResponse: true; - } - | { - websocket: true; - } - ? Response - : InputOptions extends { - stream: true; - } - ? ReadableStream - : AiModelList[Name]["postProcessedOutputs"] - >; + inputs: { + requests: AiModelList[Name]["inputs"][]; + }, + options: AiOptions & { + queueRequest: true; + }, + ): Promise; + // Raw response + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options: AiOptions & { + returnRawResponse: true; + }, + ): Promise; + // WebSocket + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options: AiOptions & { + websocket: true; + }, + ): Promise; + // Streaming + run( + model: Name, + inputs: AiModelList[Name]["inputs"] & { + stream: true; + }, + options?: AiOptions, + ): Promise; + // Normal (default) - known model + run( + model: Name, + inputs: AiModelList[Name]["inputs"], + options?: AiOptions, + ): Promise; + // Unknown model (gateway fallback) + run( + model: string & {}, + inputs: Record, + options?: AiOptions, + ): Promise>; models(params?: AiModelsSearchParams): Promise; toMarkdown(): ToMarkdownService; toMarkdown( From 312fda3d31a120788c4796ff35538057dda7c777 Mon Sep 17 00:00:00 2001 From: samscott89 Date: Thu, 9 Apr 2026 18:16:49 -0500 Subject: [PATCH 3/3] Use more specific name for AsyncResponse --- types/defines/ai.d.ts | 4 ++-- types/generated-snapshot/experimental/index.d.ts | 4 ++-- types/generated-snapshot/experimental/index.ts | 4 ++-- types/generated-snapshot/latest/index.d.ts | 4 ++-- types/generated-snapshot/latest/index.ts | 4 ++-- types/test/types/ai.ts | 2 +- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/types/defines/ai.d.ts b/types/defines/ai.d.ts index caaa62f8644..035ef04b1f3 100644 --- a/types/defines/ai.d.ts +++ b/types/defines/ai.d.ts @@ -5983,7 +5983,7 @@ export type ChatCompletionsInput = XOR< export interface InferenceUpstreamError extends Error {} export interface AiInternalError extends Error {} export type AiModelListType = Record; -export type AsyncResponse = { request_id: string }; +export type AiAsyncBatchResponse = { request_id: string }; export declare abstract class Ai< AiModelList extends AiModelListType = AiModels, > { @@ -6010,7 +6010,7 @@ export declare abstract class Ai< model: Name, inputs: { requests: AiModelList[Name]['inputs'][] }, options: AiOptions & { queueRequest: true } - ): Promise; + ): Promise; // Raw response run( diff --git a/types/generated-snapshot/experimental/index.d.ts b/types/generated-snapshot/experimental/index.d.ts index 989bf406a1c..94e8543314b 100755 --- a/types/generated-snapshot/experimental/index.d.ts +++ b/types/generated-snapshot/experimental/index.d.ts @@ -11182,7 +11182,7 @@ type ChatCompletionsInput = XOR< interface InferenceUpstreamError extends Error {} interface AiInternalError extends Error {} type AiModelListType = Record; -type AsyncResponse = { +type AiAsyncBatchResponse = { request_id: string; }; declare abstract class Ai { @@ -11210,7 +11210,7 @@ declare abstract class Ai { options: AiOptions & { queueRequest: true; }, - ): Promise; + ): Promise; // Raw response run( model: Name, diff --git a/types/generated-snapshot/experimental/index.ts b/types/generated-snapshot/experimental/index.ts index 828cba31af5..fcd873bd288 100755 --- a/types/generated-snapshot/experimental/index.ts +++ b/types/generated-snapshot/experimental/index.ts @@ -11191,7 +11191,7 @@ export type ChatCompletionsInput = XOR< export interface InferenceUpstreamError extends Error {} export interface AiInternalError extends Error {} export type AiModelListType = Record; -export type AsyncResponse = { +export type AiAsyncBatchResponse = { request_id: string; }; export declare abstract class Ai< @@ -11221,7 +11221,7 @@ export declare abstract class Ai< options: AiOptions & { queueRequest: true; }, - ): Promise; + ): Promise; // Raw response run( model: Name, diff --git a/types/generated-snapshot/latest/index.d.ts b/types/generated-snapshot/latest/index.d.ts index 427ddbfc2b9..f83f221aa40 100755 --- a/types/generated-snapshot/latest/index.d.ts +++ b/types/generated-snapshot/latest/index.d.ts @@ -10490,7 +10490,7 @@ type ChatCompletionsInput = XOR< interface InferenceUpstreamError extends Error {} interface AiInternalError extends Error {} type AiModelListType = Record; -type AsyncResponse = { +type AiAsyncBatchResponse = { request_id: string; }; declare abstract class Ai { @@ -10518,7 +10518,7 @@ declare abstract class Ai { options: AiOptions & { queueRequest: true; }, - ): Promise; + ): Promise; // Raw response run( model: Name, diff --git a/types/generated-snapshot/latest/index.ts b/types/generated-snapshot/latest/index.ts index e4b1992216e..5070ead9ef8 100755 --- a/types/generated-snapshot/latest/index.ts +++ b/types/generated-snapshot/latest/index.ts @@ -10499,7 +10499,7 @@ export type ChatCompletionsInput = XOR< export interface InferenceUpstreamError extends Error {} export interface AiInternalError extends Error {} export type AiModelListType = Record; -export type AsyncResponse = { +export type AiAsyncBatchResponse = { request_id: string; }; export declare abstract class Ai< @@ -10529,7 +10529,7 @@ export declare abstract class Ai< options: AiOptions & { queueRequest: true; }, - ): Promise; + ): Promise; // Raw response run( model: Name, diff --git a/types/test/types/ai.ts b/types/test/types/ai.ts index 17c7d32d958..a8ad0ff7bb3 100644 --- a/types/test/types/ai.ts +++ b/types/test/types/ai.ts @@ -40,7 +40,7 @@ export const handler: ExportedHandler<{ AI: Ai }> = { { requests: [{ prompt: 'hello' }, { prompt: 'world' }] }, { queueRequest: true as const } ); - expectType(result); + expectType(result); } // Gateway model -- unknown model name, permissive types