openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
242 lines (241 loc) • 10.2 kB
JavaScript
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { m as readProviderJsonResponse, n as assertOkOrThrowHttpError } from "./provider-http-errors-DmewLLH7.mjs";
import { p as sanitizeConfiguredModelProviderRequest } from "./provider-request-config-By9R9pyt.mjs";
import "./string-coerce-runtime-50qKeWeL.mjs";
import { r as resolveGeneratedMediaMaxBytes } from "./configured-max-bytes-94qDtzvY.mjs";
import { t as bufferToBlobPart } from "./blob-runtime-CBGGTQ96.mjs";
import { _ as resolveProviderOperationTimeoutMs, d as postJsonRequest, f as postMultipartRequest, h as resolveProviderHttpRequestConfig, r as createProviderOperationDeadline } from "./shared-DcnGQdHV.mjs";
import { t as expectDefined } from "./expect-runtime-CJBt0Gq2.mjs";
import { t as isProviderApiKeyConfigured } from "./provider-auth-availability-GiSm5QUa.mjs";
import "./media-generation-runtime-DrYRW4VG.mjs";
import { a as resolveApiKeyForProvider } from "./provider-auth-runtime-JvyP44zh.mjs";
import "./provider-http-CuRGJD0L.mjs";
import { a as imageSourceUploadFileName, c as resolveInlineImageJsonResponseMaxBytes, s as parseOpenAiCompatibleImageResponse } from "./image-assets-DT0442If.mjs";
import "./image-generation-CltF7hkb.mjs";
import "./provider-auth-C0Mnu3Sx.mjs";
import { d as extractFoundryEndpoint, m as isFoundryProviderApi, o as PROVIDER_ID, p as isFoundryMaiImageModel, r as DEFAULT_API } from "./shared-94JaapVz.mjs";
import { t as prepareFoundryRuntimeAuth } from "./runtime-DygFkLA9.mjs";
//#region extensions/microsoft-foundry/image-generation-provider.ts
const DEFAULT_TIMEOUT_MS = 6e5;
const DEFAULT_IMAGE_SIZE = {
width: 1024,
height: 1024
};
const MAI_MIN_IMAGE_SIDE_PX = 768;
const MAI_MAX_IMAGE_PIXELS = 1048576;
const MAI_IMAGE_BASE_PATH = "/mai/v1";
const MAI_IMAGE_MAX_RESULTS = 1;
const MAI_IMAGE_OUTPUT_MIME = "image/png";
const MAI_IMAGE_UPLOAD_MIME_TYPES = /* @__PURE__ */ new Set([
"image/jpeg",
"image/jpg",
"image/png"
]);
function ensureMaiImageModel(providerConfig, model) {
const configuredName = providerConfig?.models?.find((candidate) => candidate.id === model)?.name;
const modelName = configuredName || model;
const normalizedModel = normalizeOptionalLowercaseString(model);
const hasMetadata = Boolean(configuredName) && normalizeOptionalLowercaseString(configuredName) !== normalizedModel;
if (!isFoundryMaiImageModel(modelName) && (hasMetadata || normalizedModel?.startsWith("mai-") && !normalizedModel.startsWith("mai-image-"))) throw new Error(`Microsoft Foundry image generation supports MAI image deployments only, got "${modelName}".`);
return {
modelName,
hasMetadata
};
}
function isMaiImageEditModel(modelName) {
const normalized = normalizeOptionalLowercaseString(modelName);
return normalized === "mai-image-2.5" || normalized === "mai-image-2.5-flash";
}
function resolveMaiImageSize(size) {
if (!size) return DEFAULT_IMAGE_SIZE;
const match = size.match(/^(\d{1,5})x(\d{1,5})$/u);
if (!match) throw new Error(`Microsoft Foundry MAI image size must use WIDTHxHEIGHT, got "${size}".`);
const width = Number(match[1]);
const height = Number(match[2]);
if (width < MAI_MIN_IMAGE_SIDE_PX || height < MAI_MIN_IMAGE_SIDE_PX || width * height > MAI_MAX_IMAGE_PIXELS) throw new Error(`Microsoft Foundry MAI image size must be at least 768x768 and at most 1,048,576 total pixels, got "${size}".`);
return {
width,
height
};
}
function resolveConfiguredEndpoint(params) {
const endpoint = extractFoundryEndpoint(params.preparedBaseUrl) ?? extractFoundryEndpoint(params.providerConfig?.baseUrl) ?? extractFoundryEndpoint(process.env.AZURE_OPENAI_ENDPOINT);
if (!endpoint) throw new Error("Microsoft Foundry endpoint missing for MAI image generation.");
return endpoint;
}
function buildRuntimeModel(params) {
const api = isFoundryProviderApi(params.providerConfig?.api) ? params.providerConfig.api : DEFAULT_API;
return {
id: params.model,
name: params.modelName,
api,
provider: PROVIDER_ID,
baseUrl: params.providerConfig?.baseUrl ?? "",
reasoning: false,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0
},
contextWindow: 32e3,
maxTokens: 0
};
}
async function resolveMaiImageAuth(params) {
const auth = await resolveApiKeyForProvider({
provider: PROVIDER_ID,
cfg: params.req.cfg,
agentDir: params.req.agentDir,
store: params.req.authStore
});
if (!auth.apiKey) throw new Error("Microsoft Foundry API key missing");
if (auth.apiKey !== "__entra_id_dynamic__") return { headers: { "api-key": auth.apiKey } };
const prepared = await prepareFoundryRuntimeAuth({
config: params.req.cfg,
agentDir: params.req.agentDir,
env: process.env,
provider: PROVIDER_ID,
modelId: params.model,
model: buildRuntimeModel({
providerConfig: params.providerConfig,
model: params.model,
modelName: params.modelName
}),
apiKey: auth.apiKey,
authMode: auth.mode,
...auth.profileId ? { profileId: auth.profileId } : {}
});
if (!prepared?.apiKey) throw new Error("Microsoft Foundry Entra ID token missing after runtime auth refresh.");
return {
headers: { Authorization: `Bearer ${prepared.apiKey}` },
...prepared.baseUrl ? { baseUrl: prepared.baseUrl } : {}
};
}
function buildEditFormData(params) {
const mimeType = normalizeOptionalLowercaseString(params.image.mimeType) ?? MAI_IMAGE_OUTPUT_MIME;
if (!MAI_IMAGE_UPLOAD_MIME_TYPES.has(mimeType)) throw new Error("Microsoft Foundry MAI image edits require a PNG or JPEG input image.");
const form = new FormData();
form.set("model", params.model);
form.set("prompt", params.req.prompt);
form.set("image", new Blob([bufferToBlobPart(params.image.buffer)], { type: mimeType === "image/jpg" ? "image/jpeg" : mimeType }), imageSourceUploadFileName({
image: params.image,
index: 0,
fileNamePrefix: "microsoft-foundry-input"
}));
return form;
}
function parseMaiImageResponse(payload, label) {
const images = parseOpenAiCompatibleImageResponse(payload, {
defaultMimeType: MAI_IMAGE_OUTPUT_MIME,
fileNamePrefix: "microsoft-foundry-image",
malformedResponseError: `${label} response malformed`,
sniffMimeType: true
});
if (images.length === 0) throw new Error(`${label} response missing image data`);
return images;
}
function buildMicrosoftFoundryImageGenerationProvider() {
return {
id: PROVIDER_ID,
label: "Microsoft Foundry",
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
models: [],
isConfigured: (ctx) => isProviderApiKeyConfigured({
provider: PROVIDER_ID,
...ctx
}),
capabilities: {
generate: {
maxCount: MAI_IMAGE_MAX_RESULTS,
supportsSize: true
},
edit: {
enabled: true,
maxCount: MAI_IMAGE_MAX_RESULTS,
maxInputImages: 1,
supportsSize: false
},
output: { formats: ["png"] }
},
async generateImage(req) {
const providerConfig = req.cfg.models?.providers?.[PROVIDER_ID];
const model = normalizeOptionalString(req.model);
if (!model) throw new Error("Microsoft Foundry MAI image generation requires a deployment name.");
const { modelName, hasMetadata } = ensureMaiImageModel(providerConfig, model);
const inputImages = req.inputImages ?? [];
const mode = inputImages.length > 0 ? "edits" : "generations";
if (req.count !== void 0 && req.count !== 1) throw new Error("Microsoft Foundry MAI image models return one image per request.");
if (inputImages.length > 1) throw new Error("Microsoft Foundry MAI image edits support one input image.");
if (mode === "edits" && (hasMetadata || isFoundryMaiImageModel(model)) && !isMaiImageEditModel(modelName)) throw new Error(`${modelName} does not support Microsoft Foundry MAI image edits.`);
if (mode === "edits" && !hasMetadata && !isFoundryMaiImageModel(model)) throw new Error("Microsoft Foundry MAI image edits require MAI-Image-2.5 model metadata for custom deployment names.");
const auth = await resolveMaiImageAuth({
req,
providerConfig,
model,
modelName
});
const resolvedBaseUrl = `${resolveConfiguredEndpoint({
providerConfig,
preparedBaseUrl: auth.baseUrl
})}${MAI_IMAGE_BASE_PATH}`;
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } = resolveProviderHttpRequestConfig({
baseUrl: resolvedBaseUrl,
defaultBaseUrl: resolvedBaseUrl,
allowPrivateNetwork: false,
defaultHeaders: auth.headers,
request: sanitizeConfiguredModelProviderRequest(providerConfig?.request),
provider: PROVIDER_ID,
capability: "image",
transport: "http"
});
const label = mode === "edits" ? "Microsoft Foundry MAI image edit" : "Microsoft Foundry MAI image generation";
const deadline = createProviderOperationDeadline({
timeoutMs: req.timeoutMs,
label
});
const timeoutMs = resolveProviderOperationTimeoutMs({
deadline,
defaultTimeoutMs: DEFAULT_TIMEOUT_MS
});
const requestOptions = {
url: `${baseUrl.replace(/\/+$/u, "")}/images/${mode}`,
headers: new Headers(headers),
timeoutMs,
fetchFn: fetch,
allowPrivateNetwork,
ssrfPolicy: req.ssrfPolicy,
dispatcherPolicy
};
if (mode === "edits") requestOptions.headers.delete("Content-Type");
else requestOptions.headers.set("Content-Type", "application/json");
const { response, release } = await (mode === "edits" ? postMultipartRequest({
...requestOptions,
body: buildEditFormData({
req,
image: expectDefined(inputImages[0], "Microsoft Foundry edit source image"),
model
})
}) : postJsonRequest({
...requestOptions,
body: {
model,
prompt: req.prompt,
...resolveMaiImageSize(req.size)
}
}));
try {
await assertOkOrThrowHttpError(response, `${label} failed`);
return {
images: parseMaiImageResponse(await readProviderJsonResponse(response, "microsoft-foundry.image-generation", { maxBytes: resolveInlineImageJsonResponseMaxBytes(MAI_IMAGE_MAX_RESULTS, resolveGeneratedMediaMaxBytes(req.cfg, "image")) }), label),
model
};
} finally {
await release();
}
}
};
}
//#endregion
export { buildMicrosoftFoundryImageGenerationProvider as t };