@twilio-alpha/assistants-eval
Version:
promptfoo extension for writing AI evaluations for Twilio AI Assistants
142 lines • 5.83 kB
JavaScript
import { randomUUID } from 'crypto';
import { getOptionalEnv, getRequiredEnv } from '../utils/envs.js';
import ExternalProvider from './external.js';
export function getAssistantUrl(assistantId, region) {
const sub = region === 'prod' ? '' : `${region}.`;
return `https://assistants.${sub}twilio.com/v1/Assistants/${assistantId}/Messages`;
}
export default class TwilioProvider extends ExternalProvider {
sessionId;
userIdentity;
region;
constructor(options) {
const { config, id, label, env } = options;
const region = config.twilioRegion ||
env?.TWILIO_REGION ||
getOptionalEnv('TWILIO_REGION') ||
'prod';
const assistantIdConfigValue = typeof config.assistantId === 'string'
? config.assistantId
: config.assistantId?.[region];
const assistantId = assistantIdConfigValue ||
env?.TWILIO_ASSISTANT_ID ||
getOptionalEnv('TWILIO_ASSISTANT_ID') ||
'invalid-assistant-id'; // not throwing an error so that you can use the provider by setting assistantId only as variable on a test
const url = getAssistantUrl(assistantId, region);
const username = config.accountSid ||
env?.TWILIO_ACCOUNT_SID ||
getRequiredEnv('TWILIO_ACCOUNT_SID');
const password = config.authTokenKey
? getRequiredEnv(config.authTokenKey)
: env?.TWILIO_AUTH_TOKEN || getRequiredEnv('TWILIO_AUTH_TOKEN');
const auth = Buffer.from(`${username}:${password}`).toString('base64');
const headers = {};
if (config.constellationOverride) {
headers['User-Agent'] =
`constellationName/${config.constellationOverride}`;
}
super({
id,
label,
config: {
url,
identifier: 'twilio-provider',
requestOptions: {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Basic ${auth}`,
...headers,
},
},
},
env,
});
this.userIdentity = config.identity ?? 'test-evaluator';
this.sessionId = config.sessionId;
this.region = region;
}
getUrl(prompt, context, callApiOptions) {
let url = super.getUrl(prompt, context, callApiOptions);
if (typeof context?.vars.assistantId === 'string') {
url = getAssistantUrl(context.vars.assistantId, this.region);
}
else if (typeof context?.vars.assistantId === 'object') {
if (typeof context.vars.assistantId[this.region] === 'string') {
url = getAssistantUrl(context.vars.assistantId[this.region], this.region);
}
}
return url;
}
getBody(prompt, context, _callApiOptions) {
let sessionId = this.sessionId ?? randomUUID();
if (context?.vars.sessionId) {
if (!context.vars.runId) {
throw new Error(`
Invalid configuration. Can only use sessionId variable if you defined a runId as well. Make sure your promptfooconfig.yaml has the following set:
defaultTest:
vars:
runId: package:@twilio-alpha/assistants-eval:variableHelpers.runId
`.trim());
}
sessionId = `${context.vars.sessionId}_${context.vars.runId}`;
}
let processedPrompt = prompt;
try {
// check if the input is actually an array of messages and just send the last one
if (processedPrompt.trim().startsWith('[')) {
const parsed = JSON.parse(processedPrompt);
if (Array.isArray(parsed) &&
parsed.every((message) => typeof message.role === 'string' &&
typeof message.content === 'string')) {
if (!context?.vars.sessionId) {
throw new Error('In order to use this feature you need to have a sessionId defined as variable.');
}
processedPrompt = parsed[parsed.length - 1].content;
}
}
}
catch (err) {
this.logger.debug(err);
}
const identity = typeof context?.vars.identity === 'string'
? context?.vars.identity
: this.userIdentity;
if (identity.includes('{{runId}}')) {
if (typeof context?.vars.runId !== 'string') {
throw new Error(`
Invalid configuration. Can only use {{runId}} in the identity variable if you defined a runId as well. Make sure your promptfooconfig.yaml has the following set:
defaultTest:
vars:
runId: file://../../node_modules/@twilio-alpha/assistants-ai/dist/utils/evalRunId.js
`.trim());
}
}
return JSON.stringify({
body: processedPrompt,
session_id: sessionId,
identity: context?.vars.identity ?? this.userIdentity,
});
}
async getResponse(response) {
const data = (await response.json());
this.logger.debug('AI Assistant response is %s', JSON.stringify(data));
if (data.status === 'Success') {
const url = new URL(response.url);
const assistantId = url.pathname.split('/')[3];
return {
output: data.body,
metadata: {
sessionId: `webhook:${data.session_id}`,
assistantId,
flagged: data.flagged,
accountSid: data.account_sid,
},
};
}
return {
error: data.error,
};
}
}
//# sourceMappingURL=twilio.js.map