@salesforce/agents
Version:
Client side APIs for working with Salesforce agents
214 lines • 8.23 kB
JavaScript
;
/*
* Copyright (c) 2024, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentPreview = void 0;
const node_crypto_1 = require("node:crypto");
const core_1 = require("@salesforce/core");
const agent_1 = require("./agent");
const maybe_mock_1 = require("./maybe-mock");
const apexUtils_1 = require("./apexUtils");
;
const messages = new core_1.Messages('@salesforce/agents', 'agentPreview', new Map([["invalidBotId", "The Bot ID provided must begin with \"0Xx\" and be either 15 or 18 characters. Found: %s"], ["agentApexDebuggingError", "Agent must have a user assigned to enable apex debug mode."]]));
/**
* A service to interact with an agent. Start an interactive session,
* send messages to the agent, and end the session.
*
* **Examples**
*
* Create an instance of the service:
*
* `const agentPreview = new AgentPreview(connection, botId);`
*
* Start an interactive session:
*
* `const { sessionId } = await agentPreview.start();`
*
* Send a message to the agent using the session ID from the startResponse:
*
* `const sendResponse = await agentPreview.send(sessionId, message);`
*
* End an interactive session:
*
* `await agentPreview.end(sessionId, 'UserRequest');`
*
* Enable Apex Debug Mode:
*
* `agentPreview.toggleApexDebugMode(true);`
*/
class AgentPreview {
apiBase = 'https://api.salesforce.com/einstein/ai-agent/v1';
connection;
logger;
maybeMock;
apexDebugMode;
apexTraceFlag;
botId;
/**
* Create an instance of the service.
*
* @param connection The connection to use to make requests.
* @param botId The ID of the agent (`Bot` ID).
*/
constructor(connection, botId) {
this.connection = connection;
this.logger = core_1.Logger.childFromRoot(this.constructor.name);
this.maybeMock = new maybe_mock_1.MaybeMock(connection);
if (!botId.startsWith('0Xx') || ![15, 18].includes(botId.length)) {
throw messages.createError('invalidBotId', [botId]);
}
this.botId = botId;
}
/**
* Start an interactive session with the agent.
*
* @returns `AgentPreviewStartResponse`, which includes a session ID needed for other actions.
*/
async start() {
const url = `${this.apiBase}/agents/${this.botId}/sessions`;
this.logger.debug(`Starting agent preview session for botId: ${this.botId}`);
const body = {
externalSessionKey: (0, node_crypto_1.randomUUID)(),
instanceConfig: {
endpoint: this.connection.instanceUrl,
},
streamingCapabilities: {
chunkTypes: ['Text'],
},
bypassUser: true,
};
try {
return await this.maybeMock.request('POST', url, body);
}
catch (err) {
throw core_1.SfError.wrap(err);
}
}
/**
* Send a message to the agent using the session ID obtained by calling `start()`.
*
* @param sessionId A session ID provided by first calling `agentPreview.start()`.
* @param message A message to send to the agent.
* @returns `AgentPreviewSendResponse`
*/
async send(sessionId, message) {
const url = `${this.apiBase}/sessions/${sessionId}/messages`;
const body = {
message: {
// https://developer.salesforce.com/docs/einstein/genai/guide/agent-api-examples.html#send-synchronous-messages
// > A number that you provide to represent the sequence ID. Increase this number for each subsequent message in this session.
sequenceId: Date.now(),
type: 'Text',
text: message,
},
variables: [],
};
this.logger.debug(`Sending message to botId: ${this.botId} with apexDebugMode ${this.apexDebugMode ? 'enabled' : 'disabled'}`);
try {
// If apex debug mode is enabled, ensure we have a trace flag for the bot user and
// if there isn't one, create one.
const start = Date.now();
if (this.apexDebugMode) {
await this.ensureTraceFlag();
}
const response = await this.maybeMock.request('POST', url, body);
if (this.apexDebugMode) {
// get apex debug logs and look for a log within the start and end time
const apexLog = await (0, apexUtils_1.getDebugLog)(this.connection, start, Date.now());
if (apexLog) {
if (apexLog.Id)
this.logger.debug(`Apex debug log ID for message is ${apexLog.Id}`);
response.apexDebugLog = apexLog;
}
else {
this.logger.debug('No apex debug log found for this message');
}
}
return response;
}
catch (err) {
throw core_1.SfError.wrap(err);
}
}
/**
* Ends an interactive session with the agent.
*
* @param sessionId A session ID provided by first calling `agentPreview.start()`.
* @param reason A reason why the interactive session was ended.
* @returns `AgentPreviewEndResponse`
*/
async end(sessionId, reason) {
const url = `${this.apiBase}/sessions/${sessionId}`;
this.logger.debug(`Ending agent preview session for botId: ${this.botId} with sessionId: ${sessionId}`);
try {
// https://developer.salesforce.com/docs/einstein/genai/guide/agent-api-examples.html#end-session
return await this.maybeMock.request('DELETE', url, undefined, {
'x-session-end-reason': reason,
});
}
catch (err) {
throw core_1.SfError.wrap(err);
}
}
/**
* Get the status of the Agent API (UP | DOWN).
*
* @returns `ApiStatus`
*/
async status() {
const base = 'https://test.api.salesforce.com';
const url = `${base}/einstein/ai-agent/v1/status`;
try {
return await this.maybeMock.request('GET', url);
}
catch (err) {
throw core_1.SfError.wrap(err);
}
}
/**
* Enable or disable Apex Debug Mode, which will enable trace flags for the Bot user
* and create apex debug logs for use within VS Code's Apex Replay Debugger.
*
* @param enable Whether to enable or disable Apex Debug Mode.
*/
toggleApexDebugMode(enable) {
this.apexDebugMode = enable;
this.logger.debug(`Apex Debug Mode is now ${enable ? 'enabled' : 'disabled'}`);
}
async getBotUserId() {
const agent = new agent_1.Agent({ connection: this.connection, nameOrId: this.botId });
const botMetadata = await agent.getBotMetadata();
return botMetadata.BotUserId;
}
// If apex debug mode is enabled, ensure we have a trace flag for the bot user
// that is not expired checking in this order:
// 1. instance var (this.apexTraceFlag)
// 2. query the org
// 3. create a new trace flag
async ensureTraceFlag() {
if (this.apexTraceFlag) {
const expDate = this.apexTraceFlag.ExpirationDate;
if (expDate && new Date(expDate) > new Date()) {
this.logger.debug(`Using cached apexTraceFlag with ExpirationDate of ${expDate}`);
return;
}
else {
this.logger.debug('Cached apex trace flag is expired');
}
}
const userId = await this.getBotUserId();
if (!userId) {
throw messages.createError('agentApexDebuggingError');
}
this.apexTraceFlag = await (0, apexUtils_1.findTraceFlag)(this.connection, userId);
if (!this.apexTraceFlag) {
await (0, apexUtils_1.createTraceFlag)(this.connection, userId);
}
}
}
exports.AgentPreview = AgentPreview;
//# sourceMappingURL=agentPreview.js.map