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

W-17606916 feat: add mock agentCreateV2 API and tests #31

Merged
merged 4 commits into from
Jan 18, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
65 changes: 63 additions & 2 deletions src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { Duration } from '@salesforce/kit';
import {
type SfAgent,
type AgentCreateConfig,
type AgentCreateConfigV2,
type AgentCreateResponse,
type AgentCreateResponseV2,
type AgentJobSpec,
type AgentJobSpecV2,
type AgentJobSpecCreateConfig,
Expand All @@ -41,6 +43,12 @@ export const AgentCreateLifecycleStages = {
RetrievingMetadata: 'retrievingmetadata',
};

export const AgentCreateLifecycleStagesV2 = {
Creating: 'creatingAgent',
Previewing: 'previewingAgent',
Retrieving: 'retrievingAgent',
};

/**
* Class for creating Agents and agent specs.
*/
Expand Down Expand Up @@ -137,6 +145,61 @@ export class Agent implements SfAgent {
return response;
}

/**
* Creates an agent from a configuration, optionally saving the agent in an org.
*
* @param config a configuration for creating or previewing an agent
* @returns
*/
public async createV2(config: AgentCreateConfigV2): Promise<AgentCreateResponseV2> {
const url = '/connect/ai-assist/create-agent';

// When previewing agent creation just return the response.
if (!config.saveAgent) {
this.logger.debug(
`Previewing agent creation using config: ${inspect(config)} in project: ${this.project.getPath()}`
);
await Lifecycle.getInstance().emit(AgentCreateLifecycleStagesV2.Previewing, {});
return this.maybeMock.request<AgentCreateResponseV2>('POST', url, config);
}

// When saving agent creation we need to retrieve the created metadata.
this.logger.debug(`Creating agent using config: ${inspect(config)} in project: ${this.project.getPath()}`);
await Lifecycle.getInstance().emit(AgentCreateLifecycleStagesV2.Creating, {});
const response = await this.maybeMock.request<AgentCreateResponseV2>('POST', url, config);

await Lifecycle.getInstance().emit(AgentCreateLifecycleStagesV2.Retrieving, {});

//
// When retrieving all agent metadata by a Bot API name works in SDR we can use that.
//

// Query for the Bot API name by the Bot ID.
// const botApiName = this.connection.singleRecordQuery('get bot from response.agentId?.botId');
// const cs = await ComponentSetBuilder.build({
// metadata: {
// metadataEntries: [`Bot:${}`],
// directoryPaths: [this.project.getDefaultPackage().path],
// }
// });
// const retrieve = await cs.retrieve({
// usernameOrConnection: this.connection,
// merge: true,
// format: 'source',
// output: this.project.getDefaultPackage().path ?? 'force-app',
// });
// const retrieveResult = await retrieve.pollStatus({
// frequency: Duration.milliseconds(200),
// timeout: Duration.minutes(5),
// });

// if (!retrieveResult.response.success) {
// throw new SfError(`Unable to retrieve ${retrieveResult.response.id}`);
// }

return response;
}

/**
* Create an agent spec from provided data.
*
Expand All @@ -162,8 +225,6 @@ export class Agent implements SfAgent {
/**
* Create an agent spec from provided data.
*
* V2 API: /connect/ai-assist/draft-agent-topics
*
* @param config The configuration used to generate an agent spec.
*/
public async createSpecV2(config: AgentJobSpecCreateConfigV2): Promise<AgentJobSpecV2> {
Expand Down
4 changes: 3 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

export {
type AgentCreateConfig,
type AgentCreateConfigV2,
type AgentCreateResponse,
type AgentCreateResponseV2,
type AgentJobSpec,
type AgentJobSpecV2,
type AgentJobSpecCreateConfig,
Expand All @@ -18,7 +20,7 @@ export {
type DraftAgentTopicsResponse,
SfAgent,
} from './types';
export { Agent, AgentCreateLifecycleStages } from './agent';
export { Agent, AgentCreateLifecycleStages, AgentCreateLifecycleStagesV2 } from './agent';
export {
AgentTester,
humanFormat,
Expand Down
114 changes: 114 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,73 @@ export type AgentCreateConfig = AgentJobSpecCreateConfig & {
jobSpec: AgentJobSpec;
};

export type AgentCreateConfigV2 = DraftAgentTopicsBody & {
generationInfo: {
defaultInfo: {
/**
* List of topics from an agent spec.
*/
preDefinedTopics?: DraftAgentTopics;
};
};
/**
* Whether to persist the agent creation in the org (true) or preview
* what would be created (false).
*
* Default: false
*/
saveAgent?: boolean;

/**
* Settings for the agent being created. Needed only when saveAgent=true
*/
agentSettings?: {
/**
* The name to use for the Agent metadata to be created.
*/
agentName: string;
/**
* The API name to use for the Agent metadata to be created.
*/
agentApiName: string;
/**
* The GenAiPlanner metadata ID if already created in the org.
*/
plannerId?: string;
/**
* User ID of an existing user or "new" to create a new user.
*
* Determines what this agent can access and do. If your agent uses
* features or objects that require additional permissions, assign
* a custom user.
*
* Default: new
*/
userId: string;
/**
* Store conversation transcripts, including end-user data, in event logs
* for this agent for troubleshooting. If false, conversation data is
* replaced with, "Sensitive data not available."
*
* Default: false
*/
enrichLogs?: boolean;
/**
* The conversational style of your agent's responses.
*
* Default: Casual
*/
tone?: 'Casual';
/**
* The language your agent uses in conversations. Agent currently
* supports English only.
*
* Default: en_US
*/
primaryLanguage?: 'en_US';
};
};

/**
* The request body to send to the `draft-agent-topics` API.
*/
Expand Down Expand Up @@ -129,6 +196,53 @@ export type AgentCreateResponse = {
errorMessage?: string;
};

export type AgentCreateResponseV2 = {
isSuccess: boolean;
errorMessage?: string;
/**
* If the agent was created with saveAgent=true, these are the
* IDs that make up an agent; Bot, BotVersion, and GenAiPlanner metadata.
*/
agentId?: {
botId: string;
botVersionId: string;
plannerId: string;
};
agentDefinition: {
agentDescription: string;
topics: [
{
scope: string;
topic: string;
actions: [
{
actionName: string;
exampleOutput: string;
actionDescription: string;
inputs: [
{
inputName: string;
inputDataType: string;
inputDescription: string;
}
];
outputs: [
{
outputName: string;
outputDataType: string;
outputDescription: string;
}
];
}
];
instructions: string[];
classificationDescription: string;
}
];
sampleUtterances: string[];
};
};

export type DraftAgentTopics = [
{
name: string;
Expand Down
58 changes: 56 additions & 2 deletions test/agents.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
* 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
*/
import { join } from 'node:path';
import { expect } from 'chai';
import { MockTestOrgData, TestContext } from '@salesforce/core/testSetup';
import { Connection, SfProject } from '@salesforce/core';
import { Agent } from '../src/agent';
import type { AgentJobSpecCreateConfig } from '../src/types';
import type { AgentJobSpecCreateConfig, AgentCreateConfigV2 } from '../src/types';

describe('Agents', () => {
const $$ = new TestContext();
Expand All @@ -18,7 +19,7 @@ describe('Agents', () => {
beforeEach(async () => {
$$.inProject(true);
testOrg = new MockTestOrgData();
process.env.SF_MOCK_DIR = 'test/mocks';
process.env.SF_MOCK_DIR = join('test', 'mocks');
connection = await testOrg.getConnection();
connection.instanceUrl = 'https://mydomain.salesforce.com';
// restore the connection sandbox so that it doesn't override the builtin mocking (MaybeMock)
Expand Down Expand Up @@ -63,6 +64,59 @@ describe('Agents', () => {
expect(output.topics[0]).to.have.property('name', 'Guest_Experience_Enhancement');
});

it('createV2 save agent', async () => {
process.env.SF_MOCK_DIR = join('test', 'mocks', 'createAgent-Save');
const sfProject = SfProject.getInstance();
const agent = new Agent(connection, sfProject);
const config: AgentCreateConfigV2 = {
agentType: 'customer',
saveAgent: true,
agentSettings: {
agentName: 'My First Agent',
agentApiName: 'My_First_Agent',
userId: 'new',
},
generationInfo: {
defaultInfo: {
role: 'answer questions about vacation rentals',
companyName: 'Coral Cloud Enterprises',
companyDescription: 'Provide vacation rentals and activities',
},
},
generationSettings: {
maxNumOfTopics: 10,
},
};
const response = await agent.createV2(config);
expect(response).to.have.property('isSuccess', true);
expect(response).to.have.property('agentId');
expect(response).to.have.property('agentDefinition');
});

it('createV2 preview agent', async () => {
process.env.SF_MOCK_DIR = join('test', 'mocks', 'createAgent-Preview');
const sfProject = SfProject.getInstance();
const agent = new Agent(connection, sfProject);
const config: AgentCreateConfigV2 = {
agentType: 'customer',
saveAgent: false,
generationInfo: {
defaultInfo: {
role: 'answer questions about vacation rentals',
companyName: 'Coral Cloud Enterprises',
companyDescription: 'Provide vacation rentals and activities',
},
},
generationSettings: {
maxNumOfTopics: 10,
},
};
const response = await agent.createV2(config);
expect(response).to.have.property('isSuccess', true);
expect(response).to.not.have.property('agentId');
expect(response).to.have.property('agentDefinition');
});

it('create', async () => {
const sfProject = SfProject.getInstance();
const agent = new Agent(connection, sfProject);
Expand Down
41 changes: 41 additions & 0 deletions test/mocks/createAgent-Preview/connect_ai-assist_create-agent.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
{
"isSuccess": true,
"agentDefinition": {
"agentDescription": "some agent description",
"topics": [
{
"scope": "some scope",
"topic": "topic_name",
"actions": [
{
"actionName": "some_action_name",
"exampleOutput": "some example output",
"actionDescription": "some description",
"inputs": [
{
"inputName": "input_name_1",
"inputDataType": "string",
"inputDescription": "some description"
},
{
"inputName": "input_name_2",
"inputDataType": "date",
"inputDescription": "some description"
}
],
"outputs": [
{
"outputName": "output_name",
"outputDataType": "string",
"outputDescription": "some description"
}
]
}
],
"instructions": ["instruction 1", "instruction 2"],
"classificationDescription": "some classification description"
}
],
"Sample_Utterances": ["sample utterance 1", "sample utterance 2", "sample utterance 3"]
}
}
Loading
Loading