openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
267 lines (266 loc) • 10.9 kB
JavaScript
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { m as readProviderJsonResponse, n as assertOkOrThrowHttpError } from "./provider-http-errors-U-nhuk_f.js";
import { p as sanitizeConfiguredModelProviderRequest } from "./provider-request-config-DIOYidiO.js";
import { t as isProviderApiKeyConfigured } from "./provider-auth-availability-BUrlw84-.js";
import { n as resolveGeneratedMediaMaxBytes } from "./configured-max-bytes-CTSUbFDK.js";
import "./provider-auth-BeZ7NZUU.js";
import { t as bufferToBlobPart } from "./blob-runtime-CBGGTQ96.js";
import { h as resolveProviderOperationTimeoutMs, l as postJsonRequest, p as resolveProviderHttpRequestConfig, r as createProviderOperationDeadline, u as postMultipartRequest } from "./shared-DCDpYrmy.js";
import { t as expectDefined } from "./expect-runtime-CJBt0Gq2.js";
import "./string-coerce-runtime-GQa0ehRA.js";
import "./media-generation-runtime-Dd81xPqM.js";
import { a as resolveApiKeyForProvider } from "./provider-auth-runtime-BqyR7mkf.js";
import "./provider-http-k9RMI7iG.js";
import { c as parseOpenAiCompatibleImageResponse, l as resolveInlineImageJsonResponseMaxBytes, o as imageSourceUploadFileName } from "./image-generation-B61kkjvL.js";
import { f as extractFoundryEndpoint, g as isFoundryProviderApi, h as isFoundryMaiImageModel, o as PROVIDER_ID, r as DEFAULT_API } from "./shared-DDqy5bfX.js";
import "./shared-runtime-7GOCNIFC.js";
import { t as prepareFoundryRuntimeAuth } from "./runtime-CXNEblD5.js";
//#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 readProviderConfig(req) {
return req.cfg.models?.providers?.[PROVIDER_ID];
}
function resolveConfiguredModelName(providerConfig, model) {
const configuredName = providerConfig?.models?.find((candidate) => candidate.id === model)?.name;
const hasDistinctModelMetadata = normalizeOptionalLowercaseString(configuredName) !== normalizeOptionalLowercaseString(model);
return configuredName ? {
modelName: configuredName,
hasMetadata: hasDistinctModelMetadata
} : {
modelName: model,
hasMetadata: false
};
}
function ensureMaiImageModel(providerConfig, model) {
const resolved = resolveConfiguredModelName(providerConfig, model);
const normalizedModel = normalizeOptionalLowercaseString(model);
if (!isFoundryMaiImageModel(resolved.modelName) && (resolved.hasMetadata || normalizedModel?.startsWith("mai-") && !normalizedModel.startsWith("mai-image-"))) throw new Error(`Microsoft Foundry image generation supports MAI image deployments only, got "${resolved.modelName}".`);
return resolved;
}
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 (!Number.isInteger(width) || !Number.isInteger(height) || 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 assertSingleImageCount(count) {
if (count === void 0 || count === 1) return;
throw new Error("Microsoft Foundry MAI image models return one image per request.");
}
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 buildMaiImageUrl(baseUrl, mode) {
return `${baseUrl.replace(/\/+$/u, "")}/images/${mode}`;
}
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 = readProviderConfig(req);
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";
assertSingleImageCount(req.count);
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 { response, release } = await (mode === "edits" ? postMultipartRequest({
url: buildMaiImageUrl(baseUrl, mode),
headers: (() => {
const multipartHeaders = new Headers(headers);
multipartHeaders.delete("Content-Type");
return multipartHeaders;
})(),
body: buildEditFormData({
req,
image: expectDefined(inputImages[0], "Microsoft Foundry edit source image"),
model
}),
timeoutMs,
fetchFn: fetch,
allowPrivateNetwork,
ssrfPolicy: req.ssrfPolicy,
dispatcherPolicy
}) : postJsonRequest({
url: buildMaiImageUrl(baseUrl, mode),
headers: (() => {
const jsonHeaders = new Headers(headers);
jsonHeaders.set("Content-Type", "application/json");
return jsonHeaders;
})(),
body: {
model,
prompt: req.prompt,
...resolveMaiImageSize(req.size)
},
timeoutMs,
fetchFn: fetch,
allowPrivateNetwork,
ssrfPolicy: req.ssrfPolicy,
dispatcherPolicy
}));
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 };