Skip to content
Open
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
9 changes: 8 additions & 1 deletion src/runloop_api_client/sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

from __future__ import annotations

from .sync import DevboxOps, RunloopSDK, SnapshotOps, BlueprintOps, StorageObjectOps
from .sync import AgentOps, DevboxOps, RunloopSDK, SnapshotOps, BlueprintOps, StorageObjectOps
from .agent import Agent
from .async_ import (
AsyncAgentOps,
AsyncDevboxOps,
AsyncRunloopSDK,
AsyncSnapshotOps,
Expand All @@ -17,6 +19,7 @@
from .snapshot import Snapshot
from .blueprint import Blueprint
from .execution import Execution
from .async_agent import AsyncAgent
from .async_devbox import AsyncDevbox
from .async_snapshot import AsyncSnapshot
from .storage_object import StorageObject
Expand All @@ -31,6 +34,8 @@
"RunloopSDK",
"AsyncRunloopSDK",
# Management interfaces
"AgentOps",
"AsyncAgentOps",
"DevboxOps",
"AsyncDevboxOps",
"BlueprintOps",
Expand All @@ -40,6 +45,8 @@
"StorageObjectOps",
"AsyncStorageObjectOps",
# Resource classes
"Agent",
"AsyncAgent",
"Devbox",
"AsyncDevbox",
"Execution",
Expand Down
10 changes: 10 additions & 0 deletions src/runloop_api_client/sdk/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

from .._types import Body, Query, Headers, Timeout, NotGiven
from ..lib.polling import PollingConfig
from ..types.agent_list_params import AgentListParams
from ..types.devbox_list_params import DevboxListParams
from ..types.object_list_params import ObjectListParams
from ..types.agent_create_params import AgentCreateParams
from ..types.devbox_create_params import DevboxCreateParams, DevboxBaseCreateParams
from ..types.object_create_params import ObjectCreateParams
from ..types.blueprint_list_params import BlueprintListParams
Expand Down Expand Up @@ -142,3 +144,11 @@ class SDKObjectCreateParams(ObjectCreateParams, LongRequestOptions):

class SDKObjectDownloadParams(ObjectDownloadParams, RequestOptions):
pass


class SDKAgentCreateParams(AgentCreateParams, LongRequestOptions):
pass


class SDKAgentListParams(AgentListParams, RequestOptions):
pass
58 changes: 58 additions & 0 deletions src/runloop_api_client/sdk/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Agent resource class for synchronous operations."""

from __future__ import annotations

from typing_extensions import Unpack, override

from ._types import (
RequestOptions,
)
from .._client import Runloop
from ..types.agent_view import AgentView


class Agent:
"""Wrapper around synchronous agent operations."""

def __init__(
self,
client: Runloop,
agent_id: str,
) -> None:
"""Initialize the wrapper.

:param client: Generated Runloop client
:type client: Runloop
:param agent_id: Agent identifier returned by the API
:type agent_id: str
"""
self._client = client
self._id = agent_id

@override
def __repr__(self) -> str:
return f"<Agent id={self._id!r}>"

@property
def id(self) -> str:
"""Return the agent identifier.

:return: Unique agent ID
:rtype: str
"""
return self._id

def get_info(
self,
**options: Unpack[RequestOptions],
) -> AgentView:
"""Retrieve the latest agent information.

:param options: Optional request configuration
:return: Agent details
:rtype: AgentView
"""
return self._client.agents.retrieve(
self._id,
**options,
)
68 changes: 68 additions & 0 deletions src/runloop_api_client/sdk/async_.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@

from ._types import (
LongRequestOptions,
SDKAgentListParams,
SDKDevboxListParams,
SDKObjectListParams,
SDKAgentCreateParams,
SDKDevboxCreateParams,
SDKObjectCreateParams,
SDKBlueprintListParams,
Expand All @@ -22,6 +24,7 @@
from .._types import Timeout, NotGiven, not_given
from .._client import DEFAULT_MAX_RETRIES, AsyncRunloop
from ._helpers import detect_content_type
from .async_agent import AsyncAgent
from .async_devbox import AsyncDevbox
from .async_snapshot import AsyncSnapshot
from .async_blueprint import AsyncBlueprint
Expand Down Expand Up @@ -415,6 +418,67 @@ async def upload_from_bytes(
return obj


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

Accessed via ``runloop.agent`` from :class:`AsyncRunloopSDK`, provides
coroutines to create, retrieve, and list agents.

Example:
>>> runloop = AsyncRunloopSDK()
>>> agent = await runloop.agent.create(name="my-agent")
>>> agents = await runloop.agent.list(limit=10)
"""

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

:param client: Generated AsyncRunloop client to wrap
:type client: AsyncRunloop
"""
self._client = client

async def create(
self,
**params: Unpack[SDKAgentCreateParams],
) -> AsyncAgent:
"""Create a new agent.

:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKAgentCreateParams` for available parameters
:return: Wrapper bound to the newly created agent
:rtype: AsyncAgent
"""
agent_view = await self._client.agents.create(
**params,
)
return AsyncAgent(self._client, agent_view.id)

def from_id(self, agent_id: str) -> AsyncAgent:
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably a missing thing in API but I think we probably want name in here as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what is missing in the API?

In any case, a synchronous (and fully client-side) from_id() like this fits with BlueprintOps.from_id() and such, so this seems reasonable to pair with the AsyncAgent.get_info() call.

"""Attach to an existing agent by ID.

:param agent_id: Existing agent ID
:type agent_id: str
:return: Wrapper bound to the requested agent
:rtype: AsyncAgent
"""
return AsyncAgent(self._client, agent_id)

async def list(
self,
**params: Unpack[SDKAgentListParams],
) -> list[AsyncAgent]:
"""List agents accessible to the caller.

:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKAgentListParams` for available parameters
:return: Collection of agent wrappers
:rtype: list[AsyncAgent]
"""
page = await self._client.agents.list(
**params,
)
Comment on lines +476 to +478
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: does the list() call return just the IDs, or the ID + additional info?

return [AsyncAgent(self._client, item.id) for item in page.agents]


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

Expand All @@ -424,6 +488,8 @@ class AsyncRunloopSDK:

:ivar api: Direct access to the generated async REST API client
:vartype api: AsyncRunloop
:ivar agent: High-level async interface for agent management
:vartype agent: AsyncAgentOps
:ivar devbox: High-level async interface for devbox management
:vartype devbox: AsyncDevboxOps
:ivar blueprint: High-level async interface for blueprint management
Expand All @@ -442,6 +508,7 @@ class AsyncRunloopSDK:
"""

api: AsyncRunloop
agent: AsyncAgentOps
devbox: AsyncDevboxOps
blueprint: AsyncBlueprintOps
snapshot: AsyncSnapshotOps
Expand Down Expand Up @@ -485,6 +552,7 @@ def __init__(
http_client=http_client,
)

self.agent = AsyncAgentOps(self.api)
self.devbox = AsyncDevboxOps(self.api)
self.blueprint = AsyncBlueprintOps(self.api)
self.snapshot = AsyncSnapshotOps(self.api)
Expand Down
58 changes: 58 additions & 0 deletions src/runloop_api_client/sdk/async_agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Agent resource class for asynchronous operations."""

from __future__ import annotations

from typing_extensions import Unpack, override

from ._types import (
RequestOptions,
)
from .._client import AsyncRunloop
from ..types.agent_view import AgentView


class AsyncAgent:
"""Async wrapper around agent operations."""

def __init__(
self,
client: AsyncRunloop,
agent_id: str,
) -> None:
"""Initialize the wrapper.
:param client: Generated AsyncRunloop client
:type client: AsyncRunloop
:param agent_id: Agent identifier returned by the API
:type agent_id: str
"""
self._client = client
self._id = agent_id

@override
def __repr__(self) -> str:
return f"<AsyncAgent id={self._id!r}>"

@property
def id(self) -> str:
"""Return the agent identifier.
:return: Unique agent ID
:rtype: str
"""
return self._id

async def get_info(
self,
**options: Unpack[RequestOptions],
) -> AgentView:
"""Retrieve the latest agent information.
:param options: Optional request configuration
:return: Agent details
:rtype: AgentView
"""
return await self._client.agents.retrieve(
self._id,
**options,
)
Comment on lines +55 to +58
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if we should cache the json response, so subsequent calls can return immediately? Also, if the list() call returns details instead of just IDs, we would want to return the deets we already have here.

One question on all of this, though: are agent objects on the server mutable? If so, then sticking with the "re-fetch every call" strategy may be better for now, since that way we don't need to worry about the local and server versions going out of sync. (Well, the user would need to worry about this in their code, but we would make no promises about it at this level...)

68 changes: 68 additions & 0 deletions src/runloop_api_client/sdk/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@

import httpx

from .agent import Agent
from ._types import (
LongRequestOptions,
SDKAgentListParams,
SDKDevboxListParams,
SDKObjectListParams,
SDKAgentCreateParams,
SDKDevboxCreateParams,
SDKObjectCreateParams,
SDKBlueprintListParams,
Expand Down Expand Up @@ -415,6 +418,67 @@ def upload_from_bytes(
return obj


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

Accessed via ``runloop.agent`` from :class:`RunloopSDK`, provides methods to
create, retrieve, and list agents.

Example:
>>> runloop = RunloopSDK()
>>> agent = runloop.agent.create(name="my-agent")
>>> agents = runloop.agent.list(limit=10)
"""

def __init__(self, client: Runloop) -> None:
"""Initialize the manager.

:param client: Generated Runloop client to wrap
:type client: Runloop
"""
self._client = client

def create(
self,
**params: Unpack[SDKAgentCreateParams],
) -> Agent:
"""Create a new agent.

:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKAgentCreateParams` for available parameters
:return: Wrapper bound to the newly created agent
:rtype: Agent
"""
agent_view = self._client.agents.create(
**params,
)
return Agent(self._client, agent_view.id)

def from_id(self, agent_id: str) -> Agent:
"""Attach to an existing agent by ID.

:param agent_id: Existing agent ID
:type agent_id: str
:return: Wrapper bound to the requested agent
:rtype: Agent
"""
return Agent(self._client, agent_id)

def list(
self,
**params: Unpack[SDKAgentListParams],
) -> list[Agent]:
"""List agents accessible to the caller.

:param params: See :typeddict:`~runloop_api_client.sdk._types.SDKAgentListParams` for available parameters
:return: Collection of agent wrappers
:rtype: list[Agent]
"""
page = self._client.agents.list(
**params,
)
return [Agent(self._client, item.id) for item in page.agents]


class RunloopSDK:
"""High-level synchronous entry point for the Runloop SDK.

Expand All @@ -424,6 +488,8 @@ class RunloopSDK:

:ivar api: Direct access to the generated REST API client
:vartype api: Runloop
:ivar agent: High-level interface for agent management
:vartype agent: AgentOps
:ivar devbox: High-level interface for devbox management
:vartype devbox: DevboxOps
:ivar blueprint: High-level interface for blueprint management
Expand All @@ -442,6 +508,7 @@ class RunloopSDK:
"""

api: Runloop
agent: AgentOps
devbox: DevboxOps
blueprint: BlueprintOps
snapshot: SnapshotOps
Expand Down Expand Up @@ -485,6 +552,7 @@ def __init__(
http_client=http_client,
)

self.agent = AgentOps(self.api)
self.devbox = DevboxOps(self.api)
self.blueprint = BlueprintOps(self.api)
self.snapshot = SnapshotOps(self.api)
Expand Down