UNPKG

agents

Version:

A home for your AI agents

2,496 lines • 89 kB
import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "./classPrivateFieldGet2-DZBYAB34.js";
import { t as _classPrivateMethodInitSpec } from "./classPrivateMethodInitSpec-qMjJ6sHQ.js";
import { C as isCatalogModelId, S as isRetryableStatus, T as isRunPathLanguageModelId, _ as isGatewayErrorEnvelope, a as normalizeChatCompletion, b as classifyStatus, f as createTransport$1, g as headersToObject, h as errorFromResponse$1, i as normalizeChatCompletionsStream, m as errorFromGatewayEnvelope$1, n as imageMediaTypeOf, o as prepareChatCompletionsRequest, p as definedHeaders, r as sseDataStream, t as buildImageInput, u as requireGatewayProvider, v as unwrapEnvelope$1, w as isDynamicRouteId, x as isAbortError, y as CloudflareAIError$1 } from "./images-CfuRCjwq.js";
import { a as parseModelOptions, i as mergeModelOptions, n as PROVIDER_OPTIONS_KEY, o as resolveOptions, r as gatewayLayerOf } from "./settings-Dk2il2Hf.js";
import { APICallError, TooManyEmbeddingValuesForCallError } from "@ai-sdk/provider";
//#region src/models/ai-sdk/errors.ts
/**
* The error a string that names no model this provider runs produces.
* Language models take Workers AI ids, AI Gateway catalog ids and dynamic
* routes; the modality methods take Workers AI ids only.
*/
function notAModelId(modelId, accepts) {
	return /* @__PURE__ */ new TypeError(accepts === "language" ? `"${modelId}" is not a model id this provider knows. Use a Workers AI id ("@cf/zai-org/glm-4.7-flash"), an AI Gateway catalog id ("<author>/<model>", e.g. "openai/gpt-5.5" or "anthropic/claude-opus-4.8"), or a dynamic route ("dynamic/<route>").` : `"${modelId}" is not a Workers AI model id. The embedding, image, speech, transcription and reranking methods take Workers AI ids, which start with "@cf/".`);
}
/**
* The gate for the modality methods — embedding, image, speech,
* transcription, reranking — and their fallback legs: a Workers AI id and
* nothing else. Catalog models and dynamic routes are chat models here.
*/
function requireWorkersAIId(modelId) {
	if (isDynamicRouteId(modelId)) throw new TypeError(`"${modelId}" is an AI Gateway dynamic route, and a dynamic route is a chat model: it answers in the chat-completions shape. Use it as ai("${modelId}"); the embedding, image, speech, transcription and reranking methods take "@cf/" ids.`);
	if (!modelId.startsWith("@cf/")) throw notAModelId(modelId, "workers-ai");
	return modelId;
}
/** Whether a string names a language model: any of the three id kinds. */
function isLanguageModelId(modelId) {
	return isRunPathLanguageModelId(modelId) || isCatalogModelId(modelId);
}
/**
* The gate every string that names a language model passes through, wherever
* it enters the module: `ai("…")`, a per-model fallback leg, or a leg read
* out of the per-call `providerOptions.cloudflare` bag. All three kinds run on
* `env.AI.run`. Thrown at the line that supplied the id rather than at the
* request it would have made.
*/
function requireLanguageModelId(modelId) {
	if (!isLanguageModelId(modelId)) throw notAModelId(modelId, "language");
	return modelId;
}
/**
* Error thrown for every failed Cloudflare AI call, whether it came back as a
* non-2xx `Response` or as a thrown Workers AI binding error.
*
* Extends the AI SDK's `APICallError` so `ai`'s built-in retry loop honours
* {@link CloudflareAIError.isRetryable} without any extra wiring.
*/
var CloudflareAIError = class CloudflareAIError extends APICallError {
	constructor(options) {
		super({
			cause: options.cause,
			data: options.data,
			isRetryable: options.isRetryable ?? isRetryableStatus(options.status),
			message: options.message,
			requestBodyValues: options.requestBodyValues,
			responseBody: options.responseBody,
			responseHeaders: options.responseHeaders,
			statusCode: options.status,
			url: options.url
		});
		this.name = "CloudflareAIError";
		this.status = options.status;
		this.code = options.code;
		this.logId = options.logId;
		this.model = options.model;
		this.attempts = options.attempts;
	}
	/** Lifts the framework-neutral core error into the AI SDK error type. */
	static fromCore(error) {
		return new CloudflareAIError({
			attempts: error.attempts,
			cause: error.cause,
			code: error.code,
			data: error.data,
			isRetryable: error.isRetryable,
			logId: error.logId,
			message: error.message,
			model: error.model,
			requestBodyValues: error.requestBodyValues,
			responseBody: error.responseBody,
			responseHeaders: error.responseHeaders,
			status: error.status,
			url: error.url
		});
	}
};
/**
* Converts whatever the core transport threw into what AI SDK callers expect:
* core errors become {@link CloudflareAIError}s; everything else passes
* through untouched.
*/
function toAISDKError(error) {
	return error instanceof CloudflareAIError$1 ? CloudflareAIError.fromCore(error) : error;
}
//#endregion
//#region src/models/ai-sdk/settings.ts
/**
* The fallback legs for one call, in the caller's order. Precedence matches
* the rest of the option merge: a per-call list replaces the per-model one
* rather than extending it.
*/
function fallbackLegs(model, call) {
	return call?.fallback ?? model?.fallback ?? [];
}
/**
* Reads per-call options out of AI SDK `providerOptions.cloudflare`.
*
* This is the one place the untyped per-call bag enters the module, so it is
* where the "`@cf/` ids only" rule is enforced for the fallback legs a caller
* wrote there: `parseModelOptions` is framework-neutral and drops rather than
* throws, and no type ever sees that JSON. A vendor id in a leg would
* otherwise reach `env.AI.run` — the path a vendor model must never take.
*/
function parseCallOptions(providerOptions) {
	const options = parseModelOptions(providerOptions?.[PROVIDER_OPTIONS_KEY]);
	for (const leg of options?.fallback ?? []) requireLanguageModelId(leg);
	return options;
}
/**
* Per-call options for the modality models — embedding, image, speech,
* transcription, reranking. The same bag as {@link parseCallOptions}, but a
* fallback leg must be a Workers AI id: a dynamic route answers in the
* chat-completions shape, which none of those models can read.
*/
function parseModalityCallOptions(providerOptions) {
	const options = parseModelOptions(providerOptions?.[PROVIDER_OPTIONS_KEY]);
	for (const leg of options?.fallback ?? []) requireWorkersAIId(leg);
	return options;
}
/**
* The options each model this provider built was built with.
*
* A `WeakMap` rather than a field: `createAI` needs to read a leg's own bag
* back when it layers a fallback chain's gateway options over it, and that is
* this module's business rather than part of any model's public shape.
*/
const MODEL_OPTIONS = /* @__PURE__ */ new WeakMap();
/** Records the options a model of this provider was built with. */
function rememberOptions(model, options) {
	if (options !== void 0) MODEL_OPTIONS.set(model, options);
}
/** The options a model of this provider was built with, if it is one. */
function optionsOf(model) {
	return model !== null && typeof model === "object" ? MODEL_OPTIONS.get(model) : void 0;
}
/**
* The per-call options a fallback leg is asked with: everything the caller
* sent, minus the fallback list itself.
*
* Every leg here is a model object, so a leg that read the same
* `providerOptions.cloudflare.fallback` would build the whole chain again —
* once per leg, each of those re-expanding in turn. The rest of the per-call
* bag stays, so a leg still honours a per-call `cacheTtl` or gateway id.
*/
function withoutCallFallback(options) {
	const providerOptions = options.providerOptions;
	const ours = providerOptions?.[PROVIDER_OPTIONS_KEY];
	if (ours === void 0 || !("fallback" in ours)) return options;
	const bag = { ...ours };
	delete bag.fallback;
	return {
		...options,
		providerOptions: {
			...providerOptions,
			[PROVIDER_OPTIONS_KEY]: bag
		}
	};
}
//#endregion
//#region src/models/ai-sdk/transport.ts
/**
* The typed AI SDK error for the gateway's own error envelope — a missing
* gateway, an unpaid account — on the provider-native path.
*/
function errorFromGatewayEnvelope(body, context) {
	return CloudflareAIError.fromCore(errorFromGatewayEnvelope$1(body, context));
}
/**
* Unwraps the Cloudflare `{ success, result }` envelope, throwing an AI SDK
* {@link CloudflareAIError} for a failed envelope.
*/
function unwrapEnvelope(value, context) {
	try {
		return unwrapEnvelope$1(value, context);
	} catch (error) {
		throw toAISDKError(error);
	}
}
/** The typed AI SDK error for a non-2xx response. */
async function errorFromResponse(response, context) {
	return CloudflareAIError.fromCore(await errorFromResponse$1(response, context));
}
/**
* Builds the transport for a set of provider settings, with every failure
* lifted into the AI SDK error type.
*/
function createTransport(settings) {
	const inner = createTransport$1(settings);
	return {
		logIdFallback: (response) => inner.logIdFallback(response),
		async native(request) {
			try {
				return await inner.native(request);
			} catch (error) {
				throw toAISDKError(error);
			}
		},
		async run(request) {
			try {
				return await inner.run(request);
			} catch (error) {
				throw toAISDKError(error);
			}
		},
		url: inner.url
	};
}
//#endregion
//#region src/models/ai-sdk/models/fallback.ts
/**
* Client-side fallback across models: the primary is tried first, then each
* fallback in order, when a call fails before producing output. Shared by the
* language and embedding models.
*/
/**
* Runs each leg in order until one answers. Legs may be anything — a model id
* this provider owns, or a whole other `LanguageModelV4` — because a leg is
* only ever a name and a thunk here.
*/
async function withFallbackLegs(legs, url) {
	const attempts = [];
	let lastError;
	for (const leg of legs) try {
		return await leg.run();
	} catch (error) {
		if (isAbortError(error)) throw error;
		attempts.push({
			error,
			model: leg.model
		});
		lastError = error;
	}
	if (attempts.length <= 1) throw lastError;
	throw fallbackError(lastError, attempts, url);
}
/**
* The parts a stream emits before it has said anything: protocol preamble
* that every leg would repeat. Anything else is output the caller has seen,
* after which the leg is committed and a later failure is its own.
*/
const PREAMBLE_PARTS = /* @__PURE__ */ new Set([
	"stream-start",
	"response-metadata",
	"text-start",
	"reasoning-start",
	"tool-input-start",
	"raw"
]);
/**
* {@link withFallbackLegs} for streams. A 2xx that then fails before any
* output — an `error` part, an error finish, or a broken body — is still a
* failed leg, so the next one is tried; the preamble the failed leg sent is
* dropped and the answering leg's own is delivered instead. Once a leg has
* produced output it is the answer, failures and all.
*/
async function withStreamFallbackLegs(legs, url) {
	if (legs.length <= 1) return withFallbackLegs(legs, url);
	const attempts = [];
	let lastError;
	for (const leg of legs) {
		let result;
		try {
			result = await leg.run();
		} catch (error) {
			if (isAbortError(error)) throw error;
			attempts.push({
				error,
				model: leg.model
			});
			lastError = error;
			continue;
		}
		const reader = result.stream.getReader();
		const preamble = [];
		let failure;
		let committed = false;
		try {
			for (;;) {
				const { done, value } = await reader.read();
				if (done) {
					committed = true;
					break;
				}
				if (value.type === "error") {
					failure = value.error;
					break;
				}
				if (value.type === "finish" && value.finishReason.unified === "error") {
					failure = streamFailure(leg.model, url, value.finishReason.raw);
					break;
				}
				preamble.push(value);
				if (!PREAMBLE_PARTS.has(value.type)) {
					committed = true;
					break;
				}
			}
		} catch (error) {
			if (isAbortError(error)) throw error;
			failure = error;
		}
		if (!committed) {
			await reader.cancel().catch(() => void 0);
			attempts.push({
				error: failure,
				model: leg.model
			});
			lastError = failure;
			continue;
		}
		return {
			...result,
			stream: resumed(preamble, reader)
		};
	}
	if (attempts.length <= 1) throw lastError;
	throw fallbackError(lastError, attempts, url);
}
/** The parts already read, then the rest of the stream, as one stream. */
function resumed(head, reader) {
	return new ReadableStream({
		cancel(reason) {
			return reader.cancel(reason);
		},
		async pull(controller) {
			const { done, value } = await reader.read();
			if (done) controller.close();
			else controller.enqueue(value);
		},
		start(controller) {
			for (const part of head) controller.enqueue(part);
		}
	});
}
/** A stream that ended in an error finish before it produced anything. */
function streamFailure(model, url, raw) {
	return new CloudflareAIError({
		code: "provider-error",
		isRetryable: false,
		message: `The stream from ${model} ended in an error before producing any output${raw === void 0 ? "" : ` (${raw})`}.`,
		model,
		requestBodyValues: void 0,
		url
	});
}
async function withFallback(primary, fallback, url, run) {
	return withFallbackLegs([primary, ...fallback].map((modelId) => ({
		model: modelId,
		run: () => run(modelId)
	})), url);
}
/** Rebuilds the last error with the whole attempt chain attached. */
function fallbackError(lastError, attempts, url) {
	const models = attempts.map((attempt) => attempt.model).join(", ");
	if (lastError instanceof CloudflareAIError) return new CloudflareAIError({
		attempts,
		cause: lastError,
		code: lastError.code,
		data: lastError.data,
		isRetryable: lastError.isRetryable,
		logId: lastError.logId,
		message: `All models failed (${models}). Last error: ${lastError.message}`,
		model: lastError.model,
		requestBodyValues: lastError.requestBodyValues,
		responseBody: lastError.responseBody,
		responseHeaders: lastError.responseHeaders,
		status: lastError.status,
		url: lastError.url
	});
	if (APICallError.isInstance(lastError)) return new CloudflareAIError({
		attempts,
		cause: lastError,
		code: classifyStatus(lastError.statusCode),
		isRetryable: lastError.isRetryable,
		message: `All models failed (${models}). Last error: ${lastError.message}`,
		model: attempts.at(-1)?.model ?? "",
		requestBodyValues: lastError.requestBodyValues,
		responseBody: lastError.responseBody,
		responseHeaders: lastError.responseHeaders,
		status: lastError.statusCode,
		url: lastError.url
	});
	return new CloudflareAIError({
		attempts,
		cause: lastError,
		code: "unknown",
		isRetryable: false,
		message: `All models failed (${models}). Last error: ${String(lastError)}`,
		model: attempts.at(-1)?.model ?? "",
		requestBodyValues: void 0,
		url
	});
}
//#endregion
//#region src/models/ai-sdk/wires/shared.ts
/** Usage with every field unknown — the shape callers can always fall back to. */
function emptyUsage() {
	return {
		inputTokens: {
			cacheRead: void 0,
			cacheWrite: void 0,
			noCache: void 0,
			total: void 0
		},
		outputTokens: {
			reasoning: void 0,
			text: void 0,
			total: void 0
		}
	};
}
/** A fresh id for a stream part or a tool call the provider did not name. */
function newId() {
	return crypto.randomUUID();
}
const BASE64_CHUNK = 32768;
/** Base64-encodes bytes without blowing the argument limit on large files. */
function toBase64(data) {
	if (typeof data === "string") return data;
	let binary = "";
	for (let offset = 0; offset < data.length; offset += BASE64_CHUNK) binary += String.fromCharCode(...data.subarray(offset, offset + BASE64_CHUNK));
	return btoa(binary);
}
/** Reads a record field without widening everything to `any`. */
function record(value) {
	return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
}
/** Reads an array field, or `undefined` when it is not one. */
function array(value) {
	return Array.isArray(value) ? value : void 0;
}
/** Reads a string field, or `undefined` when it is not one. */
function text(value) {
	return typeof value === "string" ? value : void 0;
}
/** Reads a number field, or `undefined` when it is not one. */
function count(value) {
	return typeof value === "number" ? value : void 0;
}
/** The finish reason used when a provider ends a stream without saying why. */
function defaultFinishReason() {
	return {
		raw: void 0,
		unified: "stop"
	};
}
//#endregion
//#region src/models/ai-sdk/models/embedding.ts
/**
* Workers AI batches up to this many strings per embedding call.
*
* @see https://developers.cloudflare.com/workers-ai/platform/limits/
*/
const MAX_EMBEDDINGS_PER_CALL = 3e3;
var _config$3 = /* @__PURE__ */ new WeakMap();
var _options$3 = /* @__PURE__ */ new WeakMap();
/**
* An `EmbeddingModelV4` over the Workers AI text-embedding catalog. The run
* path takes `{ text: string[] }` and answers `{ data, shape, usage }`,
* sometimes inside the Cloudflare envelope.
*/
var CloudflareEmbeddingModel = class {
	constructor(modelId, options, config) {
		this.specificationVersion = "v4";
		this.provider = "cloudflare";
		this.maxEmbeddingsPerCall = MAX_EMBEDDINGS_PER_CALL;
		this.supportsParallelCalls = true;
		_classPrivateFieldInitSpec(this, _config$3, void 0);
		_classPrivateFieldInitSpec(this, _options$3, void 0);
		options?.fallback?.forEach((leg) => {
			if (typeof leg === "string") requireWorkersAIId(leg);
		});
		this.modelId = modelId;
		_classPrivateFieldSet2(_options$3, this, options);
		_classPrivateFieldSet2(_config$3, this, config);
	}
	async doEmbed(options) {
		if (options.values.length > MAX_EMBEDDINGS_PER_CALL) throw new TooManyEmbeddingValuesForCallError({
			maxEmbeddingsPerCall: MAX_EMBEDDINGS_PER_CALL,
			modelId: this.modelId,
			provider: this.provider,
			values: options.values
		});
		const resolved = resolveOptions(_classPrivateFieldGet2(_config$3, this).providerGateway, _classPrivateFieldGet2(_options$3, this), parseModalityCallOptions(options.providerOptions));
		const input = { text: options.values };
		const transport = _classPrivateFieldGet2(_config$3, this).transport;
		return withFallback(this.modelId, resolved.fallback, transport.url, async (modelId) => {
			const response = await transport.run({
				gateway: resolved.gateway,
				headers: {
					...definedHeaders(options.headers),
					...resolved.headers,
					...resolved.sessionAffinity === void 0 ? {} : { "x-session-affinity": resolved.sessionAffinity }
				},
				input,
				model: modelId,
				signal: options.abortSignal
			});
			if (!response.ok) throw await errorFromResponse(response, {
				model: modelId,
				requestBodyValues: input,
				url: transport.url
			});
			const body = record(unwrapEnvelope(await response.json(), {
				model: modelId,
				status: response.status,
				url: transport.url
			})) ?? {};
			const embeddings = (array(body.data) ?? []).map((row) => (array(row) ?? []).filter((value) => typeof value === "number"));
			const tokens = count(record(body.usage)?.prompt_tokens);
			return {
				embeddings,
				response: { headers: headersToObject(response.headers) },
				...tokens === void 0 ? {} : { usage: { tokens } },
				warnings: []
			};
		});
	}
};
//#endregion
//#region src/models/ai-sdk/wires/chat-completions.ts
function fullMediaType(mediaType) {
	return mediaType.includes("/") ? mediaType : `${mediaType}/jpeg`;
}
function unsupported$1(feature, details) {
	return {
		details,
		feature,
		type: "unsupported"
	};
}
/** A compat-layer warning in the AI SDK's warning shape. */
function fromCompat(warning) {
	return unsupported$1(warning.feature, warning.message);
}
/**
* Gemini 3 signs each assistant turn and each function call with an opaque
* `extra_content.google.thought_signature`, and rejects a function call
* replayed in the same turn without it. Nothing here reads the payload: it is
* carried out as provider metadata and put back verbatim on the next turn.
*
* `extraContent` is the signature of the assistant message; `toolExtraContent`
* is the one that belongs to an individual `tool_calls[i]` entry.
*/
function extraContentMetadata(fields) {
	const metadata = {};
	const message = record(fields.extraContent);
	const tool = record(fields.toolExtraContent);
	if (message !== void 0) metadata.extraContent = message;
	if (tool !== void 0) metadata.toolExtraContent = tool;
	if (Object.keys(metadata).length === 0) return {};
	return { providerMetadata: { [PROVIDER_OPTIONS_KEY]: metadata } };
}
/** Reads back what {@link extraContentMetadata} stored on a replayed part. */
function storedExtraContent(options, key) {
	return record(options?.[PROVIDER_OPTIONS_KEY]?.[key]);
}
function toolResultText(output, warnings) {
	switch (output.type) {
		case "text":
		case "error-text": return output.value;
		case "json":
		case "error-json": return JSON.stringify(output.value);
		case "execution-denied": return output.reason ?? "The user denied this tool call.";
		case "content": {
			const dropped = output.value.filter((part) => part.type !== "text");
			if (dropped.length > 0) warnings.push(unsupported$1("tool-result-part", `Tool result parts of type ${[...new Set(dropped.map((part) => part.type))].join(", ")} cannot be sent on the chat-completions wire and were dropped.`));
			return output.value.map((part) => part.type === "text" ? part.text : "").filter((part) => part !== "").join("\n");
		}
		default: return String(output);
	}
}
function userContent(message, warnings) {
	const parts = [];
	let onlyText = true;
	for (const part of message.content) {
		if (part.type === "text") {
			parts.push({
				text: part.text,
				type: "text"
			});
			continue;
		}
		if (!(part.mediaType === "image" || part.mediaType.startsWith("image/"))) {
			warnings.push(unsupported$1("file-part", `Files of type ${part.mediaType} are not supported on this wire and were dropped.`));
			continue;
		}
		if (part.data.type === "data") {
			onlyText = false;
			parts.push({
				image_url: { url: `data:${fullMediaType(part.mediaType)};base64,${toBase64(part.data.data)}` },
				type: "image_url"
			});
			continue;
		}
		if (part.data.type === "url") {
			onlyText = false;
			parts.push({
				image_url: { url: part.data.url.toString() },
				type: "image_url"
			});
			continue;
		}
		warnings.push(unsupported$1("file-part", "Only inline data and URL images are supported; the file was dropped."));
	}
	if (onlyText) return parts.map((part) => part.type === "text" ? part.text : "").join("");
	return parts;
}
function convertPrompt(prompt, warnings) {
	const messages = [];
	for (const message of prompt) switch (message.role) {
		case "system":
			messages.push({
				content: message.content,
				role: "system"
			});
			break;
		case "user":
			messages.push({
				content: userContent(message, warnings),
				role: "user"
			});
			break;
		case "assistant": {
			let content = "";
			let reasoning = "";
			let extraContent;
			const toolCalls = [];
			for (const part of message.content) {
				extraContent ??= storedExtraContent(part.providerOptions, "extraContent");
				if (part.type === "text") content += part.text;
				else if (part.type === "reasoning") reasoning += part.text;
				else if (part.type === "tool-call") {
					const toolExtra = storedExtraContent(part.providerOptions, "toolExtraContent");
					toolCalls.push({
						function: {
							arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input ?? {}),
							name: part.toolName
						},
						id: part.toolCallId,
						type: "function",
						...toolExtra === void 0 ? {} : { extra_content: toolExtra }
					});
				} else warnings.push(unsupported$1("assistant-part", `Assistant "${part.type}" parts cannot be replayed on the chat-completions wire and were dropped.`));
			}
			messages.push({
				content,
				...reasoning !== "" ? { reasoning_content: reasoning } : {},
				...extraContent === void 0 ? {} : { extra_content: extraContent },
				...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
				role: "assistant"
			});
			break;
		}
		case "tool":
			for (const part of message.content) {
				if (part.type !== "tool-result") {
					warnings.push(unsupported$1("tool-part", `Tool "${part.type}" parts are not carried on the chat-completions wire and were dropped.`));
					continue;
				}
				messages.push({
					content: toolResultText(part.output, warnings),
					name: part.toolName,
					role: "tool",
					tool_call_id: part.toolCallId
				});
			}
			break;
		default: throw new Error(`Unsupported message role: ${String(message)}`);
	}
	return messages;
}
/** Folds the AI SDK's schema description into the schema itself. */
function foldSchema(schema, description) {
	if (description === void 0 || schema.description !== void 0) return schema;
	return {
		...schema,
		description
	};
}
function responseFormat(options) {
	const format = options.responseFormat;
	if (format === void 0 || format.type !== "json") return void 0;
	if (format.schema === void 0) return { type: "json_object" };
	return {
		json_schema: {
			name: format.name ?? "response",
			schema: foldSchema(format.schema, format.description),
			strict: true
		},
		type: "json_schema"
	};
}
function toolFields(options, warnings) {
	const tools = [];
	for (const tool of options.tools ?? []) {
		if (tool.type !== "function") {
			warnings.push(unsupported$1("provider-tool", `Provider tool "${tool.name}" is not available through the Cloudflare run path.`));
			continue;
		}
		tools.push({
			function: {
				description: tool.description,
				name: tool.name,
				parameters: tool.inputSchema
			},
			type: "function"
		});
	}
	if (tools.length === 0) return {};
	const choice = options.toolChoice;
	let toolChoice;
	switch (choice?.type) {
		case void 0: break;
		case "auto":
			toolChoice = "auto";
			break;
		case "none":
			toolChoice = "none";
			break;
		case "required":
			toolChoice = "required";
			break;
		case "tool":
			toolChoice = {
				function: { name: choice.toolName },
				type: "function"
			};
			break;
		default: throw new Error(`Unsupported tool choice: ${String(choice)}`);
	}
	return {
		tools,
		...toolChoice === void 0 ? {} : { tool_choice: toolChoice }
	};
}
/**
* The strict-body spelling of the reasoning request: an effort level, or
* `null` for "off". The compat layer turns `null` into whatever the model
* understands, or warns when nothing does.
*/
function reasoningEffortField(options, context) {
	if (context.reasoningEffort !== void 0) return { reasoning_effort: context.reasoningEffort };
	const reasoning = options.reasoning;
	if (reasoning === void 0 || reasoning === "provider-default") return {};
	if (reasoning === "none") return { reasoning_effort: null };
	return { reasoning_effort: reasoning };
}
/**
* Builds a strict OpenAI chat-completions body from AI SDK call options and
* runs it through the compat layer for the target model.
*/
function buildOpenAIRequest(options, context) {
	const warnings = [];
	if (options.topK !== void 0) warnings.push(unsupported$1("topK", "topK is not part of the chat-completions API."));
	const format = responseFormat(options);
	const prepared = prepareChatCompletionsRequest({
		messages: convertPrompt(options.prompt, warnings),
		...options.maxOutputTokens === void 0 ? {} : { max_tokens: options.maxOutputTokens },
		...options.temperature === void 0 ? {} : { temperature: options.temperature },
		...options.topP === void 0 ? {} : { top_p: options.topP },
		...options.stopSequences === void 0 || options.stopSequences.length === 0 ? {} : { stop: options.stopSequences },
		...options.seed === void 0 ? {} : { seed: options.seed },
		...options.frequencyPenalty === void 0 ? {} : { frequency_penalty: options.frequencyPenalty },
		...options.presencePenalty === void 0 ? {} : { presence_penalty: options.presencePenalty },
		...format === void 0 ? {} : { response_format: format },
		...toolFields(options, warnings),
		...reasoningEffortField(options, context),
		...context.chatTemplateKwargs === void 0 ? {} : { chat_template_kwargs: context.chatTemplateKwargs },
		...context.stream ? {
			stream: true,
			stream_options: { include_usage: true }
		} : {}
	}, context.modelId);
	return {
		body: prepared.body,
		reasoningOff: prepared.reasoningOff,
		warnings: [...warnings, ...prepared.warnings.map(fromCompat)]
	};
}
function mapFinishReason(raw) {
	const value = text(raw);
	switch (value) {
		case "stop": return {
			raw: value,
			unified: "stop"
		};
		case "length": return {
			raw: value,
			unified: "length"
		};
		case "tool_calls":
		case "function_call": return {
			raw: value,
			unified: "tool-calls"
		};
		case "content_filter": return {
			raw: value,
			unified: "content-filter"
		};
		case "error": return {
			raw: value,
			unified: "error"
		};
		default: return value === void 0 ? defaultFinishReason() : {
			raw: value,
			unified: "other"
		};
	}
}
function mapUsage(raw) {
	const usage = record(raw);
	if (usage === void 0) return emptyUsage();
	const promptTokens = count(usage.prompt_tokens);
	const cacheRead = count(record(usage.prompt_tokens_details)?.cached_tokens);
	const reasoningTokens = count(record(usage.completion_tokens_details)?.reasoning_tokens);
	const completionTokens = count(usage.completion_tokens);
	const totalTokens = count(usage.total_tokens);
	const outputTotal = totalTokens !== void 0 && promptTokens !== void 0 ? Math.max(completionTokens ?? 0, totalTokens - promptTokens) : completionTokens;
	return {
		inputTokens: {
			cacheRead,
			cacheWrite: void 0,
			noCache: promptTokens === void 0 ? void 0 : Math.max(0, promptTokens - (cacheRead ?? 0)),
			total: promptTokens
		},
		outputTokens: {
			reasoning: reasoningTokens,
			text: void 0,
			total: outputTotal
		},
		raw: usage
	};
}
function toolCallsFrom(raw, messageExtra) {
	const calls = array(raw) ?? [];
	const content = [];
	for (const entry of calls) {
		const call = record(entry);
		if (call === void 0) continue;
		const fn = record(call.function);
		const name = text(fn?.name);
		if (name === void 0) continue;
		content.push({
			input: text(fn?.arguments) ?? "{}",
			toolCallId: text(call.id) ?? newId(),
			toolName: name,
			type: "tool-call",
			...extraContentMetadata({
				extraContent: content.length === 0 ? messageExtra : void 0,
				toolExtraContent: call.extra_content
			})
		});
	}
	return content;
}
/**
* Parses a non-streaming response. The body is normalized to the strict
* `chat.completion` shape first, so only `choices[0].message` is read.
*/
function parseOpenAIGeneration(json, modelId) {
	const body = normalizeChatCompletion(json, modelId);
	const choice = record(array(body.choices)?.[0]);
	const message = record(choice?.message);
	const content = [];
	const reasoning = text(message?.reasoning_content);
	if (reasoning !== void 0 && reasoning !== "") content.push({
		text: reasoning,
		type: "reasoning"
	});
	const answer = text(message?.content);
	const messageExtra = message?.extra_content;
	if (answer !== void 0 && answer !== "") content.push({
		text: answer,
		type: "text",
		...extraContentMetadata({ extraContent: messageExtra })
	});
	content.push(...toolCallsFrom(message?.tool_calls, answer === void 0 || answer === "" ? messageExtra : void 0));
	const created = count(body.created);
	return {
		content,
		finishReason: mapFinishReason(choice?.finish_reason),
		responseId: text(body.id),
		responseModelId: text(body.model),
		timestamp: created === void 0 ? void 0 : /* @__PURE__ */ new Date(created * 1e3),
		usage: mapUsage(body.usage)
	};
}
/**
* Streams strict OpenAI `chat.completion.chunk` events into AI SDK stream
* parts. Tool calls arrive either as incremental `arguments` fragments closed
* by a null-finalization chunk, or as one delta with the complete arguments
* string; both are handled. Usage is read from whichever chunk carries it.
*/
function openAIStreamParser() {
	const toolCalls = /* @__PURE__ */ new Map();
	let lastToolIndex;
	let textId;
	let reasoningId;
	let usage = emptyUsage();
	let finishReason;
	let sentMetadata = false;
	let receivedDone = false;
	let receivedEvent = false;
	/** Message-level `extra_content` (Gemini's thought signature). */
	let messageExtra;
	let messageExtraClaimed = false;
	let textEverOpened = false;
	/**
	* The message signature belongs on whichever part replays the message: the
	* text block when there is one, otherwise the first tool call.
	*/
	const claimMessageExtra = () => {
		if (messageExtraClaimed) return void 0;
		messageExtraClaimed = true;
		return messageExtra;
	};
	const closeReasoning = (controller) => {
		if (reasoningId === void 0) return;
		controller.enqueue({
			id: reasoningId,
			type: "reasoning-end"
		});
		reasoningId = void 0;
	};
	const closeText = (controller) => {
		if (textId === void 0) return;
		controller.enqueue({
			id: textId,
			type: "text-end",
			...extraContentMetadata({ extraContent: claimMessageExtra() })
		});
		textId = void 0;
	};
	const closeToolCall = (index, controller) => {
		const state = toolCalls.get(index);
		if (state === void 0 || state.closed) return;
		state.closed = true;
		controller.enqueue({
			id: state.id,
			type: "tool-input-end"
		});
		controller.enqueue({
			input: state.input,
			toolCallId: state.id,
			toolName: state.name,
			type: "tool-call",
			...extraContentMetadata({
				extraContent: textEverOpened ? void 0 : claimMessageExtra(),
				toolExtraContent: state.extraContent
			})
		});
	};
	const emitToolDeltas = (raw, controller) => {
		const deltas = array(raw);
		if (deltas === void 0) return;
		for (const entry of deltas) {
			const call = record(entry);
			if (call === void 0) continue;
			const fn = record(call.function);
			const name = text(fn?.name);
			const input = text(fn?.arguments);
			const id = text(call.id);
			const rawIndex = count(call.index);
			if (rawIndex === void 0 && id === void 0 && (name === void 0 || name === "") && (input === void 0 || input === "")) {
				if (lastToolIndex !== void 0) closeToolCall(lastToolIndex, controller);
				continue;
			}
			closeReasoning(controller);
			closeText(controller);
			const index = rawIndex ?? lastToolIndex ?? 0;
			let state = toolCalls.get(index);
			if (state?.closed === true) continue;
			if (state === void 0) {
				if (lastToolIndex !== void 0 && lastToolIndex !== index) closeToolCall(lastToolIndex, controller);
				state = {
					closed: false,
					extraContent: call.extra_content,
					id: id ?? newId(),
					input: "",
					name: name ?? ""
				};
				toolCalls.set(index, state);
				lastToolIndex = index;
				controller.enqueue({
					id: state.id,
					toolName: state.name,
					type: "tool-input-start"
				});
			} else {
				if (name !== void 0 && state.name === "") state.name = name;
				state.extraContent ??= call.extra_content;
			}
			if (input !== void 0 && input !== "") {
				state.input += input;
				controller.enqueue({
					delta: input,
					id: state.id,
					type: "tool-input-delta"
				});
			}
		}
	};
	return new TransformStream({
		flush(controller) {
			for (const index of toolCalls.keys()) closeToolCall(index, controller);
			closeReasoning(controller);
			closeText(controller);
			if (finishReason === void 0) finishReason = receivedEvent && !receivedDone ? {
				raw: "stream-truncated",
				unified: "error"
			} : defaultFinishReason();
			controller.enqueue({
				finishReason,
				type: "finish",
				usage
			});
		},
		transform(data, controller) {
			if (data === "") return;
			if (data === "[DONE]") {
				receivedDone = true;
				return;
			}
			let parsed;
			try {
				parsed = JSON.parse(data);
			} catch {
				return;
			}
			const chunk = record(parsed);
			if (chunk === void 0) return;
			receivedEvent = true;
			const failure = record(chunk.error);
			if (failure !== void 0) {
				closeReasoning(controller);
				closeText(controller);
				controller.enqueue({
					error: failure,
					type: "error"
				});
				finishReason = {
					raw: text(failure.type) ?? "error",
					unified: "error"
				};
				return;
			}
			if (!sentMetadata) {
				const created = count(chunk.created);
				const id = text(chunk.id);
				const modelId = text(chunk.model);
				if (id !== void 0 || modelId !== void 0) {
					sentMetadata = true;
					controller.enqueue({
						id,
						modelId,
						timestamp: created === void 0 ? void 0 : /* @__PURE__ */ new Date(created * 1e3),
						type: "response-metadata"
					});
				}
			}
			if (record(chunk.usage) !== void 0) usage = mapUsage(chunk.usage);
			const choice = record(array(chunk.choices)?.[0]);
			const delta = record(choice?.delta);
			if (delta !== void 0) {
				messageExtra ??= record(delta.extra_content);
				const reasoning = text(delta.reasoning_content);
				if (reasoning !== void 0 && reasoning !== "") {
					if (reasoningId === void 0) {
						reasoningId = newId();
						controller.enqueue({
							id: reasoningId,
							type: "reasoning-start"
						});
					}
					controller.enqueue({
						delta: reasoning,
						id: reasoningId,
						type: "reasoning-delta"
					});
				}
				const content = text(delta.content);
				if (content !== void 0 && content !== "") {
					closeReasoning(controller);
					if (textId === void 0) {
						textId = newId();
						textEverOpened = true;
						controller.enqueue({
							id: textId,
							type: "text-start"
						});
					}
					controller.enqueue({
						delta: content,
						id: textId,
						type: "text-delta"
					});
				}
				emitToolDeltas(delta.tool_calls, controller);
			}
			if (text(choice?.finish_reason) !== void 0) finishReason = mapFinishReason(choice?.finish_reason);
		}
	});
}
//#endregion
//#region src/models/ai-sdk/models/language.ts
function callHeaders(options, resolved) {
	const headers = definedHeaders(options.headers);
	Object.assign(headers, resolved.headers);
	if (resolved.sessionAffinity !== void 0) headers["x-session-affinity"] = resolved.sessionAffinity;
	return headers;
}
/**
* Builds the `providerMetadata.cloudflare` block every model in this module
* attaches to its result. Shared with the non-language models in
* `models/modality.ts`.
*/
function cloudflareMetadata(model, gateway, response, transport, provider) {
	const headers = response.headers;
	const rawStep = headers.get("cf-aig-step");
	const step = rawStep === null ? NaN : Number(rawStep);
	const metadata = {
		...gateway === void 0 ? {} : { gateway },
		model,
		...provider === void 0 ? {} : { provider },
		...Number.isNaN(step) ? {} : { step }
	};
	for (const [field, header] of [
		["cacheStatus", "cf-aig-cache-status"],
		["eventId", "cf-aig-event-id"],
		["requestId", "cf-aig-request-id"],
		["runId", "cf-aig-run-id"],
		["traceId", "cf-aig-trace-id"]
	]) {
		const value = headers.get(header);
		if (value !== null) metadata[field] = value;
	}
	const logId = headers.get("cf-aig-log-id") ?? transport.logIdFallback(response);
	if (logId !== void 0 && logId !== null) metadata.logId = logId;
	return { [PROVIDER_OPTIONS_KEY]: metadata };
}
function buildRequest(modelId, options, resolved, stream) {
	return buildOpenAIRequest(options, {
		chatTemplateKwargs: resolved.chatTemplateKwargs,
		modelId,
		reasoningEffort: resolved.reasoningEffort,
		stream
	});
}
/**
* Prepends the single `stream-start` part and stamps `providerMetadata` onto
* the `finish` part. A `TransformStream` rather than a manual enqueue so the
* pipeline keeps its backpressure and error propagation.
*/
function decorateStream(warnings, providerMetadata) {
	let started = false;
	const start = (controller) => {
		if (started) return;
		started = true;
		controller.enqueue({
			type: "stream-start",
			warnings
		});
	};
	return new TransformStream({
		flush(controller) {
			start(controller);
		},
		transform(part, controller) {
			start(controller);
			controller.enqueue(part.type === "finish" ? {
				...part,
				providerMetadata
			} : part);
		}
	});
}
/**
* Replays an already-complete generation as a stream. Workers AI sometimes
* answers a `stream: true` request with a plain JSON body; degrading
* gracefully beats failing the call.
*/
function oneShotStream(generation) {
	return new ReadableStream({ start(controller) {
		if (generation.responseId !== void 0 || generation.responseModelId !== void 0) controller.enqueue({
			id: generation.responseId,
			modelId: generation.responseModelId,
			timestamp: generation.timestamp,
			type: "response-metadata"
		});
		for (const part of generation.content) emitContent(part, controller);
		controller.enqueue({
			finishReason: generation.finishReason,
			type: "finish",
			usage: generation.usage
		});
		controller.close();
	} });
}
function emitContent(part, controller) {
	switch (part.type) {
		case "text": {
			const id = newId();
			controller.enqueue({
				id,
				type: "text-start"
			});
			controller.enqueue({
				delta: part.text,
				id,
				type: "text-delta"
			});
			controller.enqueue({
				id,
				type: "text-end",
				...part.providerMetadata === void 0 ? {} : { providerMetadata: part.providerMetadata }
			});
			break;
		}
		case "reasoning": {
			const id = newId();
			controller.enqueue({
				id,
				type: "reasoning-start"
			});
			controller.enqueue({
				delta: part.text,
				id,
				type: "reasoning-delta"
			});
			controller.enqueue({
				id,
				type: "reasoning-end",
				...part.providerMetadata === void 0 ? {} : { providerMetadata: part.providerMetadata }
			});
			break;
		}
		case "tool-call":
			controller.enqueue({
				id: part.toolCallId,
				toolName: part.toolName,
				type: "tool-input-start"
			});
			controller.enqueue({
				delta: part.input,
				id: part.toolCallId,
				type: "tool-input-delta"
			});
			controller.enqueue({
				id: part.toolCallId,
				type: "tool-input-end"
			});
			controller.enqueue(part);
			break;
		default: controller.enqueue(part);
	}
}
var _config$2 = /* @__PURE__ */ new WeakMap();
var _options$2 = /* @__PURE__ */ new WeakMap();
var _CloudflareLanguageModel_brand = /* @__PURE__ */ new WeakSet();
/**
* A `LanguageModelV4` backed by the Cloudflare run path. The model id decides
* the wire format; nothing about transport or wire format reaches user land.
*/
var CloudflareLanguageModel = class {
	constructor(modelId, options, config) {
		_classPrivateMethodInitSpec(this, _CloudflareLanguageModel_brand);
		this.specificationVersion = "v4";
		this.provider = "cloudflare";
		_classPrivateFieldInitSpec(this, _config$2, void 0);
		_classPrivateFieldInitSpec(this, _options$2, void 0);
		this.modelId = modelId;
		_classPrivateFieldSet2(_options$2, this, options);
		_classPrivateFieldSet2(_config$2, this, config);
		rememberOptions(this, options);
		this.supportedUrls = {};
	}
	async doGenerate(options) {
		const call = parseCallOptions(options.providerOptions);
		const resolved = _assertClassBrand(_CloudflareLanguageModel_brand, this, _resolve$1).call(this, call);
		const legOptions = withoutCallFallback(options);
		return _assertClassBrand(_CloudflareLanguageModel_brand, this, _withFallback$1).call(this, call, (model) => model.doGenerate(legOptions), async (modelId) => {
			const request = buildRequest(modelId, options, resolved, false);
			const response = await _assertClassBrand(_CloudflareLanguageModel_brand, this, _send).call(this, modelId, request, options, resolved);
			const generation = parseOpenAIGeneration(await _assertClassBrand(_CloudflareLanguageModel_brand, this, _readBody).call(this, modelId, request, response), modelId);
			return {
				content: generation.content,
				finishReason: generation.finishReason,
				providerMetadata: cloudflareMetadata(modelId, resolved.gateway.id, response, _classPrivateFieldGet2(_config$2, this).transport),
				request: { body: request.body },
				response: {
					headers: headersToObject(response.headers),
					id: generation.responseId,
					modelId: generation.responseModelId ?? modelId,
					timestamp: generation.timestamp
				},
				usage: generation.usage,
				warnings: request.warnings
			};
		});
	}
	async doStream(options) {
		const call = parseCallOptions(options.providerOptions);
		const resolved = _assertClassBrand(_CloudflareLanguageModel_brand, this, _resolve$1).call(this, call);
		const legOptions = withoutCallFallback(options);
		return _assertClassBrand(_CloudflareLanguageModel_brand, this, _withFallback$1).call(this, call, (model) => model.doStream(legOptions), async (modelId) => {
			const request = buildRequest(modelId, options, resolved, true);
			const response = await _assertClassBrand(_CloudflareLanguageModel_brand, this, _send).call(this, modelId, request, options, resolved);
			const providerMetadata = cloudflareMetadata(modelId, resolved.gateway.id, response, _classPrivateFieldGet2(_config$2, this).transport);
			const contentType = response.headers.get("content-type") ?? "";
			const decorate = decorateStream(request.warnings, providerMetadata);
			let parts;
			if (!contentType.includes("event-stream") || response.body === null) parts = oneShotStream(parseOpenAIGeneration(await _assertClassBrand(_CloudflareLanguageModel_brand, this, _readBody).call(this, modelId, request, response), modelId));
			else parts = response.body.pipeThrough(sseDataStream()).pipeThrough(normalizeChatCompletionsStream(modelId, { reasoningOff: request.reasoningOff })).pipeThrough(openAIStreamParser());
			return {
				request: { body: request.body },
				response: { headers: headersToObject(response.headers) },
				stream: parts.pipeThrough(decorate)
			};
		}, "stream");
	}
};
/**
* Reads a 2xx body as JSON and unwraps the Cloudflare envelope. A 2xx that
* is not JSON at all is a provider failure, not a caller bug, so it comes
* back as a {@link CloudflareAIError} rather than a bare `SyntaxError`.
*/
async function _readBody(modelId, request, response) {
	let json;
	try {
		json = await response.json();
	} catch (error) {
		throw new CloudflareAIError({
			cause: error,
			code: "provider-error",
			isRetryable: false,
			message: `The model answered ${response.status} with a body that is not JSON.`,
			model: modelId,
			requestBodyValues: request.body,
			status: response.status,
			url: _classPrivateFieldGet2(_config$2, this).transport.url
		});
	}
	return unwrapEnvelope(json, {
		model: modelId,
		status: response.status,
		url: _classPrivateFieldGet2(_config$2, this).transport.url
	});
}
function _resolve$1(call) {
	return resolveOptions(_classPrivateFieldGet2(_config$2, this).providerGateway, _classPrivateFieldGet2(_options$2, this), call);
}
async function _send(modelId, request, options, resolved) {
	const response = await _classPrivateFieldGet2(_config$2, this).transport.run({
		format: "chat-completions",
		gateway: resolved.gateway,
		headers: callHeaders(options, resolved),
		input: request.body,
		model: modelId,
		signal: options.abortSignal
	});
	if (!response.ok) throw await errorFromResponse(response, {
		model: modelId,
		requestBodyValues: request.body,
		url: _classPrivateFieldGet2(_config$2, this).transport.url
	});
	return response;
}
/**
* Runs this model, then each fallback leg in order. A leg named by id runs
* through this same pipeline; a leg that is a model object — a vendor model
* `ai()` already wrapped, or another provider's — is asked directly, so a
* chain can mix Workers AI with anything the AI SDK can reach.
*/
function _withFallback$1(call, runLeg, runSelf, mode = "generate") {
	const legs = fallbackLegs(_classPrivateFieldGet2(_options$2, this), call);
	const chain = (attempts) => mode === "stream" ? withStreamFallbackLegs(attempts, _classPrivateFieldGet2(_config$2, this).transport.url) : withFallbackLegs(attempts, _classPrivateFieldGet2(_config$2, this).transport.url);
	return chain([{
		model: this.modelId,
		run: () => runSelf(this.modelId)
	}, ...legs.map((leg) => typeof leg === "string" ? {
		model: requireLanguageModelId(leg),
		run: () => runSelf(leg)
	} : {
		model: leg.modelId,
		run: () => runLeg(leg)
	})]);
}
//#endregion
//#region src/models/ai-sdk/models/gateway.ts
/**
* Throws unless a model's provider keeps its settings where every `@ai-sdk/*`
* provider keeps them. Checked when the model is wrapped rather than when it
* is called, so the mistake is reported at the line that made it.
*/
function requireConfig(model) {
	const config = model.config;
	if (config === null || typeof config !== "object") throw new TypeError(`The model "${String(model.modelId ?? "unknown")}" from provider "${String(model.provider ?? "unknown")}" has no \`config\` setting, so AI Gateway cannot take over its requests. Every \`@ai-sdk/*\` provider has one; a middleware-wrapped or hand-written model does not. Wrap the provider's own model first and apply middleware afterwards: wrapLanguageModel({ model: ai(anthropic("claude-opus-4-8")), middleware }).`);
	return config;
}
/**
* A per-call copy of the upstream model whose `config.fetch` is ours. The
* user's model object is never touched: it may be shared across requests, and
* two concurrent calls must not see each other's transport.
*/
function cloneWithFetch(model, fetchImpl) {
	const config = requireConfig(model);
	const clone = Object.create(Object.getPrototypeOf(model));
	Object.defineProperties(clone, Object.getOwnPropertyDescriptors(model));
	Object.defineProperty(clone, "config", {
		configurable: true,
		enumerable: true,
		value: {
			...config,
			fetch: fetchImpl
		},
		writable: true
	});
	return clone;
}
/** The request URL, whichever of `fetch`'s two call shapes was used. */
function requestUrl(input) {
	if (typeof input === "string") return input;
	if (input instanceof URL) return input.href;
	return input.url;
}
/**
* The body of either `fetch` call form: the `RequestInit`'s when one was
* given, else the `Request`'s own, from a clone so the caller's object is
* neither consumed nor mutated.
*/
function requestBody(input, init) {
	if (init?.body !== void 0) return init.body;
	if (input instanceof Request && input.body !== null) return input.clone().body;
}
/** The method of either `fetch` call form. */
function requestMethod(input, init) {
	return init?.method ?? (input instanceof Request ? input.method : "POST");
}
/** The vendor model id out of a JSON body, for error reporting. */
function modelOfBody(body) {
	if (typeof body !== "string") return void 0;
	try {
		return modelOf(JSON.parse(body));
	} catch {
		return;
	}
}
/**
* The vendor's own request headers, minus the length of a body we are about
* to re-serialize, and minus the vendor's credential unless the model asked
* for it to be forwarded.
*/
function forwardedHeaders(init, input, route, vendorKey) {
	const source = init?.headers ?? (input instanceof Request ? input.headers : void 0);
	const dropped = /* @__PURE__ */ new Set([...vendorKey === "request" ? [] : route.authHeaders, "content-length"]);
	const headers = {};
	new Headers(source).forEach((value, key) => {
		if (!dropped.has(key.toLowerCase())) headers[key] = value;
	});
	return headers;
}
/** A response whose body can be read again, for the vendor's error handler. */
function replayed(response, text) {
	const bodyless = response.status === 204 || response.status === 205 || response.status === 304;
	return new Response(bodyless ? null : text, {
		headers: response.headers,
		status: response.status,
		statusText: response.statusText
	});
}
/**
* The `fetch` a routed model is given. It maps the vendor's URL onto a gateway
* provider slug and endpoint and sends the request there unchanged, through
* the binding; the answer is the vendor's own.
*/
function routedFetch(context) {
	const routed = async (input, init) => {
		const route = requireGatewayProvider(requestUrl(input));
		const body = requestBody(input, init);
		const { resolved } = context;
		const response = await context.config.transport.native({
			body,
			endpoint: route.endpoint,
			gateway: resolved.gateway,
			headers: {
				...forwardedHeaders(init, input, route, context.vendorKey),
				...resolved.headers,
				...resolved.sessionAffinity === void 0 ? {} : { "x-session-affinity": resolved.sessionAffinity }
			},
			method: requestMethod(input, init),
			provider: route.provider,
			signal: init?.signal ?? (input instanceof Request ? input.signal : void 0) ?? context.signal
		});
		context.capture.provider = route.provider;
		context.capture.response = response;
		if (response.ok) return response;
		const text = await response.text();
		let envelope;
		try {
			envelope = JSON.parse(text);
		} catch {
			return replayed(response, text);
		}
		if (!isGatewayErrorEnvelope(envelope)) return replayed(response, text);
		throw errorFromGatewayEnvelope(envelope, {
			model: modelOfBody(body) ?? route.provider,
			requestBodyValues: void 0,
			responseBody: text,
			responseHeaders: headersToObject(response.headers),
			status: response.status,
			url: context.config.transport.url
		});
	};
	return routed;
}
/** The model id out of a vendor body, for error reporting. */
function modelOf(query) {
	if (query === null || typeof query !== "object") return void 0;
	const model = query.model;
	return typeof model === "string" ? model : void 0;
}
/** The call options a routed model sees: ours are read out and removed. */
function withoutOurOptions(options) {
	const providerOptions = options.providerOptions;
	if (providerOptions === void 0 || !("cloudflare" in providerOptions)) return options;
	const rest = { ...providerOptions };
	delete rest[PROVIDER_OPTIONS_KEY];
	return {
		...options,
		providerOptions: rest
	};
}
/** Adds our metadata block without dropping the vendor's own keys. */
function mergeMetadata(existing, ours) {
	return {
		...existing,
		...ours
	};
}
/**
* The same metadata, in the shape an image result's provider metadata takes.
*
* `ImageModelV4ProviderMetadata` is not the shared shape: every provider block
* must carry an `images` array, one entry per generated image, and the AI SDK
* spreads that array when it merges the calls a multi-image request fanned out
* into. A block without one makes `generateImage` throw, and only the array's
* entries survive that merge, so ours goes inside it.
*/
function metadataFor(result, ours) {
	const images = result.images;
	if (!Array.isArray(images)) return ours;
	const block = ours[PROVIDER_OPTIONS_KEY];
	return {
		...ours,
		[PROVIDER_OPTIONS_KEY]: {
			...block,
			images: images.map(() => ({ ...block }))
		}
	};
}
/**
* The Workers AI knobs a routed vendor model cannot honour. They are not
* dropped silently: the vendor decides its own reasoning, so a caller who set
* `reasoningEffort` here is asking for something that never leaves.
*/
function ignoredWarnings(resolved) {
	const warnings = [];
	for (const [option, value] of [["reasoningEffort", resolved.reasoningEffort], ["chatTemplateKwargs", resolved.chatTemplateKwargs]]) {
		if (value === void 0) continue;
		warnings.push({
			details: `\`${option}\` is a Workers AI setting and does not apply to a model routed through AI Gateway; use the vendor's own reasoning options instead.`,
			feature: option,
			type: "unsupported"
		});
	}
	return warnings;
}
/**
* The Workers AI knobs a routed non-language model was asked with, added to
* the result's own `warnings` when it has one.
*
* Only the per-call `providerOptions.cloudflare` bag can carry them here —
* {@link RoutedModelOptions} leaves them off the per-model type — and only
* some v4 result shapes have somewhere to put a warning: image, speech,
* transcription and reranking results do, an embedding result does not.
*/
function routedWarnings(result, resolved) {
	const existing = result.warnings;
	if (!Array.isArray(existing)) return {};
	const warnings = ignoredWarnings(resolved);
	if (warnings.length === 0) return {};
	return { warnings: [...existing, ...warnings] };
}
/**
* Stamps the finish part with `providerMetadata.cloudflare`, and adds our own
* warnings to the `stream-start` the vendor emitted.
*/
function stampStream(metadata, warnings) {
	return new TransformStream({ transform(part, controller) {
		if (part.type === "finish") {
			controller.enqueue({
				...part,
				providerMetadata: mergeMetadata(part.providerMetadata, metadata())
			});
			return;
		}
		if (part.type === "stream-start" && warnings.length > 0) {
			controller.enqueue({
				...part,
				warnings: [...part.warnings, ...warnings]
			});
			return;
		}
		controller.enqueue(part);
	} });
}
var _model = /* @__PURE__ */ new WeakMap();
var _options$1 = /* @__PURE__ */ new WeakMap();
var _config$1 = /* @__PURE__ */ new WeakMap();
var _GatewayLanguageModel_brand = /* @__PURE__ */ new WeakSet();
/**
* A `LanguageModelV4` that is the user's own vendor model, routed through AI
* Gateway. Generation, parsing, tool handling and error shapes stay the
* vendor's; only the transport is Cloudflare's.
*/
var GatewayLanguageModel = class {
	constructor(model, options, config) {
		_classPrivateMethodInitSpec(this, _GatewayLanguageModel_brand);
		this.specificationVersion = "v4";
		_classPrivateFieldInitSpec(this, _model, void 0);
		_classPrivateFieldInitSpec(this, _options$1, void 0);
		_classPrivateFieldInitSpec(this, _config$1, void 0);
		requireConfig(model);
		_classPrivateFieldSet2(_model, this, model);
		_classPrivateFieldSet2(_options$1, this, options);
		_classPrivateFieldSet2(_config$1, this, config);
		rememberOptions(this, options);
	}
	/** The vendor's own provider name — `anthropic.messages`, `openai.responses`. */
	get provider() {
		return _classPrivateFieldGet2(_model, this).provider;
	}
	/** The vendor's own id spelling, which the gateway requires verbatim. */
	get modelId() {
		return _classPrivateFieldGet2(_model, this).modelId;
	}
	get supportedUrls() {
		return _classPrivateFieldGet2(_model, this).supportedUrls;
	}
	/** The model this wraps, for callers that need the vendor object back. */
	get model() {
		return _classPrivateFieldGet2(_model, this);
	}
	async doGenerate(options) {
		const call = parseCallOptions(options.providerOptions);
		const resolved = _assertClassBrand(_GatewayLanguageModel_brand, this, _resolve).call(this, call);
		const upstream = withoutOurOptions(options);
		const legOptions = withoutCallFallback(options);
		return _assertClassBrand(_GatewayLanguageModel_brand, this, _withFallback).call(this, call, (leg) => leg.doGenerate(legOptions), async () => {
			const capture = {};
			const result = await _assertClassBrand(_GatewayLanguageModel_brand, this, _route).call(this, resolved, capture, options.abortSignal).doGenerate(upstream);
			const warnings = ignoredWarnings(resolved);
			return {
				...result,
				providerMetadata: mergeMetadata(result.providerMetadata, _assertClassBrand(_GatewayLanguageModel_brand, this, _metadata).call(this, resolved, capture)),
				response: _assertClassBrand(_GatewayLanguageModel_brand, this, _response).call(this, result.response, capture),
				warnings: warnings.length > 0 ? [...result.warnings, ...warnings] : result.warnings
			};
		});
	}
	async doStream(options) {
		const call = parseCallOptions(options.providerOptions);
		const resolved = _assertClassBrand(_GatewayLanguageModel_brand, this, _resolve).call(this, call);
		const upstream = withoutOurOptions(options);
		const legOptions = withoutCallFallback(options);
		return _assertClassBrand(_GatewayLanguageModel_brand, this, _withFallback).call(this, call, (leg) => leg.doStream(legOptions), async () => {
			const capture = {};
			const result = await _assertClassBrand(_GatewayLanguageModel_brand, this, _route).call(this, resolved, capture, options.abortSignal).doStream(upstream);
			return {
				...result,
				response: _assertClassBrand(_GatewayLanguageModel_brand, this, _response).call(this, result.response, capture),
				stream: result.stream.pipeThrough(stampStream(() => _assertClassBrand(_GatewayLanguageModel_brand, this, _metadata).call(this, resolved, capture), ignoredWarnings(resolved)))
			};
		}, "stream");
	}
};
function _route(resolved, capture, signal) {
	return cloneWithFetch(_classPrivateFieldGet2(_model, this), routedFetch({
		capture,
		config: _classPrivateFieldGet2(_config$1, this),
		resolved,
		signal,
		vendorKey: _classPrivateFieldGet2(_options$1, this)?.vendorKey ?? "gateway"
	}));
}
function _resolve(call) {
	return resolveOptions(_classPrivateFieldGet2(_config$1, this).providerGateway, _classPrivateFieldGet2(_options$1, this), call);
}
function _metadata(resolved, capture) {
	const response = capture.response ?? new Response(null);
	return cloudflareMetadata(this.modelId, resolved.gateway.id, response, _classPrivateFieldGet2(_config$1, this).transport, capture.provider);
}
function _response(response, capture) {
	if (capture.response === void 0) return response;
	const headers = headersToObject(capture.response.headers);
	return {
		...response,
		headers: {
			...response?.headers,
			...headers
		}
	};
}
/**
* The vendor model first, then each fallback leg. A leg named by id is a
* Workers AI model; a leg that is a model object is asked as it stands, so
* `ai()` has already wrapped it if it needed wrapping.
*/
function _withFallback(call, runLeg, run, mode = "generate") {
	const legs = fallbackLegs(_classPrivateFieldGet2(_options$1, this), call);
	if (legs.length === 0) return run();
	const chain = (attempts) => mode === "stream" ? withStreamFallbackLegs(attempts, _classPrivateFieldGet2(_config$1, this).transport.url) : withFallbackLegs(attempts, _classPrivateFieldGet2(_config$1, this).transport.url);
	return chain([{
		model: this.modelId,
		run
	}, ...legs.map((leg) => {
		const model = typeof leg === "string" ? _assertClassBrand(_GatewayLanguageModel_brand, this, _workersAI).call(this, leg) : leg;
		return {
			model: model.modelId,
			run: () => runLeg(model)
		};
	})]);
}
/** A Workers AI leg, carrying this model's gateway options but no chain. */
function _workersAI(modelId) {
	return new CloudflareLanguageModel(requireLanguageModelId(modelId), {
		..._classPrivateFieldGet2(_options$1, this),
		fallback: void 0
	}, _classPrivateFieldGet2(_config$1, this));
}
/**
* Routes any other AI SDK v4 model the vendor built — embedding, image,
* speech, transcription — through the same provider-native request. Only
* `fetch` is swapped; every `do…` method stays the vendor's, and its result
* gains `providerMetadata.cloudflare` when the result is an object that can
* carry one.
*/
function routed(model, options, config) {
	requireConfig(model);
	return new Proxy(model, { get(target, property, receiver) {
		const value = Reflect.get(target, property, receiver);
		if (typeof value !== "function" || typeof property !== "string" || !property.startsWith("do")) return value;
		return async (call) => {
			const resolved = resolveOptions(config.providerGateway, options, parseCallOptions(call?.providerOptions));
			const upstream = call?.providerOptions === void 0 ? call : withoutOurOptions(call);
			const capture = {};
			const clone = cloneWithFetch(target, routedFetch({
				capture,
				config,
				resolved,
				signal: call?.abortSignal,
				vendorKey: options?.vendorKey ?? "gateway"
			}));
			const result = await Reflect.get(clone, property).call(clone, upstream);
			if (result === null || typeof result !== "object") return result;
			const existing = result.providerMetadata;
			return {
				...result,
				...routedWarnings(result, resolved),
				providerMetadata: mergeMetadata(existing, metadataFor(result, cloudflareMetadata(String(target.modelId ?? ""), resolved.gateway.id, capture.response ?? new Response(null), config.transport, capture.provider)))
			};
		};
	} });
}
//#endregion
//#region src/models/ai-sdk/models/modality.ts
/** Whether the caller set any gateway option, the id included. */
function gatewayConfigured(gateway) {
	const { id, ...options } = gateway;
	return id !== "default" || Object.keys(options).length > 0;
}
/** The `unsupported` warning shape, which every modality needs. */
function unsupported(feature, details) {
	return {
		feature,
		type: "unsupported",
		...details === void 0 ? {} : { details }
	};
}
/** Decodes base64 into bytes; passes `Uint8Array` input straight through. */
function toBytes(data) {
	if (typeof data !== "string") return data;
	const binary = atob(data);
	const bytes = new Uint8Array(binary.length);
	for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
	return bytes;
}
var _config = /* @__PURE__ */ new WeakMap();
var _options = /* @__PURE__ */ new WeakMap();
var _CloudflareModalityModel_brand = /* @__PURE__ */ new WeakSet();
/**
* A model over the Cloudflare run path that is not a language model. Holds
* the one call path all four modalities share; each subclass only builds a
* body and reads an answer.
*/
var CloudflareModalityModel = class {
	constructor(modelId, options, config) {
		_classPrivateMethodInitSpec(this, _CloudflareModalityModel_brand);
		this.specificationVersion = "v4";
		this.provider = "cloudflare";
		_classPrivateFieldInitSpec(this, _config, void 0);
		_classPrivateFieldInitSpec(this, _options, void 0);
		options?.fallback?.forEach((leg) => {
			if (typeof leg === "string") requireWorkersAIId(leg);
		});
		this.modelId = modelId;
		_classPrivateFieldSet2(_options, this, options);
		_classPrivateFieldSet2(_config, this, config);
	}
	/** The endpoint this model calls, for error reporting. */
	get endpoint() {
		return _classPrivateFieldGet2(_config, this).transport.url;
	}
	/**
	* Sends one call, trying each fallback leg in turn, and decodes the answer
	* as JSON or as raw bytes depending on what the model sent back.
	*/
	async send(call) {
		const resolved = resolveOptions(_classPrivateFieldGet2(_config, this).providerGateway, _classPrivateFieldGet2(_options, this), parseModalityCallOptions(call.providerOptions));
		const transport = _classPrivateFieldGet2(_config, this).transport;
		return withFallback(this.modelId, resolved.fallback, transport.url, async (modelId) => {
			const request = call.build(modelId);
			const direct = request.audio !== void 0;
			const sent = request.audio === void 0 ? request.input : {
				...request.input,
				audio: {
					body: toBase64(request.audio.bytes),
					contentType: request.audio.contentType
				}
			};
			const warnings = direct && gatewayConfigured(resolved.gateway) ? [...request.warnings, unsupported("gateway", `${modelId} goes to Workers AI directly, so the gateway options do not apply.`)] : request.warnings;
			const response = await transport.run({
				...request.audio === void 0 ? {} : { audio: request.audio },
				gateway: resolved.gateway,
				headers: {
					...definedHeaders(call.headers),
					...resolved.headers,
					...resolved.sessionAffinity === void 0 ? {} : { "x-session-affinity": resolved.sessionAffinity }
				},
				input: request.input,
				model: modelId,
				signal: call.abortSignal
			});
			if (!response.ok) throw await errorFromResponse(response, {
				model: modelId,
				requestBodyValues: sent,
				url: transport.url
			});
			const mediaType = (response.headers.get("content-type") ?? "").split(";")[0].trim().toLowerCase();
			const body = await _assertClassBrand(_CloudflareModalityModel_brand, this, _decode).call(this, response, modelId, sent, mediaType);
			const answer = {
				headers: headersToObject(response.headers),
				input: sent,
				mediaType,
				modelId,
				providerMetadata: cloudflareMetadata(modelId, direct ? void 0 : resolved.gateway.id, response, transport),
				timestamp: /* @__PURE__ */ new Date(),
				url: transport.url,
				warnings,
				...body
			};
			call.check?.(answer);
			return answer;
		});
	}
};
/**
* Reads a 2xx body. A JSON content type is parsed and unwrapped; anything
* else is the model's own bytes (PNG, JPEG, MP3). A JSON content type with a
* body that will not parse is a provider failure, not a caller bug, so it
* comes back as a {@link CloudflareAIError} rather than a bare `SyntaxError`.
*/
async function _decode(response, modelId, input, mediaType) {
	if (!mediaType.includes("json")) return {
		bytes: new Uint8Array(await response.arrayBuffer()),
		json: void 0
	};
	let json;
	try {
		json = await response.json();
	} catch (error) {
		throw new CloudflareAIError({
			cause: error,
			code: "provider-error",
			isRetryable: false,
			message: `The model answered ${response.status} with a body that is not JSON.`,
			model: modelId,
			requestBodyValues: input,
			status: response.status,
			url: _classPrivateFieldGet2(_config, this).transport.url
		});
	}
	return {
		bytes: void 0,
		json: unwrapEnvelope(json, {
			model: modelId,
			status: response.status,
			url: _classPrivateFieldGet2(_config, this).transport.url
		})
	};
}
//#endregion
//#region src/models/ai-sdk/models/image.ts
function imageOptions(providerOptions) {
	const raw = record(providerOptions?.["cloudflare"]) ?? {};
	const negativePrompt = text(raw.negativePrompt);
	return {
		...count(raw.steps) === void 0 ? {} : { steps: count(raw.steps) },
		...count(raw.guidance) === void 0 ? {} : { guidance: count(raw.guidance) },
		...count(raw.strength) === void 0 ? {} : { strength: count(raw.strength) },
		...negativePrompt === void 0 ? {} : { negativePrompt }
	};
}
/** `1024x768` → `{ width: 1024, height: 768 }`. */
function parseSize(size) {
	const [width, height] = size.split("x").map(Number);
	return Number.isFinite(width) && Number.isFinite(height) ? {
		height,
		width
	} : void 0;
}
/** The base64 bytes of a file the caller passed for img2img or masking. */
function fileData(file) {
	if (file.type === "file") return toBase64(file.data);
}
/**
* Builds the run-path body. The knobs and the flux-1 subset rule live in
* `core/images.ts`, shared with the pi-ai module so the two cannot drift; the
* AI SDK call options and the `files`/`mask` byte handling are mapped here.
*/
function buildImageRequest(modelId, options) {
	const warnings = [];
	const extra = imageOptions(options.providerOptions);
	if (options.prompt === void 0) warnings.push({
		message: "Cloudflare image models require a prompt; sent an empty one.",
		type: "other"
	});
	if (options.aspectRatio !== void 0) warnings.push(unsupported("aspectRatio", "Use `size` instead: Workers AI takes pixels."));
	let size;
	if (options.size !== void 0) {
		size = parseSize(options.size);
		if (size === void 0) warnings.push(unsupported("size", `Could not read "${options.size}".`));
	}
	const built = buildImageInput(modelId, options.prompt ?? "", {
		...size === void 0 ? {} : {
			height: size.height,
			width: size.width
		},
		...options.seed === void 0 ? {} : { seed: options.seed },
		...extra.steps === void 0 ? {} : { steps: extra.steps },
		...extra.guidance === void 0 ? {} : { guidance: extra.guidance },
		...extra.strength === void 0 ? {} : { strength: extra.strength },
		...extra.negativePrompt === void 0 ? {} : { negativePrompt: extra.negativePrompt },
		...options.mask === void 0 ? {} : { mask: true },
		...options.files?.[0] === void 0 ? {} : { sourceImage: true }
	});
	const input = built.input;
	for (const warning of built.warnings) warnings.push(unsupported(warning.feature, warning.message));
	if (built.minimal) return {
		input,
		warnings
	};
	const source = options.files?.[0];
	if (source !== void 0) {
		const data = fileData(source);
		if (data === void 0) warnings.push(unsupported("files", "Image URLs are not supported; send the bytes."));
		else input.image_b64 = data;
		if ((options.files?.length ?? 0) > 1) warnings.push(unsupported("files", "Only the first image is used for img2img."));
	}
	if (options.mask !== void 0) {
		const data = fileData(options.mask);
		if (data === void 0) warnings.push(unsupported("mask", "Mask URLs are not supported; send the bytes."));
		else input.mask = Array.from(toBytes(data));
	}
	return {
		input,
		warnings
	};
}
function readImage(answer) {
	if (answer.bytes !== void 0) return {
		base64: void 0,
		bytes: answer.bytes,
		mediaType: answer.mediaType === "" ? "image/png" : answer.mediaType
	};
	const base64 = text((record(answer.json) ?? {}).image);
	if (base64 === void 0) throw new CloudflareAIError({
		code: "provider-error",
		data: answer.json,
		isRetryable: false,
		message: "The image model answered JSON without an `image` field.",
		model: answer.modelId,
		requestBodyValues: answer.input,
		url: answer.url
	});
	return {
		base64,
		bytes: void 0,
		mediaType: imageMediaTypeOf(base64)
	};
}
function imageUsage(json) {
	const usage = record(record(json)?.usage);
	if (usage === void 0) return void 0;
	return {
		inputTokens: count(usage.prompt_tokens),
		outputTokens: count(usage.completion_tokens),
		totalTokens: count(usage.total_tokens)
	};
}
/**
* An `ImageModelV4` over the Cloudflare text-to-image catalog.
*
* The run path generates one image per call, so `maxImagesPerCall` is 1 and
* `generateImage` fans a larger `n` out itself; a direct `doGenerate` with
* `n > 1` is fanned out here instead.
*/
var CloudflareImageModel = class extends CloudflareModalityModel {
	constructor(modelId, options, config) {
		super(modelId, options, config);
		this.maxImagesPerCall = 1;
	}
	async doGenerate(options) {
		const wanted = Math.max(1, options.n);
		const answers = await Promise.all(Array.from({ length: wanted }, () => this.send({
			abortSignal: options.abortSignal,
			build: (modelId) => buildImageRequest(modelId, options),
			check: (answer) => {
				readImage(answer);
			},
			headers: options.headers,
			providerOptions: options.providerOptions
		})));
		const first = answers[0];
		const generated = answers.map(readImage);
		const images = generated.some((image) => image.base64 !== void 0) ? generated.map((image) => image.base64 ?? toBase64(image.bytes)) : generated.map((image) => image.bytes);
		const usage = imageUsage(first.json);
		return {
			images,
			providerMetadata: { [PROVIDER_OPTIONS_KEY]: {
				...first.providerMetadata[PROVIDER_OPTIONS_KEY],
				images: generated.map((image, index) => ({
					...answers[index]?.providerMetadata[PROVIDER_OPTIONS_KEY],
					mediaType: image.mediaType
				}))
			} },
			response: {
				headers: first.headers,
				modelId: first.modelId,
				timestamp: first.timestamp
			},
			...usage === void 0 ? {} : { usage },
			warnings: first.warnings
		};
	}
};
//#endregion
//#region src/models/ai-sdk/models/reranking.ts
/** Builds the run-path body for one reranking leg. */
function buildRerankingRequest(options) {
	const warnings = [];
	const documents = options.documents.type === "text" ? options.documents.values : options.documents.values.map((value) => JSON.stringify(value));
	if (options.documents.type === "object") warnings.push({
		details: "Object documents are sent as JSON text.",
		feature: "documents",
		type: "compatibility"
	});
	const input = {
		contexts: documents.map((value) => ({ text: value })),
		query: options.query
	};
	if (options.topN !== void 0) input.top_k = options.topN;
	return {
		input,
		warnings
	};
}
/**
* A `RerankingModelV4` over the Workers AI reranker catalog.
*/
var CloudflareRerankingModel = class extends CloudflareModalityModel {
	constructor(modelId, options, config) {
		super(modelId, options, config);
	}
	async doRerank(options) {
		const answer = await this.send({
			abortSignal: options.abortSignal,
			build: () => buildRerankingRequest(options),
			headers: options.headers,
			providerOptions: options.providerOptions
		});
		const ranking = (array((record(answer.json) ?? {}).response) ?? []).flatMap((entry) => {
			const row = record(entry);
			const index = count(row?.id);
			const relevanceScore = count(row?.score);
			if (index === void 0 || relevanceScore === void 0) return [];
			return [{
				index,
				relevanceScore
			}];
		});
		ranking.sort((left, right) => right.relevanceScore - left.relevanceScore);
		return {
			providerMetadata: answer.providerMetadata,
			ranking,
			response: {
				body: answer.json,
				headers: answer.headers,
				modelId: answer.modelId,
				timestamp: answer.timestamp
			},
			warnings: answer.warnings
		};
	}
};
//#endregion
//#region src/models/ai-sdk/models/speech.ts
/**
* MeloTTS is the odd one out: the prompt key is `prompt`, the language key is
* `lang`, and there is no voice.
*
* @see https://developers.cloudflare.com/workers-ai/models/melotts/
*/
const MELOTTS = "@cf/myshell-ai/melotts";
/**
* Output encodings Aura accepts. `outputFormat` maps onto this; anything else
* is warned about rather than sent, because the run path validates the enum.
*
* @see https://developers.cloudflare.com/workers-ai/models/aura-1/
*/
const AURA_ENCODINGS = /* @__PURE__ */ new Set([
	"linear16",
	"flac",
	"mulaw",
	"alaw",
	"mp3",
	"opus",
	"aac"
]);
/** Builds the run-path body for one speech leg. */
function buildSpeechRequest(modelId, options) {
	const warnings = [];
	if (options.speed !== void 0) warnings.push(unsupported("speed", "Workers AI speech models do not take a speed."));
	if (options.instructions !== void 0) warnings.push(unsupported("instructions", "Workers AI speech models take text, not instructions."));
	if (modelId === MELOTTS) {
		const input = { prompt: options.text };
		if (options.language !== void 0) input.lang = options.language;
		if (options.voice !== void 0) warnings.push(unsupported("voice", `${MELOTTS} has a single voice.`));
		if (options.outputFormat !== void 0) warnings.push(unsupported("outputFormat", `${MELOTTS} picks its own audio format.`));
		return {
			input,
			warnings
		};
	}
	const input = { text: options.text };
	if (options.voice !== void 0) input.speaker = options.voice;
	if (options.outputFormat !== void 0) if (AURA_ENCODINGS.has(options.outputFormat)) input.encoding = options.outputFormat;
	else warnings.push(unsupported("outputFormat", `Expected one of ${[...AURA_ENCODINGS].join(", ")}.`));
	if (options.language !== void 0) warnings.push(unsupported("language", "Aura picks its language from the model id, e.g. @cf/deepgram/aura-2-es."));
	return {
		input,
		warnings
	};
}
/**
* The audio in one answer. Bytes stay bytes and base64 stays base64: the AI
* SDK converts either way on demand, and converting here would only lose
* fidelity.
*/
function readAudio(answer) {
	if (answer.bytes !== void 0) return answer.bytes;
	const encoded = text((record(answer.json) ?? {}).audio);
	if (encoded === void 0) throw new CloudflareAIError({
		code: "provider-error",
		data: answer.json,
		isRetryable: false,
		message: "The speech model answered JSON without an `audio` field.",
		model: answer.modelId,
		requestBodyValues: answer.input,
		url: answer.url
	});
	return encoded;
}
/**
* A `SpeechModelV4` over the Workers AI text-to-speech catalog.
*/
var CloudflareSpeechModel = class extends CloudflareModalityModel {
	constructor(modelId, options, config) {
		super(modelId, options, config);
	}
	async doGenerate(options) {
		const answer = await this.send({
			abortSignal: options.abortSignal,
			build: (modelId) => buildSpeechRequest(modelId, options),
			check: (answer) => {
				readAudio(answer);
			},
			headers: options.headers,
			providerOptions: options.providerOptions
		});
		return {
			audio: readAudio(answer),
			providerMetadata: answer.providerMetadata,
			request: { body: answer.input },
			response: {
				headers: answer.headers,
				modelId: answer.modelId,
				timestamp: answer.timestamp
			},
			warnings: answer.warnings
		};
	}
};
//#endregion
//#region src/models/ai-sdk/models/transcription.ts
/**
* Whisper ids that take the audio as an unsigned-byte array rather than as
* base64.
*
* @see https://developers.cloudflare.com/workers-ai/models/whisper/
*/
const BYTE_ARRAY_AUDIO = /* @__PURE__ */ new Set(["@cf/openai/whisper", "@cf/openai/whisper-tiny-en"]);
/**
* Models that declare `audio: { body, contentType }`. JSON cannot carry that
* body (base64 and byte arrays answer `400 required properties at '/audio' are
* 'body,contentType'`), so the audio travels as a raw body.
*/
const RAW_AUDIO = /* @__PURE__ */ new Set(["@cf/deepgram/nova-3"]);
/** Deepgram's Flux answers `only supports websocket connections` over HTTP. */
const WEBSOCKET_ONLY = /* @__PURE__ */ new Set(["@cf/deepgram/flux"]);
function transcriptionOptions(providerOptions) {
	const raw = record(providerOptions?.["cloudflare"]) ?? {};
	const vadFilter = raw.vadFilter;
	return {
		...text(raw.language) === void 0 ? {} : { language: text(raw.language) },
		...text(raw.task) === void 0 ? {} : { task: text(raw.task) },
		...text(raw.initialPrompt) === void 0 ? {} : { initialPrompt: text(raw.initialPrompt) },
		...text(raw.prefix) === void 0 ? {} : { prefix: text(raw.prefix) },
		...typeof vadFilter === "boolean" ? { vadFilter } : {}
	};
}
/**
* Builds the request for a raw-audio model: the audio as a raw body, and
* `language` as a field. The whisper-only options have no equivalent there.
*/
function buildRawAudioRequest(modelId, options, extra) {
	const warnings = [];
	for (const feature of [
		"task",
		"initialPrompt",
		"prefix",
		"vadFilter"
	]) if (extra[feature] !== void 0) warnings.push(unsupported(feature, `${modelId} has no ${feature}.`));
	return {
		audio: {
			bytes: toBytes(options.audio),
			contentType: options.mediaType
		},
		input: extra.language === void 0 ? {} : { language: extra.language },
		warnings
	};
}
/** Builds the run-path body for one leg. */
function buildTranscriptionRequest(modelId, options) {
	const warnings = [];
	const extra = transcriptionOptions(options.providerOptions);
	if (RAW_AUDIO.has(modelId)) return buildRawAudioRequest(modelId, options, extra);
	const input = BYTE_ARRAY_AUDIO.has(modelId) ? { audio: Array.from(toBytes(options.audio)) } : { audio: toBase64(options.audio) };
	if (extra.language !== void 0) input.language = extra.language;
	if (extra.task !== void 0) input.task = extra.task;
	if (extra.initialPrompt !== void 0) input.initial_prompt = extra.initialPrompt;
	if (extra.prefix !== void 0) input.prefix = extra.prefix;
	if (extra.vadFilter !== void 0) input.vad_filter = extra.vadFilter;
	return {
		input,
		warnings
	};
}
/** Reads a timed `{ text|word, start, end }` entry as a segment. */
function timed(entry, textKeys) {
	const item = record(entry);
	if (item === void 0) return [];
	const value = textKeys.map((key) => text(item[key])).find(Boolean);
	if (value === void 0) return [];
	return [{
		endSecond: count(item.end) ?? 0,
		startSecond: count(item.start) ?? 0,
		text: value
	}];
}
/**
* Reads a Deepgram answer: `results.channels[0].alternatives[0]`. Its
* sentences are the segments when it sends paragraphs, and its words
* otherwise.
*/
function readDeepgram(body) {
	const channel = record(array(record(body.results)?.channels)?.[0]);
	const alternative = record(array(channel?.alternatives)?.[0]);
	const sentences = (array(record(alternative?.paragraphs)?.paragraphs) ?? []).flatMap((paragraph) => array(record(paragraph)?.sentences) ?? []);
	const segments = sentences.length > 0 ? sentences.flatMap((sentence) => timed(sentence, ["text"])) : (array(alternative?.words) ?? []).flatMap((word) => timed(word, ["punctuated_word", "word"]));
	return {
		durationInSeconds: count(record(body.metadata)?.duration),
		language: text(array(alternative?.languages)?.[0]) ?? text(channel?.detected_language),
		segments,
		text: text(alternative?.transcript) ?? ""
	};
}
/** Reads a whisper answer. */
function readWhisper(body) {
	const info = record(body.transcription_info);
	return {
		durationInSeconds: count(info?.duration),
		language: text(info?.language),
		segments: readSegments(body),
		text: text(body.text) ?? ""
	};
}
/**
* Reads segments out of a whisper answer. `segments` is what the large models
* send; the smaller ones send only `words`, whose timings are the best segment
* boundaries available.
*/
function readSegments(body) {
	const segments = array(body.segments);
	if (segments !== void 0) return segments.flatMap((entry) => {
		const segment = record(entry);
		const value = text(segment?.text);
		if (segment === void 0 || value === void 0) return [];
		return [{
			endSecond: count(segment.end) ?? 0,
			startSecond: count(segment.start) ?? 0,
			text: value
		}];
	});
	return (array(body.words) ?? []).flatMap((entry) => {
		const word = record(entry);
		const value = text(word?.word);
		if (word === void 0 || value === void 0) return [];
		return [{
			endSecond: count(word.end) ?? 0,
			startSecond: count(word.start) ?? 0,
			text: value
		}];
	});
}
/**
* A `TranscriptionModelV4` over the Workers AI speech-recognition catalog.
*/
var CloudflareTranscriptionModel = class extends CloudflareModalityModel {
	constructor(modelId, options, config) {
		super(modelId, options, config);
	}
	async doGenerate(options) {
		const answer = await this.send({
			abortSignal: options.abortSignal,
			build: (modelId) => {
				if (WEBSOCKET_ONLY.has(modelId)) throw new CloudflareAIError({
					code: "bad-request",
					isRetryable: false,
					message: `${modelId} takes websocket connections only. Use @cf/deepgram/nova-3 or @cf/openai/whisper-large-v3-turbo.`,
					model: modelId,
					requestBodyValues: void 0,
					url: this.endpoint
				});
				return buildTranscriptionRequest(modelId, options);
			},
			headers: options.headers,
			providerOptions: options.providerOptions
		});
		const body = record(answer.json) ?? {};
		const transcript = record(body.results) === void 0 ? readWhisper(body) : readDeepgram(body);
		return {
			durationInSeconds: transcript.durationInSeconds,
			language: transcript.language,
			providerMetadata: answer.providerMetadata,
			request: { body: JSON.stringify(answer.input) },
			response: {
				body: answer.json,
				headers: answer.headers,
				modelId: answer.modelId,
				timestamp: answer.timestamp
			},
			segments: transcript.segments,
			text: transcript.text,
			warnings: answer.warnings
		};
	}
};
//#endregion
//#region src/models/ai-sdk/index.ts
/** Whether a value is a model object rather than a model id. */
function isModel(value) {
	return typeof value === "object" && value !== null;
}
/**
* Creates a provider over Workers AI and the AI Gateway catalog.
*
* A Workers AI id goes to Workers AI and a catalog id or dynamic route to AI
* Gateway's `/run`, both on `env.AI.run`; a vendor's model object goes to AI
* Gateway's provider-native path. With `{ accountId, apiToken }` instead of
* `binding`, the same requests go to the REST API and to
* `gateway.ai.cloudflare.com`. Gateway
* options are flat (`id`, `cacheTtl`, …) or nested under `gateway`; per-model
* options and per-call `providerOptions.cloudflare` override them in that
* order.
*
* ```ts
* const ai = createAI({ binding: env.AI, id: "prod", cacheTtl: 60 });
*
* ai("@cf/zai-org/glm-4.7-flash");
* ai("anthropic/claude-opus-4.8", { fallback: ["openai/gpt-5.5"] });
* ai.embedding("@cf/baai/bge-base-en-v1.5");
* ```
*/
function createAI(settings) {
	const config = {
		providerGateway: settings,
		transport: createTransport(settings)
	};
	/**
	* Fallback legs are normalized once, here. A leg written as an id is gated
	* at the line that wrote it; a model object is routed like `ai(model)`.
	*
	* A leg inherits the chain's *gateway* options and nothing else. The rest of
	* the bag — `reasoningEffort`, `chatTemplateKwargs`, `headers`,
	* `sessionAffinity`, `vendorKey` — was written for the model it was written
	* on, and a leg that was configured with its own keeps every one of them.
	*/
	const normalize = (options) => {
		if (options?.fallback === void 0) return options;
		const inherited = gatewayLayerOf(options);
		return {
			...options,
			fallback: options.fallback.map((leg) => typeof leg === "string" ? requireLanguageModelId(leg) : languageModel(leg, inherited))
		};
	};
	function languageModel(model, options) {
		const resolved = normalize(options);
		if (!isModel(model)) return new CloudflareLanguageModel(requireLanguageModelId(model), resolved, config);
		if (model instanceof CloudflareLanguageModel) {
			const merged = normalize(mergeModelOptions(optionsOf(model), resolved));
			return new CloudflareLanguageModel(model.modelId, merged, config);
		}
		if (model instanceof GatewayLanguageModel) {
			const merged = normalize(mergeModelOptions(optionsOf(model), resolved));
			return new GatewayLanguageModel(model.model, merged, config);
		}
		return new GatewayLanguageModel(model, resolved, config);
	}
	const embedding = (modelId, options) => new CloudflareEmbeddingModel(modelId, options, config);
	const image = (modelId, options) => new CloudflareImageModel(modelId, options, config);
	const transcription = (modelId, options) => new CloudflareTranscriptionModel(modelId, options, config);
	const speech = (modelId, options) => new CloudflareSpeechModel(modelId, options, config);
	const reranking = (modelId, options) => new CloudflareRerankingModel(modelId, options, config);
	const provider = (model, options) => languageModel(model, options);
	return Object.assign(provider, {
		embedding,
		embeddingModel: embedding,
		image,
		imageModel: image,
		languageModel,
		reranking,
		rerankingModel: reranking,
		routed: (model, options) => routed(model, options, config),
		specificationVersion: "v4",
		speech,
		speechModel: speech,
		textEmbeddingModel: embedding,
		transcription,
		transcriptionModel: transcription
	});
}
//#endregion
export { GatewayLanguageModel as n, CloudflareAIError as r, createAI as t };

//# sourceMappingURL=ai-sdk-Kon40i0q.js.map