UNPKG

@salesforce/agents

Version:

Client side APIs for working with Salesforce agents

151 lines 6.14 kB
"use strict"; /* * Copyright 2026, Salesforce, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.AgentforceStudioTester = void 0; exports.normalizeAgentforceStudioResults = normalizeAgentforceStudioResults; const core_1 = require("@salesforce/core"); const kit_1 = require("@salesforce/kit"); const maybe_mock_1 = require("./maybe-mock"); const utils_1 = require("./utils"); /** * A service for testing agents using `AiTestingDefinition` metadata (Agentforce Studio). * Start asynchronous test runs, get or poll for test status, and get detailed test results. * * **Examples** * * Create an instance of the service: * * `const agentTester = new AgentforceStudioTester(connection);` * * Start a test run: * * `const startResponse = await agentTester.start(aiTestSuiteDefName);` * * Get the status for a test run: * * `const status = await agentTester.status(startResponse.runId);` * * Get detailed results for a test run: * * `const results = await agentTester.results(startResponse.runId);` */ class AgentforceStudioTester { maybeMock; constructor(connection) { this.maybeMock = new maybe_mock_1.MaybeMock(connection); } /** * Initiates a test run (i.e., AI test suite evaluation). * * @param testDefinitionName - The name of the AI test suite definition to run. * @returns Promise that resolves with the response from starting the test. */ async start(testDefinitionName) { const url = '/einstein/ai-testing/runs'; const result = await this.maybeMock.request('POST', url, { testDefinitionName, }); if (result?.runId === undefined) { throw core_1.SfError.create({ name: 'TestInProgress', message: 'a test run is already in progress' }); } return result; } /** * Get the status of a test run. * * @param {string} runId * @returns {Promise<AgentforceStudioTestStatusResponse>} */ async status(runId) { const url = `/einstein/ai-testing/runs/${runId}`; return this.maybeMock.request('GET', url); } /** * Poll the status of a test run until the tests are complete or the timeout is reached. * * @param {string} runId * @param {Duration} timeout * @returns {Promise<AgentforceStudioTestResultsResponse>} */ async poll(runId, { timeout = kit_1.Duration.minutes(5) } = {}) { const frequency = kit_1.env.getNumber('SF_AGENT_TEST_POLLING_FREQUENCY_MS', 1000); const lifecycle = core_1.Lifecycle.getInstance(); const client = await core_1.PollingClient.create({ poll: async () => { const statusResponse = await this.status(runId); if (statusResponse.status.toLowerCase() !== 'new') { const resultsResponse = await this.results(runId); const totalTestCases = resultsResponse.testCases.length; const isPassingScorer = (scorerResponse) => { try { const { actualValue, expectedValue } = JSON.parse(scorerResponse); return actualValue !== undefined && actualValue === expectedValue; } catch { return false; } }; const passingTestCases = resultsResponse.testCases.filter((tc) => tc.testScorerResults.length > 0 && tc.testScorerResults.every((s) => isPassingScorer(s.scorerResponse))).length; const failingTestCases = totalTestCases - passingTestCases; await lifecycle.emit('AGENT_TEST_POLLING_EVENT', { // to match the other AGENT_TEST_POLLING_EVENT for consumers jobId: runId, status: resultsResponse.status, totalTestCases, failingTestCases, passingTestCases, }); const terminalStatuses = ['SUCCESS', 'FAILED', 'TERMINATED']; if (terminalStatuses.includes(resultsResponse.status)) { return { payload: resultsResponse, completed: true }; } } return { completed: false }; }, frequency: kit_1.Duration.milliseconds(frequency), timeout, }); return client.subscribe(); } /** * Get detailed test run results. * * @param {string} runId * @returns {Promise<AgentforceStudioTestResultsResponse>} */ async results(runId) { const url = `/einstein/ai-testing/runs/${runId}/results`; const results = await this.maybeMock.request('GET', url); return normalizeAgentforceStudioResults(results); } } exports.AgentforceStudioTester = AgentforceStudioTester; /** Decodes HTML entities in test result subject responses and scorer responses. */ function normalizeAgentforceStudioResults(results) { return { ...results, testCases: results.testCases.map((tc) => ({ ...tc, subjectResponse: (0, utils_1.decodeHtmlEntities)(tc.subjectResponse), testScorerResults: tc.testScorerResults.map((scorer) => ({ ...scorer, scorerResponse: (0, utils_1.decodeHtmlEntities)(scorer.scorerResponse), })), })), }; } //# sourceMappingURL=agentforceStudioTester.js.map