Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add multi-stage-output #16

Merged
merged 5 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@
"flagAliases": [],
"flagChars": [
"f",
"n",
"o"
],
"flags": [
"api-version",
"flags-dir",
"job-spec",
"json",
"spec",
"name",
"target-org"
],
"plugin": "@salesforce/plugin-agent"
Expand All @@ -22,7 +24,7 @@
"flagAliases": [],
"flagChars": [
"d",
"n",
"f",
"o",
"t"
],
Expand All @@ -31,9 +33,9 @@
"company-description",
"company-name",
"company-website",
"file-name",
"flags-dir",
"json",
"name",
"output-dir",
"role",
"target-org",
Expand Down
8 changes: 6 additions & 2 deletions messages/agent.create.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,18 @@ Create an Agent from an agent spec.

Create an Agent from an agent spec. Agent metadata is created in the target org and retrieved to the local project.

# flags.spec.summary
# flags.job-spec.summary

The path to an agent spec file.

# flags.spec.description
# flags.job-spec.description

The agent spec file defines job titles and descriptions for the agent and can be created using the `sf agent create spec` command.

# flags.name.summary

The name of the agent.

# examples

- Create an Agent:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,6 @@ Create an Agent spec.

Create an Agent spec, which is a list of job titles and descriptions that the agent performs.

# flags.name.summary

The name of the agent to create.

# flags.type.summary

The type of agent to create.
Expand All @@ -34,6 +30,10 @@ The website URL for the company.

The location within the project where the agent spec will be written.

# flags.file-name.summary

The name of the file to write the agent spec to.

# examples

