Skip to content
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
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "1.0.0"
".": "1.1.0"
}
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 1.1.0 (2025-12-06)

Full Changelog: [v1.0.0...v1.1.0](https://github.com/runloopai/api-client-python/compare/v1.0.0...v1.1.0)

### Features

* **scenarios:** added scenario class to sdk ([#701](https://github.com/runloopai/api-client-python/issues/701)) ([121f0fc](https://github.com/runloopai/api-client-python/commit/121f0fc6efa779979f664a75e91d60c068358e21))
* **SDK:** Build context helpers for the python clients and SDKs ([#684](https://github.com/runloopai/api-client-python/issues/684)) ([42849d6](https://github.com/runloopai/api-client-python/commit/42849d6834098d00234e5ab345ed0d91bac00435))


### Chores

* **smoketests:** fixed smoketests and cleaned up typing/formatting ([#699](https://github.com/runloopai/api-client-python/issues/699)) ([f57aae7](https://github.com/runloopai/api-client-python/commit/f57aae71be8eb801214c012b2ec66b9414f0365e))

## 1.0.0 (2025-12-02)

Full Changelog: [v0.69.0...v1.0.0](https://github.com/runloopai/api-client-python/compare/v0.69.0...v1.0.0)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "runloop_api_client"
version = "1.0.0"
version = "1.1.0"
description = "The official Python library for the runloop API"
dynamic = ["readme"]
license = "MIT"
Expand Down
2 changes: 1 addition & 1 deletion src/runloop_api_client/_version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.

__title__ = "runloop_api_client"
__version__ = "1.0.0" # x-release-please-version
__version__ = "1.1.0" # x-release-please-version
13 changes: 12 additions & 1 deletion src/runloop_api_client/sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,35 @@

from __future__ import annotations

from .sync import AgentOps, DevboxOps, ScorerOps, RunloopSDK, SnapshotOps, BlueprintOps, StorageObjectOps
from .sync import AgentOps, DevboxOps, ScorerOps, RunloopSDK, ScenarioOps, SnapshotOps, BlueprintOps, StorageObjectOps
from .agent import Agent
from .async_ import (
AsyncAgentOps,
AsyncDevboxOps,
AsyncScorerOps,
AsyncRunloopSDK,
AsyncScenarioOps,
AsyncSnapshotOps,
AsyncBlueprintOps,
AsyncStorageObjectOps,
)
from .devbox import Devbox, NamedShell
from .scorer import Scorer
from .scenario import Scenario
from .snapshot import Snapshot
from .blueprint import Blueprint
from .execution import Execution
from .async_agent import AsyncAgent
from .async_devbox import AsyncDevbox, AsyncNamedShell
from .async_scorer import AsyncScorer
from .scenario_run import ScenarioRun
from .async_scenario import AsyncScenario
from .async_snapshot import AsyncSnapshot
from .storage_object import StorageObject
from .async_blueprint import AsyncBlueprint
from .async_execution import AsyncExecution
from .execution_result import ExecutionResult
from .async_scenario_run import AsyncScenarioRun
from .async_storage_object import AsyncStorageObject
from .async_execution_result import AsyncExecutionResult

Expand All @@ -43,6 +48,8 @@
"AsyncDevboxOps",
"BlueprintOps",
"AsyncBlueprintOps",
"ScenarioOps",
"AsyncScenarioOps",
"ScorerOps",
"AsyncScorerOps",
"SnapshotOps",
Expand All @@ -60,6 +67,10 @@
"AsyncExecutionResult",
"Blueprint",
"AsyncBlueprint",
"Scenario",
"AsyncScenario",
"ScenarioRun",
"AsyncScenarioRun",
"Scorer",
"AsyncScorer",
"Snapshot",
Expand Down
19 changes: 19 additions & 0 deletions src/runloop_api_client/sdk/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@
from ..types.agent_create_params import AgentCreateParams
from ..types.devbox_create_params import DevboxCreateParams, DevboxBaseCreateParams
from ..types.object_create_params import ObjectCreateParams
from ..types.scenario_list_params import ScenarioListParams
from ..types.blueprint_list_params import BlueprintListParams
from ..types.object_download_params import ObjectDownloadParams
from ..types.scenario_update_params import ScenarioUpdateParams
from ..types.blueprint_create_params import BlueprintCreateParams
from ..types.devbox_upload_file_params import DevboxUploadFileParams
from ..types.scenario_start_run_params import ScenarioStartRunBaseParams
from ..types.devbox_create_tunnel_params import DevboxCreateTunnelParams
from ..types.devbox_download_file_params import DevboxDownloadFileParams
from ..types.devbox_execute_async_params import DevboxNiceExecuteAsyncParams
Expand Down Expand Up @@ -167,3 +170,19 @@ class SDKAgentCreateParams(AgentCreateParams, LongRequestOptions):

class SDKAgentListParams(AgentListParams, BaseRequestOptions):
pass


class SDKScenarioListParams(ScenarioListParams, BaseRequestOptions):
pass


class SDKScenarioUpdateParams(ScenarioUpdateParams, LongRequestOptions):
pass


class SDKScenarioRunAsyncParams(ScenarioStartRunBaseParams, LongRequestOptions):
pass


class SDKScenarioRunParams(ScenarioStartRunBaseParams, LongPollingRequestOptions):
pass
5 changes: 1 addition & 4 deletions src/runloop_api_client/sdk/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ class Agent:
including retrieving agent information.

Example:
>>> agent = runloop.agent.create_from_npm(
... name="my-agent",
... package_name="@runloop/example-agent"
... )
>>> agent = runloop.agent.create_from_npm(name="my-agent", package_name="@runloop/example-agent")
>>> info = agent.get_info()
>>> print(info.name)
"""
Expand Down
78 changes: 63 additions & 15 deletions src/runloop_api_client/sdk/async_.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
SDKAgentCreateParams,
SDKDevboxCreateParams,
SDKObjectCreateParams,
SDKScenarioListParams,
SDKScorerCreateParams,
SDKBlueprintListParams,
SDKBlueprintCreateParams,
Expand All @@ -31,6 +32,7 @@
from .async_agent import AsyncAgent
from .async_devbox import AsyncDevbox
from .async_scorer import AsyncScorer
from .async_scenario import AsyncScenario
from .async_snapshot import AsyncSnapshot
from .async_blueprint import AsyncBlueprint
from ..lib.context_loader import TarFilter, build_directory_tar
Expand Down Expand Up @@ -221,17 +223,16 @@ class AsyncBlueprintOps:
... dockerfile="FROM ubuntu:22.04\\nRUN apt-get update",
... )
>>> blueprints = await runloop.blueprint.list()

To use a local directory as a build context, use an object.

Example:
>>> from datetime import timedelta
>>> from runloop_api_client.types.blueprint_build_parameters import BuildContext
>>>
>>> runloop = AsyncRunloopSDK()
>>> obj = await runloop.object_storage.upload_from_dir(
... "./",
... ttl=timedelta(hours=1),
... ttl=timedelta(hours=1),
... )
>>> blueprint = await runloop.blueprint.create(
... name="my-blueprint",
Expand Down Expand Up @@ -549,7 +550,8 @@ async def list(self, **params: Unpack[SDKScorerListParams]) -> list[AsyncScorer]
"""
page = await self._client.scenarios.scorers.list(**params)
return [AsyncScorer(self._client, item.id) async for item in page]



class AsyncAgentOps:
"""High-level async manager for creating and managing agents.

Expand All @@ -559,15 +561,10 @@ class AsyncAgentOps:
Example:
>>> runloop = AsyncRunloopSDK()
>>> # Create agent from NPM package
>>> agent = await runloop.agent.create_from_npm(
... name="my-agent",
... package_name="@runloop/example-agent"
... )
>>> agent = await runloop.agent.create_from_npm(name="my-agent", package_name="@runloop/example-agent")
>>> # Create agent from Git repository
>>> agent = await runloop.agent.create_from_git(
... name="git-agent",
... repository="https://github.com/user/agent-repo",
... ref="main"
... name="git-agent", repository="https://github.com/user/agent-repo", ref="main"
... )
>>> # List all agents
>>> agents = await runloop.agent.list(limit=10)
Expand Down Expand Up @@ -621,7 +618,9 @@ async def create_from_npm(
:raises ValueError: If 'source' is provided in params
"""
if "source" in params:
raise ValueError("Cannot specify 'source' when using create_from_npm(); source is automatically set to npm configuration")
raise ValueError(
"Cannot specify 'source' when using create_from_npm(); source is automatically set to npm configuration"
)

npm_config: dict = {"package_name": package_name}
if npm_version is not None:
Expand Down Expand Up @@ -661,7 +660,9 @@ async def create_from_pip(
:raises ValueError: If 'source' is provided in params
"""
if "source" in params:
raise ValueError("Cannot specify 'source' when using create_from_pip(); source is automatically set to pip configuration")
raise ValueError(
"Cannot specify 'source' when using create_from_pip(); source is automatically set to pip configuration"
)

pip_config: dict = {"package_name": package_name}
if pip_version is not None:
Expand Down Expand Up @@ -698,7 +699,9 @@ async def create_from_git(
:raises ValueError: If 'source' is provided in params
"""
if "source" in params:
raise ValueError("Cannot specify 'source' when using create_from_git(); source is automatically set to git configuration")
raise ValueError(
"Cannot specify 'source' when using create_from_git(); source is automatically set to git configuration"
)

git_config: dict = {"repository": repository}
if ref is not None:
Expand Down Expand Up @@ -730,7 +733,9 @@ async def create_from_object(
:raises ValueError: If 'source' is provided in params
"""
if "source" in params:
raise ValueError("Cannot specify 'source' when using create_from_object(); source is automatically set to object configuration")
raise ValueError(
"Cannot specify 'source' when using create_from_object(); source is automatically set to object configuration"
)

object_config: dict = {"object_id": object_id}
if agent_setup is not None:
Expand Down Expand Up @@ -767,6 +772,45 @@ async def list(
return [AsyncAgent(self._client, item.id, item) for item in page.agents]


class AsyncScenarioOps:
"""Manage scenarios (async). Access via ``runloop.scenario``.

Example:
>>> runloop = AsyncRunloopSDK()
>>> scenario = runloop.scenario.from_id("scn-xxx")
>>> run = await scenario.run()
>>> scenarios = await runloop.scenario.list()
"""

def __init__(self, client: AsyncRunloop) -> None:
"""Initialize AsyncScenarioOps.

:param client: AsyncRunloop client instance
:type client: AsyncRunloop
"""
self._client = client

def from_id(self, scenario_id: str) -> AsyncScenario:
"""Get an AsyncScenario instance for an existing scenario ID.

:param scenario_id: ID of the scenario
:type scenario_id: str
:return: AsyncScenario instance for the given ID
:rtype: AsyncScenario
"""
return AsyncScenario(self._client, scenario_id)

async def list(self, **params: Unpack[SDKScenarioListParams]) -> list[AsyncScenario]:
"""List all scenarios, optionally filtered by parameters.

:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKScenarioListParams` for available parameters
:return: List of scenarios
:rtype: list[AsyncScenario]
"""
page = await self._client.scenarios.list(**params)
return [AsyncScenario(self._client, item.id) async for item in page]


class AsyncRunloopSDK:
"""High-level asynchronous entry point for the Runloop SDK.

Expand All @@ -782,6 +826,8 @@ class AsyncRunloopSDK:
:vartype devbox: AsyncDevboxOps
:ivar blueprint: High-level async interface for blueprint management
:vartype blueprint: AsyncBlueprintOps
:ivar scenario: High-level async interface for scenario management
:vartype scenario: AsyncScenarioOps
:ivar scorer: High-level async interface for scorer management
:vartype scorer: AsyncScorerOps
:ivar snapshot: High-level async interface for snapshot management
Expand All @@ -801,6 +847,7 @@ class AsyncRunloopSDK:
agent: AsyncAgentOps
devbox: AsyncDevboxOps
blueprint: AsyncBlueprintOps
scenario: AsyncScenarioOps
scorer: AsyncScorerOps
snapshot: AsyncSnapshotOps
storage_object: AsyncStorageObjectOps
Expand Down Expand Up @@ -846,6 +893,7 @@ def __init__(
self.agent = AsyncAgentOps(self.api)
self.devbox = AsyncDevboxOps(self.api)
self.blueprint = AsyncBlueprintOps(self.api)
self.scenario = AsyncScenarioOps(self.api)
self.scorer = AsyncScorerOps(self.api)
self.snapshot = AsyncSnapshotOps(self.api)
self.storage_object = AsyncStorageObjectOps(self.api)
Expand Down
5 changes: 1 addition & 4 deletions src/runloop_api_client/sdk/async_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,7 @@ class AsyncAgent:
including retrieving agent information.

Example:
>>> agent = await runloop.agent.create_from_npm(
... name="my-agent",
... package_name="@runloop/example-agent"
... )
>>> agent = await runloop.agent.create_from_npm(name="my-agent", package_name="@runloop/example-agent")
>>> info = await agent.get_info()
>>> print(info.name)
"""
Expand Down
2 changes: 0 additions & 2 deletions src/runloop_api_client/sdk/async_devbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,6 @@ async def exec(
>>> result = await shell.exec("npm install", stdout=lambda line: print(f"[LOG] {line}"))
"""
# Ensure shell_name is set and cannot be overridden by user params
params = dict(params)
params["shell_name"] = self._shell_name
return await self._devbox.cmd.exec(command, **params)

Expand Down Expand Up @@ -677,7 +676,6 @@ async def exec_async(
... print("Task completed successfully!")
"""
# Ensure shell_name is set and cannot be overridden by user params
params = dict(params)
params["shell_name"] = self._shell_name
return await self._devbox.cmd.exec_async(command, **params)

Expand Down
Loading