UNPKG

@twilio-alpha/assistants-eval

Version:

promptfoo extension for writing AI evaluations for Twilio AI Assistants

126 lines (105 loc) 3.13 kB
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; } }