Skip to content

Commit 20ef8ff

Browse files
committed
Add convenience builders for agents + some tests
1 parent 99f81b6 commit 20ef8ff

2 files changed

Lines changed: 371 additions & 0 deletions

File tree

src/sdk.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -675,6 +675,141 @@ export class AgentOps {
675675
return Agent.create(this.client, params, options);
676676
}
677677

678+
/**
679+
* Create an agent from an NPM package.
680+
*
681+
* @param {object} params - Parameters for creating the agent.
682+
* @param {string} params.package_name - NPM package name.
683+
* @param {string} [params.npm_version] - NPM version constraint.
684+
* @param {string} [params.registry_url] - NPM registry URL.
685+
* @param {string[]} [params.agent_setup] - Setup commands to run after installation.
686+
* @param {Core.RequestOptions} [options] - Request options.
687+
* @returns {Promise<Agent>} An {@link Agent} instance.
688+
*/
689+
async createFromNpm(
690+
params: Omit<AgentCreateParams, 'source'> & {
691+
package_name: string;
692+
npm_version?: string;
693+
registry_url?: string;
694+
agent_setup?: string[];
695+
},
696+
options?: Core.RequestOptions,
697+
): Promise<Agent> {
698+
const { package_name, npm_version, registry_url, agent_setup, ...restParams } = params;
699+
700+
const npmConfig: any = { package_name };
701+
if (npm_version !== undefined) npmConfig.npm_version = npm_version;
702+
if (registry_url !== undefined) npmConfig.registry_url = registry_url;
703+
if (agent_setup !== undefined) npmConfig.agent_setup = agent_setup;
704+
705+
return this.create(
706+
{
707+
...restParams,
708+
source: { type: 'npm', npm: npmConfig },
709+
},
710+
options,
711+
);
712+
}
713+
714+
/**
715+
* Create an agent from a Pip package.
716+
*
717+
* @param {object} params - Parameters for creating the agent.
718+
* @param {string} params.package_name - Pip package name.
719+
* @param {string} [params.pip_version] - Pip version constraint.
720+
* @param {string} [params.registry_url] - Pip registry URL.
721+
* @param {string[]} [params.agent_setup] - Setup commands to run after installation.
722+
* @param {Core.RequestOptions} [options] - Request options.
723+
* @returns {Promise<Agent>} An {@link Agent} instance.
724+
*/
725+
async createFromPip(
726+
params: Omit<AgentCreateParams, 'source'> & {
727+
package_name: string;
728+
pip_version?: string;
729+
registry_url?: string;
730+
agent_setup?: string[];
731+
},
732+
options?: Core.RequestOptions,
733+
): Promise<Agent> {
734+
const { package_name, pip_version, registry_url, agent_setup, ...restParams } = params;
735+
736+
const pipConfig: any = { package_name };
737+
if (pip_version !== undefined) pipConfig.pip_version = pip_version;
738+
if (registry_url !== undefined) pipConfig.registry_url = registry_url;
739+
if (agent_setup !== undefined) pipConfig.agent_setup = agent_setup;
740+
741+
return this.create(
742+
{
743+
...restParams,
744+
source: { type: 'pip', pip: pipConfig },
745+
},
746+
options,
747+
);
748+
}
749+
750+
/**
751+
* Create an agent from a Git repository.
752+
*
753+
* @param {object} params - Parameters for creating the agent.
754+
* @param {string} params.repository - Git repository URL.
755+
* @param {string} [params.ref] - Optional Git ref (branch/tag/commit), defaults to main/HEAD.
756+
* @param {string[]} [params.agent_setup] - Setup commands to run after cloning.
757+
* @param {Core.RequestOptions} [options] - Request options.
758+
* @returns {Promise<Agent>} An {@link Agent} instance.
759+
*/
760+
async createFromGit(
761+
params: Omit<AgentCreateParams, 'source'> & {
762+
repository: string;
763+
ref?: string;
764+
agent_setup?: string[];
765+
},
766+
options?: Core.RequestOptions,
767+
): Promise<Agent> {
768+
const { repository, ref, agent_setup, ...restParams } = params;
769+
770+
const gitConfig: any = { repository };
771+
if (ref !== undefined) gitConfig.ref = ref;
772+
if (agent_setup !== undefined) gitConfig.agent_setup = agent_setup;
773+
774+
return this.create(
775+
{
776+
...restParams,
777+
source: { type: 'git', git: gitConfig },
778+
},
779+
options,
780+
);
781+
}
782+
783+
/**
784+
* Create an agent from a storage object.
785+
*
786+
* @param {object} params - Parameters for creating the agent.
787+
* @param {string} params.object_id - Storage object ID.
788+
* @param {string[]} [params.agent_setup] - Setup commands to run after unpacking.
789+
* @param {Core.RequestOptions} [options] - Request options.
790+
* @returns {Promise<Agent>} An {@link Agent} instance.
791+
*/
792+
async createFromObject(
793+
params: Omit<AgentCreateParams, 'source'> & {
794+
object_id: string;
795+
agent_setup?: string[];
796+
},
797+
options?: Core.RequestOptions,
798+
): Promise<Agent> {
799+
const { object_id, agent_setup, ...restParams } = params;
800+
801+
const objectConfig: any = { object_id };
802+
if (agent_setup !== undefined) objectConfig.agent_setup = agent_setup;
803+
804+
return this.create(
805+
{
806+
...restParams,
807+
source: { type: 'object', object: objectConfig },
808+
},
809+
options,
810+
);
811+
}
812+
678813
/**
679814
* Get an agent object by its ID.
680815
*

tests/sdk/agent-ops.test.ts

Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
import { AgentOps } from '../../src/sdk';
2+
import { Agent } from '../../src/sdk/agent';
3+
import type { AgentView } from '../../src/resources/agents';
4+
5+
// Mock the Agent class
6+
jest.mock('../../src/sdk/agent');
7+
8+
describe('AgentOps', () => {
9+
let mockClient: any;
10+
let agentOps: AgentOps;
11+
let mockAgentData: AgentView;
12+
13+
beforeEach(() => {
14+
// Create mock client
15+
mockClient = {
16+
agents: {
17+
create: jest.fn(),
18+
retrieve: jest.fn(),
19+
list: jest.fn(),
20+
},
21+
} as any;
22+
23+
agentOps = new AgentOps(mockClient);
24+
25+
// Mock agent data
26+
mockAgentData = {
27+
id: 'agent-123',
28+
create_time_ms: Date.now(),
29+
name: 'test-agent',
30+
is_public: false,
31+
source: {
32+
type: 'npm',
33+
npm: {
34+
package_name: '@runloop/example-agent',
35+
},
36+
},
37+
};
38+
39+
// Mock Agent.create to return a mock Agent instance
40+
(Agent.create as jest.Mock).mockResolvedValue(new Agent(mockClient, 'agent-123'));
41+
});
42+
43+
describe('createFromNpm', () => {
44+
it('should create an agent from npm package', async () => {
45+
await agentOps.createFromNpm({
46+
name: 'test-agent',
47+
package_name: '@runloop/example-agent',
48+
});
49+
50+
expect(Agent.create).toHaveBeenCalledWith(
51+
mockClient,
52+
{
53+
name: 'test-agent',
54+
source: {
55+
type: 'npm',
56+
npm: {
57+
package_name: '@runloop/example-agent',
58+
},
59+
},
60+
},
61+
undefined,
62+
);
63+
});
64+
65+
it('should create an agent with all npm options', async () => {
66+
await agentOps.createFromNpm({
67+
name: 'test-agent',
68+
package_name: '@runloop/example-agent',
69+
npm_version: '1.2.3',
70+
registry_url: 'https://registry.example.com',
71+
agent_setup: ['npm install', 'npm run setup'],
72+
});
73+
74+
expect(Agent.create).toHaveBeenCalledWith(
75+
mockClient,
76+
{
77+
name: 'test-agent',
78+
source: {
79+
type: 'npm',
80+
npm: {
81+
package_name: '@runloop/example-agent',
82+
npm_version: '1.2.3',
83+
registry_url: 'https://registry.example.com',
84+
agent_setup: ['npm install', 'npm run setup'],
85+
},
86+
},
87+
},
88+
undefined,
89+
);
90+
});
91+
});
92+
93+
describe('createFromPip', () => {
94+
it('should create an agent from pip package', async () => {
95+
await agentOps.createFromPip({
96+
name: 'test-agent',
97+
package_name: 'runloop-example-agent',
98+
});
99+
100+
expect(Agent.create).toHaveBeenCalledWith(
101+
mockClient,
102+
{
103+
name: 'test-agent',
104+
source: {
105+
type: 'pip',
106+
pip: {
107+
package_name: 'runloop-example-agent',
108+
},
109+
},
110+
},
111+
undefined,
112+
);
113+
});
114+
115+
it('should create an agent with all pip options', async () => {
116+
await agentOps.createFromPip({
117+
name: 'test-agent',
118+
package_name: 'runloop-example-agent',
119+
pip_version: '1.2.3',
120+
registry_url: 'https://pypi.example.com',
121+
agent_setup: ['pip install extra-deps'],
122+
});
123+
124+
expect(Agent.create).toHaveBeenCalledWith(
125+
mockClient,
126+
{
127+
name: 'test-agent',
128+
source: {
129+
type: 'pip',
130+
pip: {
131+
package_name: 'runloop-example-agent',
132+
pip_version: '1.2.3',
133+
registry_url: 'https://pypi.example.com',
134+
agent_setup: ['pip install extra-deps'],
135+
},
136+
},
137+
},
138+
undefined,
139+
);
140+
});
141+
});
142+
143+
describe('createFromGit', () => {
144+
it('should create an agent from git repository', async () => {
145+
await agentOps.createFromGit({
146+
name: 'test-agent',
147+
repository: 'https://github.com/example/agent-repo',
148+
});
149+
150+
expect(Agent.create).toHaveBeenCalledWith(
151+
mockClient,
152+
{
153+
name: 'test-agent',
154+
source: {
155+
type: 'git',
156+
git: {
157+
repository: 'https://github.com/example/agent-repo',
158+
},
159+
},
160+
},
161+
undefined,
162+
);
163+
});
164+
165+
it('should create an agent with all git options', async () => {
166+
await agentOps.createFromGit({
167+
name: 'test-agent',
168+
repository: 'https://github.com/example/agent-repo',
169+
ref: 'develop',
170+
agent_setup: ['npm install', 'npm run build'],
171+
});
172+
173+
expect(Agent.create).toHaveBeenCalledWith(
174+
mockClient,
175+
{
176+
name: 'test-agent',
177+
source: {
178+
type: 'git',
179+
git: {
180+
repository: 'https://github.com/example/agent-repo',
181+
ref: 'develop',
182+
agent_setup: ['npm install', 'npm run build'],
183+
},
184+
},
185+
},
186+
undefined,
187+
);
188+
});
189+
});
190+
191+
describe('createFromObject', () => {
192+
it('should create an agent from object', async () => {
193+
await agentOps.createFromObject({
194+
name: 'test-agent',
195+
object_id: 'obj_123',
196+
});
197+
198+
expect(Agent.create).toHaveBeenCalledWith(
199+
mockClient,
200+
{
201+
name: 'test-agent',
202+
source: {
203+
type: 'object',
204+
object: {
205+
object_id: 'obj_123',
206+
},
207+
},
208+
},
209+
undefined,
210+
);
211+
});
212+
213+
it('should create an agent with agent_setup', async () => {
214+
await agentOps.createFromObject({
215+
name: 'test-agent',
216+
object_id: 'obj_123',
217+
agent_setup: ['chmod +x setup.sh', './setup.sh'],
218+
});
219+
220+
expect(Agent.create).toHaveBeenCalledWith(
221+
mockClient,
222+
{
223+
name: 'test-agent',
224+
source: {
225+
type: 'object',
226+
object: {
227+
object_id: 'obj_123',
228+
agent_setup: ['chmod +x setup.sh', './setup.sh'],
229+
},
230+
},
231+
},
232+
undefined,
233+
);
234+
});
235+
});
236+
});

0 commit comments

Comments
 (0)