@twilio-alpha/assistants-eval
Version:
promptfoo extension for writing AI evaluations for Twilio AI Assistants
126 lines (105 loc) • 3.13 kB
text/typescript
import fetchRetry, { FetchLibrary, RequestInitRetryParams } from 'fetch-retry';
import pino from 'pino';
import {
ApiProvider,
CallApiContextParams,
CallApiOptionsParams,
ProviderOptions,
ProviderResponse,
} from 'promptfoo/dist/src/types/providers';
const fetcher = fetchRetry(global.fetch);
export type ExternalProviderOptions = ProviderOptions & {
config: {
url: string;
identifier?: string;
requestOptions?: Omit<RequestInit, 'body'>;
retryOptions?: RequestInitRetryParams<FetchLibrary>;
};
};
export default class ExternalProvider implements ApiProvider {
protected readonly logger: pino.Logger;
readonly defaultUrl: string;
private readonly identifier: string;
readonly requestOptions: RequestInit;
private readonly retryOptions: RequestInitRetryParams<FetchLibrary>;
constructor({ id, label, config }: ExternalProviderOptions) {
this.defaultUrl = config.url;
this.identifier = id ?? label ?? 'test-ai-evaluator';
const requestOptions = config.requestOptions ?? {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
};
this.requestOptions = {
method: 'POST',
...requestOptions,
headers: {
'Content-Type': 'application/json',
...requestOptions.headers,
},
};
this.retryOptions = config.retryOptions ?? {
retries: 5,
retryDelay(attempt: number) {
return 2 ** attempt * 1000;
},
retryOn: [500, 501, 502, 503, 504],
};
this.logger = pino({
level: process.env.LOG_LEVEL || 'info',
}).child({
module: this.constructor.name,
});
}
id() {
return this.identifier;
}
protected getUrl(
_prompt: string,
_context?: CallApiContextParams,
_callApiOptions?: CallApiOptionsParams,
) {
return this.defaultUrl;
}
protected getBody(
prompt: string,
_context?: CallApiContextParams,
_callApiOptions?: CallApiOptionsParams,
) {
return JSON.stringify({
body: prompt,
});
}
protected async getResponse(response: Response): Promise<ProviderResponse> {
const data = await response.json();
return { output: data as string };
}
public async callApi(
prompt: string,
context?: CallApiContextParams,
callApiOptions?: CallApiOptionsParams,
): Promise<ProviderResponse> {
const request = {
...this.requestOptions,
...this.retryOptions,
body: this.getBody(prompt, context, callApiOptions),
};
this.logger.debug(
'Calling endpoint %s with request payload %s',
this.getUrl(prompt, context, callApiOptions),
JSON.stringify(request),
);
const fetchResponse = await fetcher(
this.getUrl(prompt, context, callApiOptions),
request,
);
if (fetchResponse.status > 300) {
this.logger.error('Received error response %s', fetchResponse.statusText);
throw new Error(fetchResponse.statusText);
}
const response = await this.getResponse(fetchResponse);
this.logger.debug('Received response %s', JSON.stringify(response));
return response;
}
}