-
Notifications
You must be signed in to change notification settings - Fork 1
Add agents to the python SDK #683
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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, | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,8 +10,10 @@ | |
|
|
||
| from ._types import ( | ||
| LongRequestOptions, | ||
| SDKAgentListParams, | ||
| SDKDevboxListParams, | ||
| SDKObjectListParams, | ||
| SDKAgentCreateParams, | ||
| SDKDevboxCreateParams, | ||
| SDKObjectCreateParams, | ||
| SDKBlueprintListParams, | ||
|
|
@@ -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 | ||
|
|
@@ -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: | ||
| """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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
|
||
|
|
@@ -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 | ||
|
|
@@ -442,6 +508,7 @@ class AsyncRunloopSDK: | |
| """ | ||
|
|
||
| api: AsyncRunloop | ||
| agent: AsyncAgentOps | ||
| devbox: AsyncDevboxOps | ||
| blueprint: AsyncBlueprintOps | ||
| snapshot: AsyncSnapshotOps | ||
|
|
@@ -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) | ||
|
|
||
| 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
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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...) |
||
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.