openai
Version:
The official TypeScript library for the OpenAI API
154 lines (134 loc) • 5.63 kB
text/typescript
import * as Errors from '../error';
import type { ApiKeySetter } from '../client';
import type { FinalizedRequestInit } from './types';
import type { ProviderRequestContext } from './provider';
import { readEnv } from './utils';
export interface BedrockEndpointOptions {
/** AWS region used to derive the default Mantle endpoint. */
region?: string | undefined;
/** Bedrock API root. Defaults to AWS_BEDROCK_BASE_URL or the regional Mantle endpoint. */
baseURL?: string | null | undefined;
}
export interface BedrockBearerOptions {
/** Explicit Bedrock bearer credential. Set to null to skip the environment bearer fallback. */
apiKey?: string | null | undefined;
/** A function that resolves a Bedrock bearer credential before every request attempt. */
tokenProvider?: ApiKeySetter | undefined;
}
export interface BedrockRequestAuth {
prepareRequest(request: FinalizedRequestInit, context: ProviderRequestContext): void | Promise<void>;
}
export type BedrockAuthFactory = () => BedrockRequestAuth;
export function errorWithCause(message: string, cause: unknown): Errors.OpenAIError {
const error = new Errors.OpenAIError(message) as Errors.OpenAIError & { cause?: unknown };
error.cause = cause;
return error;
}
export function normalizeOptionalString(value: string | null | undefined): string | undefined {
const normalized = typeof value === 'string' ? value.trim() : undefined;
return normalized ? normalized : undefined;
}
function normalizeBaseURL(baseURL: string): string {
const url = new URL(baseURL);
const responsesMatch = url.pathname.match(/\/responses(?:\/.*)?$/);
if (responsesMatch?.index !== undefined) {
url.pathname = url.pathname.slice(0, responsesMatch.index) || '/';
}
return url.toString().replace(/\/$/, '');
}
export function resolveBedrockEndpoint(options: BedrockEndpointOptions): {
region: string | undefined;
baseURL: string;
} {
if (options.region !== undefined && !normalizeOptionalString(options.region)) {
throw new Errors.OpenAIError('The Bedrock AWS `region` must not be empty.');
}
if (
options.baseURL !== undefined &&
options.baseURL !== null &&
!normalizeOptionalString(options.baseURL)
) {
throw new Errors.OpenAIError('The Bedrock `baseURL` must not be empty.');
}
const region =
normalizeOptionalString(options.region) ??
normalizeOptionalString(readEnv('AWS_REGION')) ??
normalizeOptionalString(readEnv('AWS_DEFAULT_REGION'));
const configuredBaseURL =
options.baseURL === undefined ? normalizeOptionalString(readEnv('AWS_BEDROCK_BASE_URL'))
: options.baseURL === null ? undefined
: normalizeOptionalString(options.baseURL);
if (configuredBaseURL) return { region, baseURL: normalizeBaseURL(configuredBaseURL) };
if (!region) {
throw new Errors.OpenAIError(
'Bedrock requires an AWS region. Pass `region` to `bedrock(...)`, or set `AWS_REGION` or `AWS_DEFAULT_REGION`.',
);
}
return { region, baseURL: `https://bedrock-mantle.${region}.api.aws/openai/v1` };
}
export function assertProviderOwnsAuthorization(headers: Headers): void {
if (headers.has('authorization')) {
throw new Errors.OpenAIError(
'Bedrock provider authentication cannot be combined with a custom `Authorization` header.',
);
}
}
class BedrockBearerAuth implements BedrockRequestAuth {
constructor(private readonly tokenProvider: ApiKeySetter) {}
async prepareRequest(request: FinalizedRequestInit, _context: ProviderRequestContext): Promise<void> {
const headers = new Headers(request.headers);
assertProviderOwnsAuthorization(headers);
let token: unknown;
try {
token = await this.tokenProvider();
} catch (cause) {
throw errorWithCause('Failed to resolve a bearer credential for Bedrock.', cause);
}
if (typeof token !== 'string' || !token.trim()) {
throw new Errors.OpenAIError('The Bedrock bearer credential provider must return a non-empty string.');
}
headers.set('authorization', `Bearer ${token}`);
request.headers = headers;
}
}
export function resolveBedrockBearerAuth(
options: BedrockBearerOptions,
{ allowEnvironment = true }: { allowEnvironment?: boolean } = {},
): { factory: BedrockAuthFactory | undefined; explicit: boolean } {
if (
options.apiKey !== undefined &&
options.apiKey !== null &&
(typeof options.apiKey !== 'string' || !options.apiKey.trim())
) {
throw new Errors.OpenAIError('The Bedrock bearer credential must not be empty.');
}
if (options.apiKey != null && options.tokenProvider) {
throw new Errors.OpenAIError(
'The `apiKey` and `tokenProvider` options are mutually exclusive. Configure only one.',
);
}
if (options.tokenProvider) {
const tokenProvider = options.tokenProvider;
return { factory: () => new BedrockBearerAuth(tokenProvider), explicit: true };
}
if (options.apiKey != null) {
const apiKey = options.apiKey;
return { factory: () => new BedrockBearerAuth(async () => apiKey), explicit: true };
}
if (allowEnvironment && options.apiKey !== null && readEnv('AWS_BEARER_TOKEN_BEDROCK')) {
return {
explicit: false,
factory: () =>
new BedrockBearerAuth(async () => {
const token = readEnv('AWS_BEARER_TOKEN_BEDROCK');
if (!token) {
throw new Errors.OpenAIError(
'Could not find credentials for Bedrock. Set `AWS_BEARER_TOKEN_BEDROCK` or configure AWS credential authentication.',
);
}
return token;
}),
};
}
return { factory: undefined, explicit: false };
}