genkitx-azure-openai
Version:
Genkit AI framework plugin for Azure OpenAI APIs.
431 lines (428 loc) • 12.8 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var azure_functions_exports = {};
__export(azure_functions_exports, {
allOf: () => allOf,
allowAll: () => allowAll,
anyOf: () => anyOf,
default: () => azure_functions_default,
onCallGenkit: () => onCallGenkit,
requireApiKey: () => requireApiKey,
requireBearerToken: () => requireBearerToken,
requireHeader: () => requireHeader
});
module.exports = __toCommonJS(azure_functions_exports);
var import_functions = require("@azure/functions");
var import_genkit = require("genkit");
var import_context = require("genkit/context");
function buildCorsHeaders(corsOptions, requestOrigin) {
if (corsOptions === false) {
return {};
}
const opts = corsOptions === true || corsOptions === void 0 ? {} : corsOptions;
const headers = {
"Content-Type": "application/json"
};
const origin = opts.origin ?? "*";
if (Array.isArray(origin)) {
if (requestOrigin && origin.includes(requestOrigin)) {
headers["Access-Control-Allow-Origin"] = requestOrigin;
}
} else {
headers["Access-Control-Allow-Origin"] = origin;
}
const methods = opts.methods ?? ["POST", "OPTIONS"];
headers["Access-Control-Allow-Methods"] = methods.join(", ");
const allowedHeaders = opts.allowedHeaders ?? [
"Content-Type",
"Authorization"
];
headers["Access-Control-Allow-Headers"] = allowedHeaders.join(", ");
if (opts.exposedHeaders && opts.exposedHeaders.length > 0) {
headers["Access-Control-Expose-Headers"] = opts.exposedHeaders.join(", ");
}
if (opts.credentials) {
headers["Access-Control-Allow-Credentials"] = "true";
}
const maxAge = opts.maxAge ?? 86400;
headers["Access-Control-Max-Age"] = String(maxAge);
return headers;
}
async function parseRequestBody(request) {
let bodyText;
try {
bodyText = await request.text();
} catch {
return {};
}
if (!bodyText) {
return {};
}
try {
const parsed = JSON.parse(bodyText);
if (parsed && typeof parsed === "object" && "data" in parsed) {
return parsed.data;
}
return parsed;
} catch {
throw new import_genkit.UserFacingError(
"INVALID_ARGUMENT",
"Invalid JSON in request body"
);
}
}
function getRequestOrigin(request) {
return request.headers.get("origin") || void 0;
}
function normalizeHeaders(request) {
const result = {};
request.headers.forEach((value, key) => {
result[key.toLowerCase()] = value;
});
return result;
}
function getQueryParams(request) {
const result = {};
const url = new URL(request.url);
url.searchParams.forEach((value, key) => {
result[key] = value;
});
return result;
}
function toRequestData(request, input) {
return {
method: request.method,
headers: normalizeHeaders(request),
input
};
}
function onCallGenkit(optsOrFlow, flowArg) {
let opts;
let flow;
if (arguments.length === 1) {
opts = {};
flow = optsOrFlow;
} else {
opts = optsOrFlow;
flow = flowArg;
}
const flowName = flow.__action?.name || "unknown";
function buildAzureFunctionsContext(request, azureContext) {
return {
azureFunctions: {
request: {
url: request.url,
headers: normalizeHeaders(request),
query: getQueryParams(request),
params: request.params
},
context: {
functionName: azureContext.functionName,
invocationId: azureContext.invocationId
}
}
};
}
async function resolveActionContext(request, azureContext, input) {
const azureFunctionsContext = buildAzureFunctionsContext(
request,
azureContext
);
if (opts.contextProvider) {
const requestData = toRequestData(request, input);
const providerContext = await opts.contextProvider(requestData);
return { ...azureFunctionsContext, ...providerContext };
}
return azureFunctionsContext;
}
async function buildErrorResponse(error, corsHeaders) {
if (opts.onError) {
const customError = await opts.onError(
error instanceof Error ? error : new Error(String(error))
);
return {
status: customError.statusCode,
headers: corsHeaders,
jsonBody: {
error: {
status: "INTERNAL",
message: customError.message
}
}
};
}
return {
status: (0, import_context.getHttpStatus)(error),
headers: corsHeaders,
jsonBody: (0, import_context.getCallableJSON)(error)
};
}
async function standardHandler(request, azureContext) {
const requestOrigin = getRequestOrigin(request);
const corsHeaders = buildCorsHeaders(opts.cors, requestOrigin);
if (request.method === "OPTIONS") {
return {
status: 204,
headers: corsHeaders
};
}
if (opts.debug) {
azureContext.log(
`[${flowName}] Request: ${request.method} ${request.url}`
);
azureContext.log(
`[${flowName}] Headers:`,
JSON.stringify(normalizeHeaders(request), null, 2)
);
}
try {
const input = await parseRequestBody(request);
const actionContext = await resolveActionContext(
request,
azureContext,
input
);
if (opts.debug) {
azureContext.log(`[${flowName}] Running flow with input:`, input);
}
const runResult = await flow.run(input, { context: actionContext });
const result = runResult.result;
if (opts.debug) {
azureContext.log(`[${flowName}] Flow completed successfully`);
}
return {
status: 200,
headers: corsHeaders,
jsonBody: {
result
}
};
} catch (error) {
azureContext.error(`[${flowName}] Error:`, error);
return buildErrorResponse(error, corsHeaders);
}
}
async function streamingHandler(request, azureContext) {
const requestOrigin = getRequestOrigin(request);
const corsHeaders = buildCorsHeaders(opts.cors, requestOrigin);
if (request.method === "OPTIONS") {
return {
status: 204,
headers: corsHeaders
};
}
if (opts.debug) {
azureContext.log(
`[${flowName}] Stream request: ${request.method} ${request.url}`
);
}
try {
const input = await parseRequestBody(request);
const actionContext = await resolveActionContext(
request,
azureContext,
input
);
const acceptHeader = request.headers.get("accept") || "";
const clientWantsStreaming = acceptHeader.includes("text/event-stream");
if (clientWantsStreaming) {
const encoder = new TextEncoder();
const readableStream = new ReadableStream({
async start(controller) {
try {
const { stream, output } = flow.stream(input, {
context: actionContext
});
for await (const chunk of stream) {
const sseData = `data: ${JSON.stringify({ message: chunk })}
`;
controller.enqueue(encoder.encode(sseData));
}
const result = await output;
const sseFinal = `data: ${JSON.stringify({ result })}
`;
controller.enqueue(encoder.encode(sseFinal));
controller.close();
if (opts.debug) {
azureContext.log(
`[${flowName}] Streaming flow completed successfully`
);
}
} catch (error) {
azureContext.error(`[${flowName}] Stream error:`, error);
const errorData = `data: ${JSON.stringify((0, import_context.getCallableJSON)(error))}
`;
controller.enqueue(encoder.encode(errorData));
controller.close();
}
}
});
return {
status: 200,
headers: {
...corsHeaders,
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive"
},
body: readableStream
};
} else {
const runResult = await flow.run(input, {
context: actionContext
});
const result = runResult.result;
return {
status: 200,
headers: corsHeaders,
jsonBody: { result }
};
}
} catch (error) {
azureContext.error(`[${flowName}] Stream error:`, error);
return buildErrorResponse(error, corsHeaders);
}
}
const handler = opts.streaming ? streamingHandler : standardHandler;
const callableFunction = {
handler,
flow,
flowName,
run: async (input, options) => {
const runResult = await flow.run(input, {
context: options?.context
});
return runResult.result;
},
stream: (input, options) => {
return flow.stream(input, {
context: options?.context
});
}
};
const methods = opts.httpMethods ?? ["POST", "OPTIONS"];
const authLevel = opts.authLevel ?? "anonymous";
import_functions.app.http(flowName, {
methods,
authLevel,
...opts.route ? { route: opts.route } : {},
handler
});
return callableFunction;
}
function requireApiKey(headerName, expectedValueOrValidator) {
const lowerHeaderName = headerName.toLowerCase();
return async (request) => {
const apiKey = request.headers[lowerHeaderName];
if (!apiKey) {
throw new import_genkit.UserFacingError(
"UNAUTHENTICATED",
`Missing required header: ${headerName}`
);
}
if (typeof expectedValueOrValidator === "string") {
if (apiKey !== expectedValueOrValidator) {
throw new import_genkit.UserFacingError("PERMISSION_DENIED", "Invalid API key");
}
} else {
await expectedValueOrValidator(apiKey);
}
return {
auth: { apiKey }
};
};
}
function requireBearerToken(validateToken) {
return async (request) => {
const authHeader = request.headers["authorization"];
if (!authHeader) {
throw new import_genkit.UserFacingError(
"UNAUTHENTICATED",
"Missing Authorization header"
);
}
const match = authHeader.match(/^Bearer\s+(.+)$/i);
if (!match) {
throw new import_genkit.UserFacingError(
"UNAUTHENTICATED",
"Invalid Authorization header format. Expected: Bearer <token>"
);
}
const token = match[1];
return await validateToken(token);
};
}
function requireHeader(headerName, expectedValue) {
const lowerHeaderName = headerName.toLowerCase();
return async (request) => {
const value = request.headers[lowerHeaderName];
if (!value) {
throw new import_genkit.UserFacingError(
"UNAUTHENTICATED",
`Missing required header: ${headerName}`
);
}
if (expectedValue !== void 0 && value !== expectedValue) {
throw new import_genkit.UserFacingError(
"PERMISSION_DENIED",
`Invalid value for header: ${headerName}`
);
}
return {};
};
}
function allowAll() {
return async () => ({});
}
function allOf(...providers) {
return async (request) => {
let mergedContext = {};
for (const provider of providers) {
const context = await provider(request);
mergedContext = { ...mergedContext, ...context };
}
return mergedContext;
};
}
function anyOf(...providers) {
return async (request) => {
let lastError;
for (const provider of providers) {
try {
const context = await provider(request);
return context;
} catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
}
}
throw lastError || new import_genkit.UserFacingError("UNAUTHENTICATED", "Unauthorized");
};
}
var azure_functions_default = onCallGenkit;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
allOf,
allowAll,
anyOf,
onCallGenkit,
requireApiKey,
requireBearerToken,
requireHeader
});
//# sourceMappingURL=azure_functions.js.map