feat(sdk): regenerate controlplane client with externalId support (ENG-3141)#171
Conversation
…G-3141) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
✅ Linked to Linear issue ENG-3141 — status already In Progress, assigned to Charles Drappier. Note Posted by Linear Issue Enforcer · Tag @mendral-app with feedback. |
…olume wrappers Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Good catch — fixed in f5add38. The |
🧪 Testing GuideWhat this PR addressesThis PR regenerates the controlplane Python SDK client from an updated OpenAPI spec (from controlplane PR #4456). Key changes include:
Steps to exercise the new behaviorSince this is auto-generated client code, focus on:
What to verify (expected behavior)
Note Posted by PR Testing Guide · Tag @mendral-app with feedback. |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
| volumes = response.data if hasattr(response, "data") else response | ||
| return [cls(volume) for volume in volumes or []] |
There was a problem hiding this comment.
🔴 VolumeInstance.list() wraps LiteVolume objects in VolumeInstance, causing AttributeError on property access
The list_volumes API now returns VolumeList whose .data field contains LiteVolume objects (src/blaxel/core/client/models/volume_list.py:27), but both VolumeInstance.__init__ and SyncVolumeInstance.__init__ expect full Volume objects (src/blaxel/core/volume/volume.py:143). LiteVolume uses LiteVolumeSpec (only has region and size) instead of VolumeSpec (has region, size, template, infrastructure_id), and LiteVolumeMetadata (only name, display_name, created_at, updated_at) instead of Metadata (has labels, workspace, url, etc.).
Accessing VolumeInstance.template on items from .list() will crash with AttributeError because LiteVolumeSpec has no template attribute (src/blaxel/core/volume/volume.py:176). Similarly, accessing .metadata.labels (used in test cleanup at tests/integration/core/conftest.py:47) will crash because LiteVolumeMetadata has no labels.
Prompt for agents
The root problem is that VolumeList.data contains LiteVolume objects but VolumeInstance and SyncVolumeInstance expect full Volume objects. LiteVolume is missing key attributes that VolumeInstance exposes (template, labels, etc.).
Possible approaches:
1. In VolumeInstance.list() and SyncVolumeInstance.list(), do NOT unwrap response.data directly. Instead, for each LiteVolume, call VolumeInstance.get(name) to fetch the full Volume. This is expensive but correct.
2. Change VolumeInstance to accept either Volume or LiteVolume, and make the .template property gracefully handle the missing attribute (e.g., return None or UNSET when the underlying object is a LiteVolume).
3. Change the generated VolumeList model to use Volume instead of LiteVolume (requires changing the OpenAPI spec and regenerating).
Option 2 is probably the best balance: update VolumeInstance.__init__ to accept Union[Volume, LiteVolume], and guard property accessors with hasattr checks or try/except. The same fix is needed in SyncVolumeInstance at line 414.
Was this helpful? React with 👍 or 👎 to provide feedback.
| response = list_volumes_sync(client=client) | ||
| return [cls(volume) for volume in response or []] | ||
| volumes = response.data if hasattr(response, "data") else response | ||
| return [cls(volume) for volume in volumes or []] |
There was a problem hiding this comment.
🔴 SyncVolumeInstance.list() has the same LiteVolume type mismatch
Same issue as the async VolumeInstance.list() — SyncVolumeInstance.list() also wraps LiteVolume objects (from VolumeList.data) into SyncVolumeInstance which expects Volume. Accessing .template or .metadata.labels on returned instances will crash with AttributeError.
Was this helpful? React with 👍 or 👎 to provide feedback.
| if response.status_code == 200: | ||
| response_200 = [] | ||
| _response_200 = response.json() | ||
| for response_200_item_data in _response_200: | ||
| response_200_item = Sandbox.from_dict(response_200_item_data) | ||
|
|
||
| response_200.append(response_200_item) | ||
| response_200 = SandboxList.from_dict(response.json()) | ||
|
|
There was a problem hiding this comment.
🚩 Backward compatibility concern: SandboxList.from_dict crashes on bare arrays from older API versions
The generated _parse_response at src/blaxel/core/client/api/compute/list_sandboxes.py:64 now calls SandboxList.from_dict(response.json()) for all 200 responses. The docstring states older API versions return a bare array, but SandboxList.from_dict (src/blaxel/core/client/models/sandbox_list.py:60-88) expects a dict. If response.json() returns a list [...], the call d = src_dict.copy() succeeds (lists have .copy()), but then d.pop("data", UNSET) will raise TypeError because list.pop() takes an integer index, not a string key. This same pattern affects all list endpoints (agents, drives, functions, jobs, models, policies, volumes). The wrapper code's hasattr(response, "data") fallback won't help because the crash happens inside the generated parser before the wrapper is reached. This means the SDK is incompatible with older API servers that return bare arrays.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Devin is archived and cannot be woken up. Please unarchive Devin if you want to continue using it. |
Summary
Regenerates the controlplane API client from the OpenAPI spec on branch
cdrappier/devin/externalid-all-resources-step2(controlplane PR #4456).Key additions from the new spec:
external_idfield onMetadatamodel (max 64 chars, alphanumeric + dash)GET /sandboxes/by-external-id/{externalId}endpoint →get_sandbox_by_external_idexternal_idquery parameter onlist_sandboxesDepends on: controlplane PR #4456 being merged first.
No manual edits — all files are auto-generated via
openapi-python-client.Link to Devin session: https://app.devin.ai/sessions/18b29621f60f4e459f119aba6516ba6b
Requested by: @drappier-charles
Note
Regenerates the controlplane API client adding
externalIdsupport (new field on Metadata, newGET /sandboxes/by-external-id/{externalId}endpoint, cursor pagination for list endpoints). Also fixes the runtime breakage from the previous review by unwrapping paginated responses with.datain sandbox, drive, and volume wrappers. Adds integration tests for the externalId feature.Written by Mendral for commit 3249294.