- Create an Agent spec in the default location:
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"@inquirer/input": "^4.0.1",
"@inquirer/select": "^4.0.1",
"@oclif/core": "^4",
"@oclif/multi-stage-output": "^0.7.12",
"@salesforce/agents": "^0.1.2",
"@salesforce/core": "^8.5.2",
"@salesforce/kit": "^3.2.1",
Expand Down
45 changes: 41 additions & 4 deletions src/commands/agent/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
import { Messages } from '@salesforce/core';
import { Duration, sleep } from '@salesforce/kit';
import { MultiStageOutput } from '@oclif/multi-stage-output';
import { colorize } from '@oclif/core/ux';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.create');
Expand All @@ -29,24 +31,59 @@ export default class AgentCreate extends SfCommand<AgentCreateResult> {
public static readonly flags = {
'target-org': Flags.requiredOrg(),
'api-version': Flags.orgApiVersion(),
spec: Flags.file({
'job-spec': Flags.file({
char: 'f',
required: true,
summary: messages.getMessage('flags.spec.summary'),
description: messages.getMessage('flags.spec.description'),
summary: messages.getMessage('flags.job-spec.summary'),
description: messages.getMessage('flags.job-spec.description'),
}),
name: Flags.string({
char: 'n',
required: true,
summary: messages.getMessage('flags.name.summary'),
}),
};

public async run(): Promise<AgentCreateResult> {
const { flags } = await this.parse(AgentCreate);
const jsonParsingStage = `Parsing ${flags['job-spec']}`;
const mso = new MultiStageOutput({
jsonEnabled: this.jsonEnabled(),
title: `Creating ${flags.name} Agent`,
stages: [
jsonParsingStage,
'Generating GenAiPlanner metadata',
'Creating agent in org',
'Retrieving agent metadata',
],
});

mso.goto(jsonParsingStage);
await sleep(Duration.milliseconds(200));

mso.goto('Generating GenAiPlanner metadata');
await sleep(Duration.milliseconds(200));

this.log(`Creating agent from spec: ${flags.spec}`);
mso.goto('Creating agent in org');

// POST to /services/data/{api-version}/connect/attach-agent-topics

// To simulate time spent on the server generating the spec.
await sleep(Duration.seconds(5));

mso.goto('Retrieving agent metadata');
await sleep(Duration.seconds(3));

mso.stop();

this.log(
colorize(
'green',
`Successfully created ${flags.name} in ${flags['target-org'].getUsername() ?? 'the target org'}.`
)
);
this.log(`Use ${colorize('dim', `sf org open agent --name ${flags.name}`)} to view the agent in the browser.`);

return { isSuccess: true };
}
}
32 changes: 20 additions & 12 deletions src/commands/agent/generate/spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import figures from '@inquirer/figures';
import { Agent, SfAgent } from '@salesforce/agents';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.create.spec');
const messages = Messages.loadMessages('@salesforce/plugin-agent', 'agent.generate.spec');

export type AgentCreateSpecResult = {
isSuccess: boolean;
Expand All @@ -40,12 +40,6 @@ type FlagsOfPrompts<T extends Record<string, FlaggablePrompt>> = Record<
>;

const FLAGGABLE_PROMPTS = {
name: {
message: messages.getMessage('flags.name.summary'),
validate: (d: string): boolean | string => d.length > 0 || 'Name cannot be empty',
char: 'n',
required: true,
},
type: {
message: messages.getMessage('flags.type.summary'),
validate: (d: string): boolean | string => d.length > 0 || 'Type cannot be empty',
Expand Down Expand Up @@ -124,6 +118,11 @@ export default class AgentCreateSpec extends SfCommand<AgentCreateSpecResult> {
summary: messages.getMessage('flags.output-dir.summary'),
default: 'config',
}),
'file-name': Flags.string({
char: 'f',
summary: messages.getMessage('flags.file-name.summary'),
default: 'agentSpec.json',
}),
};

public async run(): Promise<AgentCreateSpecResult> {
Expand All @@ -142,11 +141,15 @@ export default class AgentCreateSpec extends SfCommand<AgentCreateSpecResult> {

this.log();
this.styledHeader('Agent Details');
const name = await this.getFlagOrPrompt(flags.name, FLAGGABLE_PROMPTS.name);
const type = await this.getFlagOrPrompt(flags.type, FLAGGABLE_PROMPTS.type) as 'customer_facing' | 'employee_facing';
const type = (await this.getFlagOrPrompt(flags.type, FLAGGABLE_PROMPTS.type)) as
| 'customer_facing'
| 'employee_facing';
const role = await this.getFlagOrPrompt(flags.role, FLAGGABLE_PROMPTS.role);
const companyName = await this.getFlagOrPrompt(flags['company-name'], FLAGGABLE_PROMPTS['company-name']);
const companyDescription = await this.getFlagOrPrompt(flags['company-description'], FLAGGABLE_PROMPTS['company-description']);
const companyDescription = await this.getFlagOrPrompt(
flags['company-description'],
FLAGGABLE_PROMPTS['company-description']
);
const companyWebsite = await this.getFlagOrPrompt(flags['company-website'], FLAGGABLE_PROMPTS['company-website']);

this.log();
Expand All @@ -155,11 +158,16 @@ export default class AgentCreateSpec extends SfCommand<AgentCreateSpecResult> {
const connection = flags['target-org'].getConnection(flags['api-version']);
const agent = new Agent(connection, this.project as SfProject) as SfAgent;
const agentSpec = await agent.createSpec({
name, type, role, companyName, companyDescription, companyWebsite
name: flags['file-name'].split('.json')[0],
type,
role,
companyName,
companyDescription,
companyWebsite,
});

// Write a file with the returned job specs
const filePath = join(flags['output-dir'], 'agentSpec.json');
const filePath = join(flags['output-dir'], flags['file-name']);
writeFileSync(filePath, JSON.stringify(agentSpec, null, 4));

this.spinner.stop();
Expand Down
27 changes: 26 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,19 @@
wordwrap "^1.0.0"
wrap-ansi "^7.0.0"

"@oclif/multi-stage-output@^0.7.12":
version "0.7.12"
resolved "https://registry.yarnpkg.com/@oclif/multi-stage-output/-/multi-stage-output-0.7.12.tgz#04df5efb6dce527920cf475c9ad9f20236803ccd"
integrity sha512-MmCgqPb7jC7QUOiX9jMik2njplCO+tRybGxiB55OXDmZ7osifM3KuQb/ykgP4XYn559k3DXeNLFS0NpokuH+mw==
dependencies:
"@oclif/core" "^4"
"@types/react" "^18.3.12"
cli-spinners "^2"
figures "^6.1.0"
ink "^5.0.1"
react "^18.3.1"
wrap-ansi "^9.0.0"

"@oclif/plugin-command-snapshot@^5.2.19":
version "5.2.19"
resolved "https://registry.yarnpkg.com/@oclif/plugin-command-snapshot/-/plugin-command-snapshot-5.2.19.tgz#02f3f2c426aa0791bfcc598c9210e061f98caf54"
Expand Down Expand Up @@ -2966,7 +2979,7 @@ cli-progress@^3.12.0:
dependencies:
string-width "^4.2.3"

cli-spinners@^2.9.2:
cli-spinners@^2, cli-spinners@^2.9.2:
version "2.9.2"
resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41"
integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==
Expand Down Expand Up @@ -3889,6 +3902,13 @@ faye@^1.4.0:
tough-cookie "*"
tunnel-agent "*"

figures@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a"
integrity sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==
dependencies:
is-unicode-supported "^2.0.0"

file-entry-cache@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
Expand Down Expand Up @@ -4788,6 +4808,11 @@ is-unicode-supported@^0.1.0:
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==

is-unicode-supported@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz#09f0ab0de6d3744d48d265ebb98f65d11f2a9b3a"
integrity sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==

is-weakref@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
Expand Down
Loading