@salesforce/plugin-agent 1.14.5-dev.4 → 1.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -33
- package/lib/commands/agent/create.js +2 -2
- package/lib/commands/agent/create.js.map +1 -1
- package/lib/commands/agent/generate/template.d.ts +4 -3
- package/lib/commands/agent/generate/template.js +108 -20
- package/lib/commands/agent/generate/template.js.map +1 -1
- package/lib/commands/agent/generate/test-spec.d.ts +0 -4
- package/lib/commands/agent/generate/test-spec.js +76 -130
- package/lib/commands/agent/generate/test-spec.js.map +1 -1
- package/lib/commands/agent/test/create.d.ts +1 -2
- package/lib/commands/agent/test/create.js +16 -62
- package/lib/commands/agent/test/create.js.map +1 -1
- package/lib/flags.d.ts +0 -1
- package/lib/flags.js +0 -31
- package/lib/flags.js.map +1 -1
- package/lib/handleTestResults.js +21 -2
- package/lib/handleTestResults.js.map +1 -1
- package/lib/read-dir.d.ts +1 -0
- package/lib/read-dir.js +16 -0
- package/lib/read-dir.js.map +1 -0
- package/messages/agent.generate.template.md +7 -7
- package/messages/agent.generate.test-spec.md +0 -8
- package/messages/agent.test.create.md +7 -13
- package/npm-shrinkwrap.json +33 -194
- package/oclif.lock +16 -91
- package/oclif.manifest.json +16 -37
- package/package.json +5 -5
- package/schemas/agent-generate-template.json +5 -2
|
@@ -1,71 +1,45 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Copyright (c)
|
|
2
|
+
* Copyright (c) 2024, salesforce.com, inc.
|
|
3
3
|
* All rights reserved.
|
|
4
4
|
* Licensed under the BSD 3-Clause license.
|
|
5
5
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
6
|
*/
|
|
7
|
-
import { readFile } from 'node:fs/promises';
|
|
8
7
|
import { join } from 'node:path';
|
|
9
|
-
import {
|
|
10
|
-
import { SfCommand
|
|
11
|
-
import { Messages, SfError
|
|
8
|
+
import { readFile } from 'node:fs/promises';
|
|
9
|
+
import { SfCommand } from '@salesforce/sf-plugins-core';
|
|
10
|
+
import { Messages, SfError } from '@salesforce/core';
|
|
12
11
|
import { generateTestSpec } from '@salesforce/agents';
|
|
13
12
|
import { select, input, confirm, checkbox } from '@inquirer/prompts';
|
|
14
13
|
import { XMLParser } from 'fast-xml-parser';
|
|
15
|
-
import { ComponentSetBuilder } from '@salesforce/source-deploy-retrieve';
|
|
16
14
|
import { theme } from '../../../inquirer-theme.js';
|
|
15
|
+
import { readDir } from '../../../read-dir.js';
|
|
17
16
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
18
17
|
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.generate.test-spec');
|
|
19
18
|
function castArray(value) {
|
|
20
19
|
return Array.isArray(value) ? value : [value];
|
|
21
20
|
}
|
|
22
|
-
|
|
23
|
-
* Prompts the user for test case information through interactive prompts.
|
|
24
|
-
*
|
|
25
|
-
* @param genAiPlugins - Record mapping topic names to GenAiPlugin XML file paths (used to find the related actions)
|
|
26
|
-
* @param genAiFunctions - Array of GenAiFunction names from the GenAiPlanner
|
|
27
|
-
* @returns Promise resolving to a TestCase object containing:
|
|
28
|
-
* - utterance: The user input string
|
|
29
|
-
* - expectedTopic: The expected topic for classification
|
|
30
|
-
* - expectedActions: Array of expected action names
|
|
31
|
-
* - expectedOutcome: Expected outcome string
|
|
32
|
-
*
|
|
33
|
-
* @remarks
|
|
34
|
-
* This function guides users through creating a test case by:
|
|
35
|
-
* 1. Prompting for an utterance
|
|
36
|
-
* 2. Selecting an expected topic (from GenAiPlugins specified in the Bot's GenAiPlanner)
|
|
37
|
-
* 3. Choosing expected actions (from GenAiFunctions in the GenAiPlanner or GenAiPlugin)
|
|
38
|
-
* 4. Defining an expected outcome
|
|
39
|
-
*/
|
|
40
|
-
async function promptForTestCase(genAiPlugins, genAiFunctions) {
|
|
21
|
+
async function promptForTestCase(genAiPlugins) {
|
|
41
22
|
const utterance = await input({
|
|
42
23
|
message: 'Utterance',
|
|
43
24
|
validate: (d) => d.length > 0 || 'utterance cannot be empty',
|
|
44
25
|
theme,
|
|
45
26
|
});
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
theme,
|
|
50
|
-
});
|
|
51
|
-
// GenAiFunctions (aka actions) can be defined in the GenAiPlugin or GenAiPlanner
|
|
52
|
-
// the actions from the planner are passed in as an argument to this function
|
|
53
|
-
// the actions from the plugin are read from the GenAiPlugin file
|
|
54
|
-
let actions = [];
|
|
55
|
-
if (genAiPlugins[expectedTopic]) {
|
|
56
|
-
const genAiPluginXml = await readFile(genAiPlugins[expectedTopic], 'utf-8');
|
|
57
|
-
const parser = new XMLParser();
|
|
58
|
-
const parsed = parser.parse(genAiPluginXml);
|
|
59
|
-
actions = castArray(parsed.GenAiPlugin.genAiFunctions ?? []).map((f) => f.functionName);
|
|
60
|
-
}
|
|
61
|
-
const expectedActions = await checkbox({
|
|
27
|
+
const customKey = '<OTHER>';
|
|
28
|
+
const topics = Object.keys(genAiPlugins);
|
|
29
|
+
const askForOtherActions = async () => (await input({
|
|
62
30
|
message: 'Expected action(s)',
|
|
63
|
-
|
|
31
|
+
validate: (d) => {
|
|
32
|
+
if (!d.length) {
|
|
33
|
+
return 'expected value cannot be empty';
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
},
|
|
64
37
|
theme,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
38
|
+
}))
|
|
39
|
+
.split(',')
|
|
40
|
+
.map((a) => a.trim());
|
|
41
|
+
const askForBotRating = async () => input({
|
|
42
|
+
message: 'Expected response',
|
|
69
43
|
validate: (d) => {
|
|
70
44
|
if (!d.length) {
|
|
71
45
|
return 'expected value cannot be empty';
|
|
@@ -74,96 +48,62 @@ async function promptForTestCase(genAiPlugins, genAiFunctions) {
|
|
|
74
48
|
},
|
|
75
49
|
theme,
|
|
76
50
|
});
|
|
51
|
+
const expectedTopic = await select({
|
|
52
|
+
message: 'Expected topic',
|
|
53
|
+
choices: [...topics, customKey],
|
|
54
|
+
theme,
|
|
55
|
+
});
|
|
56
|
+
if (expectedTopic === customKey) {
|
|
57
|
+
return {
|
|
58
|
+
utterance,
|
|
59
|
+
expectedTopic: await input({
|
|
60
|
+
message: 'Expected topic',
|
|
61
|
+
validate: (d) => {
|
|
62
|
+
if (!d.length) {
|
|
63
|
+
return 'expected value cannot be empty';
|
|
64
|
+
}
|
|
65
|
+
return true;
|
|
66
|
+
},
|
|
67
|
+
theme,
|
|
68
|
+
}),
|
|
69
|
+
// If the user selects OTHER for the topic, then we don't have a genAiPlugin to get actions from so we ask for them for custom input
|
|
70
|
+
expectedActions: await askForOtherActions(),
|
|
71
|
+
expectedOutcome: await askForBotRating(),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const genAiPluginXml = await readFile(genAiPlugins[expectedTopic], 'utf-8');
|
|
75
|
+
const parser = new XMLParser();
|
|
76
|
+
const parsed = parser.parse(genAiPluginXml);
|
|
77
|
+
const actions = castArray(parsed.GenAiPlugin.genAiFunctions).map((f) => f.functionName);
|
|
78
|
+
let expectedActions = await checkbox({
|
|
79
|
+
message: 'Expected action(s)',
|
|
80
|
+
choices: [...actions, customKey],
|
|
81
|
+
theme,
|
|
82
|
+
required: true,
|
|
83
|
+
});
|
|
84
|
+
if (expectedActions.includes(customKey)) {
|
|
85
|
+
const additional = await askForOtherActions();
|
|
86
|
+
expectedActions = [...expectedActions.filter((a) => a !== customKey), ...additional];
|
|
87
|
+
}
|
|
88
|
+
const expectedOutcome = await askForBotRating();
|
|
77
89
|
return {
|
|
78
90
|
utterance,
|
|
79
|
-
expectedTopic,
|
|
80
91
|
expectedActions,
|
|
81
92
|
expectedOutcome,
|
|
93
|
+
expectedTopic,
|
|
82
94
|
};
|
|
83
95
|
}
|
|
84
|
-
/**
|
|
85
|
-
* Retrieves GenAIPlugins and GenAiFunctions from a Bot's GenAiPlanner
|
|
86
|
-
*
|
|
87
|
-
* We have to get the bot version and planner for the selected bot so that we can get
|
|
88
|
-
* the actions (GenAiFunctions) and topics (GenAiPlugins) that can be selected for the
|
|
89
|
-
* test cases.
|
|
90
|
-
*
|
|
91
|
-
* The BotVersion tells us which GenAiPlanner to use, and the GenAiPlanner
|
|
92
|
-
* tells us which GenAiPlugins and GenAiFunctions are available. More GenAiFunctions
|
|
93
|
-
* might be available in the GenAiPlugin, so we read those later when the user
|
|
94
|
-
* has selected a GenAiPlugin/topic - inside of `promptForTestCase`.
|
|
95
|
-
*
|
|
96
|
-
* @param subjectName - The name of the Bot to analyze
|
|
97
|
-
* @param cs - ComponentSet containing Bot, GenAiPlanner, and GenAiPlugin components
|
|
98
|
-
*
|
|
99
|
-
* @returns Object containing:
|
|
100
|
-
* - genAiPlugins: Record of plugin names to their file paths
|
|
101
|
-
* - genAiFunctions: Array of function names
|
|
102
|
-
*/
|
|
103
|
-
async function getPluginsAndFunctions(subjectName, cs) {
|
|
104
|
-
const botVersions = [...cs.filter((component) => component.type.name === 'Bot' && component.fullName !== '*')].reduce((acc, component) => ({
|
|
105
|
-
...acc,
|
|
106
|
-
// this resolves to the BotVersion filepath
|
|
107
|
-
[component.fullName]: cs.getComponentFilenamesByNameAndType({
|
|
108
|
-
fullName: component.fullName,
|
|
109
|
-
type: 'Bot',
|
|
110
|
-
})[0],
|
|
111
|
-
}), {});
|
|
112
|
-
const genAiPlanners = [
|
|
113
|
-
...cs.filter((component) => component.type.name === 'GenAiPlanner' && component.fullName !== '*'),
|
|
114
|
-
].reduce((acc, component) => ({
|
|
115
|
-
...acc,
|
|
116
|
-
[component.fullName]: cs.getComponentFilenamesByNameAndType({
|
|
117
|
-
fullName: component.fullName,
|
|
118
|
-
type: 'GenAiPlanner',
|
|
119
|
-
})[0],
|
|
120
|
-
}), {});
|
|
121
|
-
const parser = new XMLParser();
|
|
122
|
-
const botVersionXml = await readFile(botVersions[subjectName], 'utf-8');
|
|
123
|
-
const parsedBotVersion = parser.parse(botVersionXml);
|
|
124
|
-
const plannerXml = await readFile(genAiPlanners[parsedBotVersion.BotVersion.conversationDefinitionPlanners.genAiPlannerName ?? subjectName], 'utf-8');
|
|
125
|
-
const parsedPlanner = parser.parse(plannerXml);
|
|
126
|
-
const genAiFunctions = castArray(parsedPlanner.GenAiPlanner.genAiFunctions).map(({ genAiFunctionName }) => genAiFunctionName);
|
|
127
|
-
const genAiPlugins = castArray(parsedPlanner.GenAiPlanner.genAiPlugins).reduce((acc, { genAiPluginName }) => ({
|
|
128
|
-
...acc,
|
|
129
|
-
[genAiPluginName]: cs.getComponentFilenamesByNameAndType({
|
|
130
|
-
fullName: genAiPluginName,
|
|
131
|
-
type: 'GenAiPlugin',
|
|
132
|
-
})[0],
|
|
133
|
-
}), {});
|
|
134
|
-
return { genAiPlugins, genAiFunctions };
|
|
135
|
-
}
|
|
136
96
|
export default class AgentGenerateTestSpec extends SfCommand {
|
|
137
97
|
static summary = messages.getMessage('summary');
|
|
138
98
|
static description = messages.getMessage('description');
|
|
139
99
|
static examples = messages.getMessages('examples');
|
|
140
100
|
static enableJsonFlag = false;
|
|
141
101
|
static state = 'beta';
|
|
142
|
-
static flags = {
|
|
143
|
-
'output-dir': Flags.directory({
|
|
144
|
-
char: 'd',
|
|
145
|
-
summary: messages.getMessage('flags.output-dir.summary'),
|
|
146
|
-
default: 'specs',
|
|
147
|
-
}),
|
|
148
|
-
'output-file': Flags.string({
|
|
149
|
-
char: 'f',
|
|
150
|
-
summary: messages.getMessage('flags.output-file.summary'),
|
|
151
|
-
default: 'agentTestSpec.yaml',
|
|
152
|
-
}),
|
|
153
|
-
};
|
|
154
102
|
async run() {
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const cs = await ComponentSetBuilder.build({
|
|
158
|
-
metadata: {
|
|
159
|
-
metadataEntries: ['GenAiPlanner', 'GenAiPlugin', 'Bot'],
|
|
160
|
-
directoryPaths,
|
|
161
|
-
},
|
|
162
|
-
});
|
|
163
|
-
const botsComponents = cs.filter((component) => component.type.name === 'Bot');
|
|
164
|
-
const bots = [...botsComponents.map((c) => c.fullName).filter((n) => n !== '*')];
|
|
103
|
+
const botsDir = join('force-app', 'main', 'default', 'bots');
|
|
104
|
+
const bots = await readDir(botsDir);
|
|
165
105
|
if (bots.length === 0) {
|
|
166
|
-
throw new SfError(`No agents found in ${
|
|
106
|
+
throw new SfError(`No agents found in ${botsDir}`, 'NoAgentsFoundError');
|
|
167
107
|
}
|
|
168
108
|
const subjectType = await select({
|
|
169
109
|
message: 'What are you testing',
|
|
@@ -175,7 +115,6 @@ export default class AgentGenerateTestSpec extends SfCommand {
|
|
|
175
115
|
choices: bots,
|
|
176
116
|
theme,
|
|
177
117
|
});
|
|
178
|
-
const { genAiPlugins, genAiFunctions } = await getPluginsAndFunctions(subjectName, cs);
|
|
179
118
|
const name = await input({
|
|
180
119
|
message: 'Enter a name for the test definition',
|
|
181
120
|
validate(d) {
|
|
@@ -184,6 +123,12 @@ export default class AgentGenerateTestSpec extends SfCommand {
|
|
|
184
123
|
return 'Name cannot be empty';
|
|
185
124
|
}
|
|
186
125
|
return true;
|
|
126
|
+
// TODO: add back validation once we refine the regex
|
|
127
|
+
// check against FORTY_CHAR_API_NAME_REGEX
|
|
128
|
+
// if (!FORTY_CHAR_API_NAME_REGEX.test(d)) {
|
|
129
|
+
// return 'The non-namespaced portion an API name must begin with a letter, contain only letters, numbers, and underscores, not contain consecutive underscores, and not end with an underscore.';
|
|
130
|
+
// }
|
|
131
|
+
// return true;
|
|
187
132
|
},
|
|
188
133
|
theme,
|
|
189
134
|
});
|
|
@@ -191,30 +136,31 @@ export default class AgentGenerateTestSpec extends SfCommand {
|
|
|
191
136
|
message: 'Enter a description for test definition (optional)',
|
|
192
137
|
theme,
|
|
193
138
|
});
|
|
139
|
+
const genAiPluginDir = join('force-app', 'main', 'default', 'genAiPlugins');
|
|
140
|
+
const genAiPlugins = Object.fromEntries((await readDir(genAiPluginDir)).map((genAiPlugin) => [
|
|
141
|
+
genAiPlugin.replace('.genAiPlugin-meta.xml', ''),
|
|
142
|
+
join(genAiPluginDir, genAiPlugin),
|
|
143
|
+
]));
|
|
194
144
|
const testCases = [];
|
|
195
145
|
do {
|
|
196
146
|
this.log();
|
|
197
147
|
this.styledHeader(`Adding test case #${testCases.length + 1}`);
|
|
198
148
|
// eslint-disable-next-line no-await-in-loop
|
|
199
|
-
testCases.push(await promptForTestCase(genAiPlugins
|
|
149
|
+
testCases.push(await promptForTestCase(genAiPlugins));
|
|
200
150
|
} while ( // eslint-disable-next-line no-await-in-loop
|
|
201
151
|
await confirm({
|
|
202
152
|
message: 'Would you like to add another test case',
|
|
203
153
|
default: true,
|
|
204
154
|
}));
|
|
205
155
|
this.log();
|
|
206
|
-
const outputFile = join(flags['output-dir'], `${subjectName}-${flags['output-file']}`);
|
|
207
|
-
if (existsSync(outputFile)) {
|
|
208
|
-
await this.confirm({ message: `File ${outputFile} already exists. Overwrite?`, defaultAnswer: false });
|
|
209
|
-
}
|
|
210
156
|
await generateTestSpec({
|
|
211
157
|
name,
|
|
212
158
|
description,
|
|
213
159
|
subjectType,
|
|
214
160
|
subjectName,
|
|
215
161
|
testCases,
|
|
216
|
-
},
|
|
217
|
-
this.log(`Created ${
|
|
162
|
+
}, `${name}-test-spec.yaml`);
|
|
163
|
+
this.log(`Created ${name}-test-spec.yaml`);
|
|
218
164
|
}
|
|
219
165
|
}
|
|
220
166
|
//# sourceMappingURL=test-spec.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"test-spec.js","sourceRoot":"","sources":["../../../../src/commands/agent/generate/test-spec.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"test-spec.js","sourceRoot":"","sources":["../../../../src/commands/agent/generate/test-spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,12 +11,11 @@ export default class AgentTestCreate extends SfCommand<AgentTestCreateResult> {
|
|
|
11
11
|
static readonly examples: string[];
|
|
12
12
|
static readonly state = "beta";
|
|
13
13
|
static readonly flags: {
|
|
14
|
+
spec: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
15
|
'target-org': import("@oclif/core/interfaces").OptionFlag<import("@salesforce/core").Org, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
16
|
'api-version': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
17
|
preview: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
18
|
'no-prompt': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
-
spec: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
-
"api-name": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
19
|
};
|
|
21
20
|
private mso?;
|
|
22
21
|
run(): Promise<AgentTestCreateResult>;
|
|
@@ -4,58 +4,26 @@
|
|
|
4
4
|
* Licensed under the BSD 3-Clause license.
|
|
5
5
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
6
|
*/
|
|
7
|
-
import { join
|
|
8
|
-
import { existsSync } from 'node:fs';
|
|
7
|
+
import { join } from 'node:path';
|
|
9
8
|
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
|
|
10
|
-
import { Lifecycle, Messages
|
|
9
|
+
import { Lifecycle, Messages } from '@salesforce/core';
|
|
11
10
|
import { AgentTester, AgentTestCreateLifecycleStages } from '@salesforce/agents';
|
|
12
11
|
import { MultiStageOutput } from '@oclif/multi-stage-output';
|
|
13
|
-
import { makeFlags, promptForFlag, promptForYamlFile } from '../../../flags.js';
|
|
14
|
-
// TODO: fix this REGEX for validating Salesforce API names
|
|
15
|
-
// for example: LocalInfoAgent passes but not LocalInfoAgentTest
|
|
16
|
-
// export const FORTY_CHAR_API_NAME_REGEX =
|
|
17
|
-
// /^(?=.{1,57}$)[a-zA-Z]([a-zA-Z0-9]|_(?!_)){0,14}(__[a-zA-Z]([a-zA-Z0-9]|_(?!_)){0,39})?$/;
|
|
18
12
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
19
13
|
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.test.create');
|
|
20
|
-
const FLAGGABLE_PROMPTS = {
|
|
21
|
-
'api-name': {
|
|
22
|
-
message: messages.getMessage('flags.api-name.summary'),
|
|
23
|
-
validate: (d) => {
|
|
24
|
-
// ensure that it's not empty
|
|
25
|
-
if (!d.length) {
|
|
26
|
-
return 'Name cannot be empty';
|
|
27
|
-
}
|
|
28
|
-
// validate that it contains no spaces or special chars (expect for underscores)
|
|
29
|
-
if (d.match(/[^a-zA-Z0-9_]/)) {
|
|
30
|
-
return 'Name must contain only letters, numbers, and underscores';
|
|
31
|
-
}
|
|
32
|
-
// check against FORTY_CHAR_API_NAME_REGEX once it's fixed
|
|
33
|
-
// if (!FORTY_CHAR_API_NAME_REGEX.test(d)) {
|
|
34
|
-
// return 'The non-namespaced portion an API name must begin with a letter, contain only letters, numbers, and underscores, not contain consecutive underscores, and not end with an underscore.';
|
|
35
|
-
// }
|
|
36
|
-
return true;
|
|
37
|
-
},
|
|
38
|
-
required: true,
|
|
39
|
-
},
|
|
40
|
-
spec: {
|
|
41
|
-
message: messages.getMessage('flags.spec.summary'),
|
|
42
|
-
validate: (d) => {
|
|
43
|
-
const specPath = resolve(d);
|
|
44
|
-
if (!existsSync(specPath)) {
|
|
45
|
-
return 'Please enter an existing test spec (yaml) file';
|
|
46
|
-
}
|
|
47
|
-
return true;
|
|
48
|
-
},
|
|
49
|
-
required: true,
|
|
50
|
-
},
|
|
51
|
-
};
|
|
52
14
|
export default class AgentTestCreate extends SfCommand {
|
|
53
15
|
static summary = messages.getMessage('summary');
|
|
54
16
|
static description = messages.getMessage('description');
|
|
55
17
|
static examples = messages.getMessages('examples');
|
|
56
18
|
static state = 'beta';
|
|
57
19
|
static flags = {
|
|
58
|
-
|
|
20
|
+
spec: Flags.file({
|
|
21
|
+
summary: messages.getMessage('flags.spec.summary'),
|
|
22
|
+
description: messages.getMessage('flags.spec.description'),
|
|
23
|
+
char: 's',
|
|
24
|
+
required: true,
|
|
25
|
+
exists: true,
|
|
26
|
+
}),
|
|
59
27
|
'target-org': Flags.requiredOrg(),
|
|
60
28
|
'api-version': Flags.orgApiVersion(),
|
|
61
29
|
preview: Flags.boolean({
|
|
@@ -69,24 +37,13 @@ export default class AgentTestCreate extends SfCommand {
|
|
|
69
37
|
mso;
|
|
70
38
|
async run() {
|
|
71
39
|
const { flags } = await this.parse(AgentTestCreate);
|
|
72
|
-
// throw error if --json is used and not all required flags are provided
|
|
73
|
-
if (this.jsonEnabled()) {
|
|
74
|
-
if (!flags['api-name']) {
|
|
75
|
-
throw messages.createError('error.missingRequiredFlags', ['api-name']);
|
|
76
|
-
}
|
|
77
|
-
if (!flags.spec) {
|
|
78
|
-
throw messages.createError('error.missingRequiredFlags', ['spec']);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
40
|
const agentTester = new AgentTester(flags['target-org'].getConnection(flags['api-version']));
|
|
82
|
-
const apiName = flags['api-name'] ?? (await promptForFlag(FLAGGABLE_PROMPTS['api-name']));
|
|
83
|
-
const spec = flags.spec ?? (await promptForYamlFile(FLAGGABLE_PROMPTS.spec));
|
|
84
41
|
const lifecycle = Lifecycle.getInstance();
|
|
85
42
|
lifecycle.on(AgentTestCreateLifecycleStages.CreatingLocalMetadata, async () => {
|
|
86
43
|
this.mso = new MultiStageOutput({
|
|
87
44
|
jsonEnabled: this.jsonEnabled(),
|
|
88
45
|
stages: Object.values(AgentTestCreateLifecycleStages),
|
|
89
|
-
title: `Creating test for ${spec}`,
|
|
46
|
+
title: `Creating test for ${flags.spec}`,
|
|
90
47
|
});
|
|
91
48
|
this.mso?.skipTo(AgentTestCreateLifecycleStages.CreatingLocalMetadata);
|
|
92
49
|
return Promise.resolve();
|
|
@@ -104,19 +61,16 @@ export default class AgentTestCreate extends SfCommand {
|
|
|
104
61
|
}
|
|
105
62
|
return Promise.resolve();
|
|
106
63
|
});
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
message: messages.getMessage('prompt.confirm', [
|
|
64
|
+
const confirmationCallback = flags['no-prompt']
|
|
65
|
+
? async () => Promise.resolve(true)
|
|
66
|
+
: async (spec) => this.confirm({
|
|
67
|
+
message: messages.getMessage('prompt.confirm', [spec.name]),
|
|
111
68
|
defaultAnswer: false,
|
|
112
69
|
});
|
|
113
|
-
|
|
114
|
-
throw new SfError(`An AiEvaluationDefinition with the name ${apiName} already exists in the org.`);
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
const { path, contents } = await agentTester.create(apiName, spec, {
|
|
70
|
+
const { path, contents } = await agentTester.create(flags.spec, {
|
|
118
71
|
outputDir: join('force-app', 'main', 'default', 'aiEvaluationDefinitions'),
|
|
119
72
|
preview: flags.preview,
|
|
73
|
+
confirmationCallback,
|
|
120
74
|
});
|
|
121
75
|
if (flags.preview) {
|
|
122
76
|
this.mso?.skipTo(AgentTestCreateLifecycleStages.Done);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../../src/commands/agent/test/create.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,IAAI,EAAE,
|
|
1
|
+
{"version":3,"file":"create.js","sourceRoot":"","sources":["../../../../src/commands/agent/test/create.ts"],"names":[],"mappings":"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"}
|
package/lib/flags.d.ts
CHANGED
|
@@ -20,7 +20,6 @@ export declare const testOutputDirFlag: Interfaces.FlagDefinition<string, Interf
|
|
|
20
20
|
requiredOrDefaulted: false;
|
|
21
21
|
}>;
|
|
22
22
|
export declare function makeFlags<T extends Record<string, FlaggablePrompt>>(flaggablePrompts: T): FlagsOfPrompts<T>;
|
|
23
|
-
export declare const promptForYamlFile: (flagDef: FlaggablePrompt) => Promise<string>;
|
|
24
23
|
export declare const promptForFlag: (flagDef: FlaggablePrompt) => Promise<string>;
|
|
25
24
|
export declare const validateAgentType: (agentType?: string, required?: boolean) => string | undefined;
|
|
26
25
|
export declare const validateMaxTopics: (maxTopics?: number) => number | undefined;
|
package/lib/flags.js
CHANGED
|
@@ -4,13 +4,10 @@
|
|
|
4
4
|
* Licensed under the BSD 3-Clause license.
|
|
5
5
|
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
|
|
6
6
|
*/
|
|
7
|
-
import { readdir } from 'node:fs/promises';
|
|
8
|
-
import { join, relative } from 'node:path';
|
|
9
7
|
import { Flags } from '@salesforce/sf-plugins-core';
|
|
10
8
|
import { Messages, SfError } from '@salesforce/core';
|
|
11
9
|
import { camelCaseToTitleCase } from '@salesforce/kit';
|
|
12
10
|
import { select, input as inquirerInput } from '@inquirer/prompts';
|
|
13
|
-
import autocomplete from 'inquirer-autocomplete-standalone';
|
|
14
11
|
import { theme } from './inquirer-theme.js';
|
|
15
12
|
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
|
|
16
13
|
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'shared');
|
|
@@ -45,34 +42,6 @@ export function makeFlags(flaggablePrompts) {
|
|
|
45
42
|
}),
|
|
46
43
|
]));
|
|
47
44
|
}
|
|
48
|
-
async function traverseForYamlFiles(dir) {
|
|
49
|
-
const files = await readdir(dir, { withFileTypes: true });
|
|
50
|
-
const results = [];
|
|
51
|
-
for (const file of files) {
|
|
52
|
-
const fullPath = join(dir, file.name);
|
|
53
|
-
if (file.isDirectory()) {
|
|
54
|
-
// eslint-disable-next-line no-await-in-loop
|
|
55
|
-
results.push(...(await traverseForYamlFiles(fullPath)));
|
|
56
|
-
}
|
|
57
|
-
else if (file.name.endsWith('.yaml') || file.name.endsWith('.yml')) {
|
|
58
|
-
results.push(fullPath);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return results;
|
|
62
|
-
}
|
|
63
|
-
export const promptForYamlFile = async (flagDef) => {
|
|
64
|
-
const options = await traverseForYamlFiles(process.cwd());
|
|
65
|
-
return autocomplete({
|
|
66
|
-
message: flagDef.message,
|
|
67
|
-
// eslint-disable-next-line @typescript-eslint/require-await
|
|
68
|
-
source: async (input) => {
|
|
69
|
-
const opts = options.map((o) => ({ name: relative(process.cwd(), o), value: o }));
|
|
70
|
-
if (!input)
|
|
71
|
-
return opts;
|
|
72
|
-
return opts.filter((o) => o.name.includes(input));
|
|
73
|
-
},
|
|
74
|
-
});
|
|
75
|
-
};
|
|
76
45
|
export const promptForFlag = async (flagDef) => {
|
|
77
46
|
const message = flagDef.promptMessage ?? flagDef.message.replace(/\.$/, '');
|
|
78
47
|
if (flagDef.options) {
|
package/lib/flags.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"flags.js","sourceRoot":"","sources":["../src/flags.ts"],"names":[],"mappings":"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"}
|
package/lib/handleTestResults.js
CHANGED
|
@@ -14,6 +14,25 @@ async function writeFileToDir(outputDir, fileName, content) {
|
|
|
14
14
|
await mkdir(outputDir, { recursive: true });
|
|
15
15
|
await writeFile(join(outputDir, fileName), content);
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Clean a string by replacing HTML entities with their respective characters. Implementation done by copilot.
|
|
19
|
+
*
|
|
20
|
+
* This is only required until W-17594913 is resolved by SF Eval
|
|
21
|
+
*
|
|
22
|
+
* @param str - The string to clean.
|
|
23
|
+
* @returns The cleaned string with all HTML entities replaced with their respective characters.
|
|
24
|
+
*/
|
|
25
|
+
function decodeHtmlEntities(str) {
|
|
26
|
+
const entities = {
|
|
27
|
+
'"': '"',
|
|
28
|
+
''': "'",
|
|
29
|
+
'&': '&',
|
|
30
|
+
'<': '<',
|
|
31
|
+
'>': '>',
|
|
32
|
+
''': "'",
|
|
33
|
+
};
|
|
34
|
+
return str.replace(/&[a-zA-Z0-9#]+;/g, (entity) => entities[entity] || entity);
|
|
35
|
+
}
|
|
17
36
|
function makeSimpleTable(data, title) {
|
|
18
37
|
if (Object.keys(data).length === 0) {
|
|
19
38
|
return '';
|
|
@@ -66,8 +85,8 @@ export function humanFormat(results) {
|
|
|
66
85
|
data: testCase.testResults.map((r) => ({
|
|
67
86
|
test: humanFriendlyName(r.name),
|
|
68
87
|
result: r.result === 'PASS' ? ansis.green('Pass') : ansis.red('Fail'),
|
|
69
|
-
expected: r.expectedValue,
|
|
70
|
-
actual: r.actualValue,
|
|
88
|
+
expected: decodeHtmlEntities(r.expectedValue),
|
|
89
|
+
actual: decodeHtmlEntities(r.actualValue),
|
|
71
90
|
})),
|
|
72
91
|
width: '100%',
|
|
73
92
|
});
|