Skip to content

Commit 7c8322b

Browse files
Merge remote-tracking branch 'origin/main' into next
2 parents 159b43f + c7c3274 commit 7c8322b

11 files changed

Lines changed: 708 additions & 39 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dist-deno
1010
.idea/
1111
.eslintcache
1212
.DS_Store
13+
coverage
1314
coverage-objects
1415
object-coverage-report.json
1516
docs

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,33 @@ The SDK provides object-oriented interfaces for all major Runloop resources:
6161
- **[`runloop.blueprint`](https://runloopai.github.io/api-client-ts/stable/classes/BlueprintOps.html)** - Blueprint management (create, list, build blueprints)
6262
- **[`runloop.snapshot`](https://runloopai.github.io/api-client-ts/stable/classes/SnapshotOps.html)** - Snapshot management (list disk snapshots)
6363
- **[`runloop.storageObject`](https://runloopai.github.io/api-client-ts/stable/classes/StorageObjectOps.html)** - Storage object management (upload, download, list objects)
64+
- **[`runloop.agent`](https://runloopai.github.io/api-client-ts/stable/classes/AgentOps.html)** - Agent management (create, list agents from npm/pip/git)
65+
- **[`runloop.scenario`](https://runloopai.github.io/api-client-ts/stable/classes/ScenarioOps.html)** - Scenario management (list scenarios, start runs)
6466
- **[`runloop.scorer`](https://runloopai.github.io/api-client-ts/stable/classes/ScorerOps.html)** - Scorer management (create, list, validate, update)
6567
- **[`runloop.api`](https://runloopai.github.io/api-client-ts/stable/classes/Runloop.html)** - Direct access to the REST API client
6668

6769
## TypeScript Support
6870

6971
The SDK is fully typed with comprehensive TypeScript definitions:
7072

73+
### Blueprints
74+
75+
Blueprints define reusable devbox configurations. Create blueprints via `runloop.blueprint.create()` and access build logs with `blueprint.logs()`:
76+
77+
```typescript
78+
const blueprint = await runloop.blueprint.create({
79+
name: 'my-blueprint',
80+
dockerfile: 'FROM ubuntu:22.04\nRUN apt-get update',
81+
});
82+
83+
// Get build logs
84+
const logs = await blueprint.logs();
85+
console.log(logs.logs);
86+
87+
// Create a devbox from the blueprint
88+
const devbox = await blueprint.createDevbox({ name: 'my-devbox' });
89+
```
90+
7191
### Scorers
7292

7393
Scorers are custom scoring functions used to evaluate scenario outputs. Create scorers via `runloop.scorer.create()`, then update or validate them with the returned `Scorer` instance:
@@ -87,6 +107,37 @@ const result = await scorer.validate({ scoring_context: { output: 'hello' } });
87107
console.log(result.scoring_result.score);
88108
```
89109

110+
### Scenarios
111+
112+
Scenarios define tasks with a well defined starting environment, task evaluation scorer and an optional reference solution.. Use `runloop.scenario.fromId()` to get a scenario, then `scenario.run()` to start a run with your agent mounted:
113+
114+
```typescript
115+
const scenario = runloop.scenario.fromId('scn_123');
116+
const run = await scenario.run({
117+
run_name: 'my-run',
118+
runProfile: {
119+
mounts: [
120+
{
121+
type: 'agent_mount',
122+
agent_id: 'agt_123',
123+
agent_path: '/home/user/agent',
124+
},
125+
],
126+
},
127+
});
128+
await run.devbox.cmd.exec('python /home/user/agent/main.py');
129+
await run.scoreAndComplete();
130+
```
131+
132+
### Benchmarks
133+
134+
Benchmarks are collections of scenarios for evaluating AI agents. Access via `runloop.api.benchmarks`:
135+
136+
```typescript
137+
const benchmarks = await runloop.api.benchmarks.listPublic();
138+
const definitions = await runloop.api.benchmarks.definitions('benchmark_id');
139+
```
140+
90141
## Migration from API Client
91142

92143
If you're currently using the legacy API, migration is straightforward:

src/sdk.ts

Lines changed: 122 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { StorageObject } from './sdk/storage-object';
88
import { Agent } from './sdk/agent';
99
import { Scorer } from './sdk/scorer';
1010
import { NetworkPolicy } from './sdk/network-policy';
11+
import { Scenario } from './sdk/scenario';
1112

1213
// Import types used in this file
1314
import type {
@@ -21,6 +22,7 @@ import type { ObjectCreateParams, ObjectListParams } from './resources/objects';
2122
import type { AgentCreateParams, AgentListParams } from './resources/agents';
2223
import type { ScorerCreateParams, ScorerListParams } from './resources/scenarios/scorers';
2324
import type { NetworkPolicyCreateParams, NetworkPolicyListParams } from './resources/network-policies';
25+
import type { ScenarioListParams } from './resources/scenarios/scenarios';
2426
import { PollingOptions } from './lib/polling';
2527
import * as Shared from './resources/shared';
2628

@@ -282,6 +284,14 @@ export class RunloopSDK {
282284
*/
283285
public readonly networkPolicy: NetworkPolicyOps;
284286

287+
/**
288+
* **Scenario Operations** - {@link ScenarioOps} for accessing {@link Scenario} class instances.
289+
*
290+
* Scenarios define repeatable AI coding evaluation tests with starting environments and
291+
* success criteria. Use these operations to get existing scenarios by ID or list all scenarios.
292+
*/
293+
public readonly scenario: ScenarioOps;
294+
285295
/**
286296
* Creates a new RunloopSDK instance.
287297
* @param {ClientOptions} [options] - Optional client configuration options.
@@ -295,6 +305,7 @@ export class RunloopSDK {
295305
this.agent = new AgentOps(this.api);
296306
this.scorer = new ScorerOps(this.api);
297307
this.networkPolicy = new NetworkPolicyOps(this.api);
308+
this.scenario = new ScenarioOps(this.api);
298309
}
299310
}
300311

@@ -433,7 +444,7 @@ export class DevboxOps {
433444
}
434445

435446
/**
436-
* List all devboxes with optional filters.
447+
* List devboxes with optional filters (paginated).
437448
* @param {DevboxListParams} [params] - Optional filter parameters.
438449
* @param {Core.RequestOptions} [options] - Request options.
439450
* @returns {Promise<Devbox[]>} An array of {@link Devbox} instances.
@@ -529,7 +540,7 @@ export class BlueprintOps {
529540
}
530541

531542
/**
532-
* List all blueprints with optional filters.
543+
* List blueprints with optional filters (paginated).
533544
* @param {BlueprintListParams} [params] - Optional filter parameters.
534545
* @param {Core.RequestOptions} [options] - Request options.
535546
* @returns {Promise<Blueprint[]>} An array of {@link Blueprint} instances.
@@ -587,7 +598,7 @@ export class SnapshotOps {
587598
}
588599

589600
/**
590-
* List all snapshots.
601+
* List snapshots with optional filters (paginated).
591602
* @param {DevboxListDiskSnapshotsParams} [params] - Optional filter parameters.
592603
* @param {Core.RequestOptions} [options] - Request options.
593604
* @returns {Promise<Snapshot[]>} An array of {@link Snapshot} instances.
@@ -663,7 +674,7 @@ export class StorageObjectOps {
663674
}
664675

665676
/**
666-
* List all storage objects with optional filters.
677+
* List storage objects with optional filters (paginated).
667678
* @param {ObjectListParams} [params] - Optional filter parameters.
668679
* @param {Core.RequestOptions} [options] - Request options.
669680
* @returns {Promise<StorageObject[]>} An array of {@link StorageObject} instances.
@@ -1246,7 +1257,7 @@ export class AgentOps {
12461257
}
12471258

12481259
/**
1249-
* List all agents with optional filters.
1260+
* List agents with optional filters (paginated).
12501261
*
12511262
* @example
12521263
* List all agents:
@@ -1376,7 +1387,7 @@ export class ScorerOps {
13761387
}
13771388

13781389
/**
1379-
* List all scorers with optional filters.
1390+
* List scorers with optional filters (paginated).
13801391
*
13811392
* @example
13821393
* ```typescript
@@ -1472,7 +1483,7 @@ export class NetworkPolicyOps {
14721483
}
14731484

14741485
/**
1475-
* List all network policies with optional filters.
1486+
* List network policies with optional filters (paginated).
14761487
*
14771488
* @example
14781489
* ```typescript
@@ -1497,6 +1508,106 @@ export class NetworkPolicyOps {
14971508
}
14981509
}
14991510

1511+
/**
1512+
* Scenario SDK interface for managing scenarios.
1513+
*
1514+
* @category Scenario
1515+
*
1516+
* @remarks
1517+
* ## Overview
1518+
*
1519+
* The `ScenarioOps` class provides a high-level abstraction for managing scenarios.
1520+
*
1521+
* ## Quickstart
1522+
*
1523+
* Use `fromId()` to get a {@link Scenario} by ID, or `list()` to retrieve all scenarios.
1524+
* Once you have a scenario, call `scenario.run()` to start a {@link ScenarioRun} with
1525+
* your agent mounted.
1526+
*
1527+
* ## Usage
1528+
*
1529+
* This interface is accessed via {@link RunloopSDK.scenario}. You should construct
1530+
* a {@link RunloopSDK} instance and use it from there:
1531+
*
1532+
* @example
1533+
* ```typescript
1534+
* const runloop = new RunloopSDK();
1535+
* const scenario = runloop.scenario.fromId('scn_123');
1536+
*
1537+
* // Get scenario details
1538+
* const info = await scenario.getInfo();
1539+
* console.log(info.name);
1540+
*
1541+
* // Start a run with agent mounted and wait for the devbox to be ready
1542+
* const run = await scenario.run({
1543+
* run_name: 'my-run',
1544+
* runProfile: {
1545+
* mounts: [{
1546+
* type: 'agent_mount',
1547+
* agent_id: 'agt_123',
1548+
* agent_name: null,
1549+
* agent_path: '/home/user/agent',
1550+
* }],
1551+
* },
1552+
* });
1553+
*
1554+
* // Execute your agent on the devbox
1555+
* await run.devbox.cmd.exec('python /home/user/agent/main.py');
1556+
*
1557+
* // Score and complete
1558+
* await run.scoreAndComplete();
1559+
* ```
1560+
*/
1561+
export class ScenarioOps {
1562+
/**
1563+
* @private
1564+
*/
1565+
constructor(private client: RunloopAPI) {}
1566+
1567+
/**
1568+
* Get a scenario object by its ID.
1569+
*
1570+
* @example
1571+
* ```typescript
1572+
* const runloop = new RunloopSDK();
1573+
* const scenario = runloop.scenario.fromId('scn_123');
1574+
* const info = await scenario.getInfo();
1575+
* console.log(info.name);
1576+
* ```
1577+
*
1578+
* @param {string} id - The ID of the scenario
1579+
* @returns {Scenario} A {@link Scenario} instance
1580+
*/
1581+
fromId(id: string): Scenario {
1582+
return Scenario.fromId(this.client, id);
1583+
}
1584+
1585+
/**
1586+
* List scenarios with optional filters (paginated).
1587+
*
1588+
* @example
1589+
* ```typescript
1590+
* const runloop = new RunloopSDK();
1591+
* const scenarios = await runloop.scenario.list({ limit: 10 });
1592+
* console.log(scenarios.map((s) => s.id));
1593+
* ```
1594+
*
1595+
* @param {ScenarioListParams} [params] - Optional filter parameters
1596+
* @param {Core.RequestOptions} [options] - Request options
1597+
* @returns {Promise<Scenario[]>} An array of {@link Scenario} instances
1598+
*/
1599+
async list(params?: ScenarioListParams, options?: Core.RequestOptions): Promise<Scenario[]> {
1600+
const result = await this.client.scenarios.list(params, options);
1601+
const scenarios: Scenario[] = [];
1602+
1603+
for (const scenario of result.scenarios) {
1604+
scenarios.push(Scenario.fromId(this.client, scenario.id));
1605+
}
1606+
1607+
return scenarios;
1608+
}
1609+
}
1610+
15001611
// @deprecated Use {@link RunloopSDK} instead.
15011612
/**
15021613
* @deprecated Use {@link RunloopSDK} instead.
@@ -1519,13 +1630,15 @@ export declare namespace RunloopSDK {
15191630
AgentOps as AgentOps,
15201631
ScorerOps as ScorerOps,
15211632
NetworkPolicyOps as NetworkPolicyOps,
1633+
ScenarioOps as ScenarioOps,
15221634
Devbox as Devbox,
15231635
Blueprint as Blueprint,
15241636
Snapshot as Snapshot,
15251637
StorageObject as StorageObject,
15261638
Agent as Agent,
15271639
Scorer as Scorer,
15281640
NetworkPolicy as NetworkPolicy,
1641+
Scenario as Scenario,
15291642
};
15301643
}
15311644
// Export SDK classes from sdk/sdk.ts - these are separate from RunloopSDK to avoid circular dependencies
@@ -1543,4 +1656,6 @@ export {
15431656
NetworkPolicy,
15441657
Execution,
15451658
ExecutionResult,
1659+
Scenario,
1660+
ScenarioRun,
15461661
} from './sdk/index';

src/sdk/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export { ExecutionResult } from './execution-result';
88
export { Scorer } from './scorer';
99
export { NetworkPolicy } from './network-policy';
1010
export { ScenarioRun } from './scenario-run';
11+
export { Scenario, type ScenarioRunParams } from './scenario';

src/sdk/scenario-run.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,23 @@ import * as path from 'path';
2727
* import { RunloopSDK } from '@runloop/api-client';
2828
*
2929
* const runloop = new RunloopSDK();
30-
* const scenario = runloop.scenario.fromId('scenario-123');
31-
* const run = await scenario.run({ run_name: 'my-run' });
30+
* const scenario = runloop.scenario.fromId('scn_123');
3231
*
33-
* // Access the devbox and execute your agent to solve the scenario
34-
* const devbox = run.devbox;
35-
* await devbox.cmd.exec('python /home/user/agent/main.py');
32+
* // Start a run with agent mounted
33+
* const run = await scenario.run({
34+
* run_name: 'my-run',
35+
* runProfile: {
36+
* mounts: [{
37+
* type: 'agent_mount',
38+
* agent_id: 'agt_123',
39+
* agent_name: null,
40+
* agent_path: '/home/user/agent',
41+
* }],
42+
* },
43+
* });
44+
*
45+
* // Execute your agent on the devbox
46+
* await run.devbox.cmd.exec('python /home/user/agent/main.py');
3647
*
3748
* // Score and complete the run
3849
* await run.scoreAndComplete();

0 commit comments

Comments
 (0)