genkitx-azure-openai
Version:
Genkit AI framework plugin for Azure OpenAI APIs.
442 lines (438 loc) • 13.7 kB
TypeScript
import { HttpHandler } from '@azure/functions';
import { ActionContext, Flow, z } from 'genkit';
export { ActionContext } from 'genkit';
import { ContextProvider } from 'genkit/context';
export { ContextProvider, RequestData } from 'genkit/context';
/**
* Copyright 2026 Xavier Portilla Edo
* Copyright 2026 Google LLC
* Copyright 2026 Bloom Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Type helpers to extract input/output types from Flow
*/
type FlowInput<F extends Flow> = F extends Flow<infer I, z.ZodTypeAny, z.ZodTypeAny> ? z.infer<I> : never;
type FlowOutput<F extends Flow> = F extends Flow<z.ZodTypeAny, infer O, z.ZodTypeAny> ? z.infer<O> : never;
type FlowStream<F extends Flow> = F extends Flow<z.ZodTypeAny, z.ZodTypeAny, infer S> ? z.infer<S> : never;
/**
* CORS configuration options
*/
interface CorsOptions {
/**
* Allowed origins for CORS requests.
* Can be a string, array of strings, or '*' for all origins.
* @default '*'
*/
origin?: string | string[];
/**
* Allowed HTTP methods.
* @default ['POST', 'OPTIONS']
*/
methods?: string[];
/**
* Allowed headers in requests.
* @default ['Content-Type', 'Authorization']
*/
allowedHeaders?: string[];
/**
* Headers exposed to the client.
*/
exposedHeaders?: string[];
/**
* Whether to allow credentials.
* @default false
*/
credentials?: boolean;
/**
* Max age for preflight cache (in seconds).
* @default 86400 (24 hours)
*/
maxAge?: number;
}
/**
* Extended action context that includes Azure Functions-specific information
*/
interface AzureFunctionsActionContext extends ActionContext {
/** Azure Functions-specific context data */
azureFunctions?: {
request: {
url: string;
headers: Record<string, string>;
query: Record<string, string>;
params: Record<string, string>;
};
context: {
functionName: string;
invocationId: string;
};
};
}
/**
* Options for configuring the Azure Functions handler
*/
interface AzureFunctionsOptions<C extends ActionContext = ActionContext, T = unknown> {
/**
* The authorization level for the Azure Functions HTTP trigger.
* @default 'anonymous'
*/
authLevel?: "anonymous" | "function" | "admin";
/**
* HTTP methods to register for the Azure Functions HTTP trigger.
* @default ['POST', 'OPTIONS']
*/
httpMethods?: string[];
/**
* Optional custom route for the Azure Functions HTTP trigger.
* If not provided, the function name is used as the route.
*/
route?: string;
/**
* CORS configuration. Set to false to disable CORS headers.
* @default { origin: '*', methods: ['POST', 'OPTIONS'] }
*/
cors?: CorsOptions | boolean;
/**
* Context provider that parses request data and returns context for the flow.
* This follows the same pattern as express, next.js, and other Genkit integrations.
*
* The context provider receives a RequestData object containing:
* - method: HTTP method ('GET', 'POST', etc.)
* - headers: Lowercase headers from the request
* - input: Parsed request body
*
* Return an ActionContext object that will be available via getContext() in the flow.
* Throw UserFacingError for authentication/authorization failures.
*
* @example
* ```typescript
* import { UserFacingError } from 'genkit';
*
* const authProvider: ContextProvider = async (req) => {
* const token = req.headers['authorization'];
* if (!token) {
* throw new UserFacingError('UNAUTHENTICATED', 'Missing auth token');
* }
* const user = await verifyToken(token);
* return { auth: { user } };
* };
*
* export const handler = onCallGenkit(
* { contextProvider: authProvider },
* myFlow
* );
* ```
*/
contextProvider?: ContextProvider<C, T>;
/**
* Custom error handler for transforming errors before response.
*/
onError?: (error: Error) => {
statusCode: number;
message: string;
} | Promise<{
statusCode: number;
message: string;
}>;
/**
* Whether to log incoming requests (for debugging).
* @default false
*/
debug?: boolean;
/**
* Whether to return a streaming handler.
* When true, the handler returns a streaming response using
* `ReadableStream` for incremental SSE delivery.
*
* The streaming handler is compatible with `streamFlow` from `genkit/beta/client`.
* For clients sending `Accept: text/event-stream`, it writes SSE chunks
* incrementally. Otherwise it falls back to a buffered JSON response.
*
* @default false
*
* @example
* ```typescript
* export const handler = onCallGenkit(
* { streaming: true },
* myStreamingFlow
* );
* ```
*/
streaming?: boolean;
}
/**
* Response wrapper for successful flow execution (callable protocol).
* Follows the same format as express and other Genkit integrations.
*/
interface FlowResponse<T> {
result: T;
}
/**
* Response wrapper for failed flow execution (callable protocol).
* Shape matches genkit's getCallableJSON output.
*/
interface FlowErrorResponse {
error: {
status: string;
message: string;
details?: unknown;
};
}
/**
* Union type for flow responses
*/
type AzureFunctionsFlowResponse<T> = FlowResponse<T> | FlowErrorResponse;
/**
* Azure Functions handler type
*/
type AzureFunctionsHandler = HttpHandler;
/**
* Run options for flow execution
*/
interface FlowRunOptions {
context?: Record<string, unknown>;
}
/**
* Callable function type that includes the raw handler and metadata
*/
interface CallableAzureFunction<F extends Flow> {
/**
* The Azure Functions HTTP handler
*/
handler: HttpHandler;
/**
* The underlying Genkit flow
*/
flow: F;
/**
* Execute the flow directly (for testing)
*/
run: (input: FlowInput<F>, options?: FlowRunOptions) => Promise<FlowOutput<F>>;
/**
* Stream the flow directly (for testing)
*/
stream: (input: FlowInput<F>, options?: FlowRunOptions) => {
stream: AsyncIterable<FlowStream<F>>;
output: Promise<FlowOutput<F>>;
};
/**
* Flow name
*/
flowName: string;
}
/**
* Creates an Azure Functions handler for a Genkit flow.
*
* This function wraps a Genkit flow to create an Azure Functions HTTP handler that:
* - Handles CORS automatically
* - Supports ContextProvider for authentication/authorization
* - Provides proper error handling
* - Returns standardized response format
* - Supports streaming responses via ReadableStream
*
* @example Basic usage (auto-registers Azure Functions HTTP trigger)
* ```typescript
* import { genkit, z } from 'genkit';
* import { onCallGenkit, azureOpenAI, gpt4o } from 'genkitx-azure-openai';
*
* const ai = genkit({
* plugins: [azureOpenAI()],
* model: gpt4o,
* });
*
* const myFlow = ai.defineFlow(
* { name: 'myFlow', inputSchema: z.string(), outputSchema: z.string() },
* async (input) => {
* const { text } = await ai.generate({ prompt: input });
* return text;
* }
* );
*
* // Automatically registered as POST /api/myFlow (uses flow name)
* export const myFlowFn = onCallGenkit(myFlow);
* ```
*
* @example With ContextProvider for authentication
* ```typescript
* import { UserFacingError } from 'genkit';
* import type { ContextProvider } from 'genkit/context';
*
* interface AuthContext {
* auth: { user: { id: string; name: string } };
* }
*
* const authProvider: ContextProvider<AuthContext> = async (req) => {
* const token = req.headers['authorization'];
* if (!token) {
* throw new UserFacingError('UNAUTHENTICATED', 'Missing auth token');
* }
* const user = await verifyToken(token);
* return { auth: { user } };
* };
*
* // Registered as POST /api/myFlow (uses flow name)
* export const mySecureFlowFn = onCallGenkit(
* { contextProvider: authProvider },
* myFlow
* );
* ```
*
* @param flow - The Genkit flow to wrap
* @returns A CallableAzureFunction with `handler`, `flow`, `run`, `stream`, and `flowName`
*/
declare function onCallGenkit<F extends Flow>(flow: F): CallableAzureFunction<F>;
/**
* Creates an Azure Functions handler for a Genkit flow with options.
*
* @param opts - Configuration options for the Azure Functions handler
* @param flow - The Genkit flow to wrap
* @returns A CallableAzureFunction with `handler`, `flow`, `run`, `stream`, and `flowName`
*/
declare function onCallGenkit<C extends ActionContext, F extends Flow>(opts: AzureFunctionsOptions<C, FlowInput<F>> & {
streaming: true;
}, flow: F): CallableAzureFunction<F>;
declare function onCallGenkit<C extends ActionContext, F extends Flow>(opts: AzureFunctionsOptions<C, FlowInput<F>>, flow: F): CallableAzureFunction<F>;
/**
* Context with API key authentication
*/
interface ApiKeyContext extends ActionContext {
auth: {
apiKey: string;
};
}
/**
* Context with bearer token authentication
*/
interface BearerTokenContext extends ActionContext {
auth: {
token: string;
};
}
/**
* Creates a context provider that requires an API key in a specific header.
*
* @example
* ```typescript
* // Require API key to match a specific value
* const callable = onCallGenkit(
* { contextProvider: requireApiKey('X-API-Key', process.env.API_KEY!) },
* myFlow
* );
*
* // Or with a custom validation function
* const callable = onCallGenkit(
* {
* contextProvider: requireApiKey('X-API-Key', async (key) => {
* const valid = await validateApiKey(key);
* if (!valid) {
* throw new UserFacingError('PERMISSION_DENIED', 'Invalid API key');
* }
* })
* },
* myFlow
* );
* ```
*/
declare function requireApiKey(headerName: string, expectedValueOrValidator: string | ((apiKey: string) => void | Promise<void>)): ContextProvider<ApiKeyContext>;
/**
* Creates a context provider that requires Bearer token authentication.
*
* @example
* ```typescript
* // With custom token validation
* const callable = onCallGenkit(
* {
* contextProvider: requireBearerToken(async (token) => {
* const user = await verifyJWT(token);
* return { auth: { user } };
* })
* },
* myFlow
* );
* ```
*/
declare function requireBearerToken<C extends ActionContext = BearerTokenContext>(validateToken: (token: string) => C | Promise<C>): ContextProvider<C>;
/**
* Creates a context provider that requires a specific header to be present.
*
* @example
* ```typescript
* // Require header to exist
* const callable = onCallGenkit(
* { contextProvider: requireHeader('X-Request-ID') },
* myFlow
* );
*
* // Require header to have specific value
* const callable = onCallGenkit(
* { contextProvider: requireHeader('X-API-Version', '2.0') },
* myFlow
* );
* ```
*/
declare function requireHeader(headerName: string, expectedValue?: string): ContextProvider<ActionContext>;
/**
* Creates a context provider that always allows requests (no authentication).
* Useful for public endpoints.
*
* @example
* ```typescript
* const callable = onCallGenkit(
* { contextProvider: allowAll() },
* myPublicFlow
* );
* ```
*/
declare function allowAll(): ContextProvider<ActionContext>;
/**
* Combines multiple context providers. All providers must succeed.
* The returned context is a merge of all provider contexts.
*
* @example
* ```typescript
* const callable = onCallGenkit(
* {
* contextProvider: allOf(
* requireHeader('X-Request-ID'),
* requireApiKey('X-API-Key', process.env.API_KEY!)
* )
* },
* myFlow
* );
* ```
*/
declare function allOf<C extends ActionContext = ActionContext>(...providers: ContextProvider<ActionContext>[]): ContextProvider<C>;
/**
* Tries context providers in order, returning the first one that succeeds.
* If all providers fail, throws the error from the last provider.
*
* @example
* ```typescript
* // Accept either API key or Bearer token
* const callable = onCallGenkit(
* {
* contextProvider: anyOf(
* requireApiKey('X-API-Key', process.env.API_KEY!),
* requireBearerToken(async (token) => {
* const user = await verifyJWT(token);
* return { auth: { user } };
* })
* )
* },
* myFlow
* );
* ```
*/
declare function anyOf<C extends ActionContext = ActionContext>(...providers: ContextProvider<ActionContext>[]): ContextProvider<C>;
export { type ApiKeyContext, type AzureFunctionsActionContext, type AzureFunctionsFlowResponse, type AzureFunctionsHandler, type AzureFunctionsOptions, type BearerTokenContext, type CallableAzureFunction, type CorsOptions, type FlowErrorResponse, type FlowResponse, type FlowRunOptions, allOf, allowAll, anyOf, onCallGenkit as default, onCallGenkit, requireApiKey, requireBearerToken, requireHeader };