airbyte.agents
PyAirbyte classes and methods for the Airbyte Agents platform.
⚠️ Experimental Interface
The Airbyte Agents Python interfaces are experimental. Class names, method signatures, and result models may change or be removed without notice between minor versions of PyAirbyte. Pin an exact PyAirbyte version if you depend on them.
Airbyte Agents connectors expose read and write actions on individual entities, executed
one action at a time, rather than the batch record replication that airbyte.cloud
provides. This module is that interface.
Airbyte Cloud credentials authenticate against the Agents API, so the AIRBYTE_CLOUD_*
variables are reused. Set AIRBYTE_AGENTS_API_URL to override the Agents API root when
connecting through a proxy or local development endpoint.
Usage Examples
Read entities from a connector, paging automatically as you iterate:
from airbyte import agents
workspace = agents.AgentWorkspace.from_env()
connector = workspace.get_connector("GitHub") # by ID or name (case insensitive)
for issue in connector.iter_entities(
"issues",
api_args={"repository": "airbytehq/PyAirbyte"}, # Passthrough API args
):
print(issue["title"])
Fetch a single page instead, when the result's status and metadata are needed:
result = connector.list_entities(
"issues",
api_args={"repository": "airbytehq/PyAirbyte"},
page_size=50,
)
print(result.status, result.has_next_page)
for entity in result.entities:
print(entity["title"])
Pass result.end_cursor back as cursor to page through manually:
cursor = None
while True:
result = connector.list_entities(
"issues",
api_args={"repository": "airbytehq/PyAirbyte"},
cursor=cursor,
)
print(len(result.entities))
if not result.has_next_page:
break
cursor = result.end_cursor
Discover what a connector supports, and what an organization can reach:
organization = agents.AgentOrganization.from_env()
for workspace in organization.list_workspaces():
print(workspace.workspace_id, workspace.name)
print(connector.inspect().integration_name)
Convert between the Cloud and Agents domains:
from airbyte.cloud import CloudWorkspace
cloud_workspace = CloudWorkspace.from_env()
agent_workspace = agents.AgentWorkspace.from_cloud_workspace(cloud_workspace)
back_to_cloud = agent_workspace.as_cloud_workspace()
1# Copyright (c) 2026 Airbyte, Inc., all rights reserved. 2"""PyAirbyte classes and methods for the Airbyte Agents platform. 3 4> ## ⚠️ Experimental Interface 5> 6> **The Airbyte Agents Python interfaces are experimental.** Class names, method signatures, 7> and result models may change or be removed without notice between minor versions of 8> PyAirbyte. Pin an exact PyAirbyte version if you depend on them. 9 10Airbyte Agents connectors expose read and write actions on individual entities, executed 11one action at a time, rather than the batch record replication that `airbyte.cloud` 12provides. This module is that interface. 13 14Airbyte Cloud credentials authenticate against the Agents API, so the `AIRBYTE_CLOUD_*` 15variables are reused. Set `AIRBYTE_AGENTS_API_URL` to override the Agents API root when 16connecting through a proxy or local development endpoint. 17 18## Usage Examples 19 20Read entities from a connector, paging automatically as you iterate: 21 22```python 23from airbyte import agents 24 25workspace = agents.AgentWorkspace.from_env() 26connector = workspace.get_connector("GitHub") # by ID or name (case insensitive) 27 28for issue in connector.iter_entities( 29 "issues", 30 api_args={"repository": "airbytehq/PyAirbyte"}, # Passthrough API args 31): 32 print(issue["title"]) 33``` 34 35Fetch a single page instead, when the result's status and metadata are needed: 36 37```python 38result = connector.list_entities( 39 "issues", 40 api_args={"repository": "airbytehq/PyAirbyte"}, 41 page_size=50, 42) 43print(result.status, result.has_next_page) 44for entity in result.entities: 45 print(entity["title"]) 46``` 47 48Pass `result.end_cursor` back as `cursor` to page through manually: 49 50```python 51cursor = None 52while True: 53 result = connector.list_entities( 54 "issues", 55 api_args={"repository": "airbytehq/PyAirbyte"}, 56 cursor=cursor, 57 ) 58 print(len(result.entities)) 59 if not result.has_next_page: 60 break 61 cursor = result.end_cursor 62``` 63 64Discover what a connector supports, and what an organization can reach: 65 66```python 67organization = agents.AgentOrganization.from_env() 68for workspace in organization.list_workspaces(): 69 print(workspace.workspace_id, workspace.name) 70 71print(connector.inspect().integration_name) 72``` 73 74Convert between the Cloud and Agents domains: 75 76```python 77from airbyte.cloud import CloudWorkspace 78 79cloud_workspace = CloudWorkspace.from_env() 80agent_workspace = agents.AgentWorkspace.from_cloud_workspace(cloud_workspace) 81back_to_cloud = agent_workspace.as_cloud_workspace() 82``` 83""" 84 85from __future__ import annotations 86 87from typing import TYPE_CHECKING 88 89from airbyte.agents.connectors import AgentConnector 90from airbyte.agents.models import ( 91 AgentConnectorDetails, 92 AgentConnectorInfo, 93 AgentConnectorMetadata, 94 AgentContextStoreEntity, 95 AgentContextStoreReadiness, 96 AgentExecuteResult, 97 AgentExecutionMetadata, 98 AgentSkillDocs, 99 AgentSkillInfo, 100 AgentSkillList, 101 AgentSkillSection, 102 AgentWorkspaceInfo, 103) 104from airbyte.agents.organizations import AgentOrganization 105from airbyte.agents.skills import AgentSkill 106from airbyte.agents.workspaces import AgentWorkspace 107 108 109# Submodules imported here for documentation reasons: https://github.com/mitmproxy/pdoc/issues/757 110if TYPE_CHECKING: 111 # ruff: noqa: TC004 112 from airbyte.agents import ( 113 connectors, 114 models, 115 organizations, 116 skills, 117 workspaces, 118 ) 119 120 121__all__ = [ 122 # Submodules 123 "connectors", 124 "models", 125 "organizations", 126 "skills", 127 "workspaces", 128 # Classes 129 "AgentConnector", 130 "AgentConnectorDetails", 131 "AgentConnectorInfo", 132 "AgentConnectorMetadata", 133 "AgentContextStoreEntity", 134 "AgentContextStoreReadiness", 135 "AgentExecuteResult", 136 "AgentExecutionMetadata", 137 "AgentOrganization", 138 "AgentSkill", 139 "AgentSkillDocs", 140 "AgentSkillInfo", 141 "AgentSkillList", 142 "AgentSkillSection", 143 "AgentWorkspace", 144 "AgentWorkspaceInfo", 145]
152class AgentConnector: 153 """A connector in an Airbyte Agents workspace. 154 155 Get one from `AgentWorkspace.get_connector()` rather than constructing it directly. 156 157 ```python 158 from airbyte import agents 159 160 workspace = agents.AgentWorkspace.from_env() 161 connector = workspace.get_connector("GitHub") # by ID or name (case insensitive) 162 result = connector.list_entities("issues", api_args={"repository": "airbytehq/PyAirbyte"}) 163 for entity in result.entities: 164 print(entity["title"]) 165 ``` 166 """ 167 168 def __init__( 169 self, 170 connector_id: str, 171 *, 172 credentials: _AirbyteCredentials, 173 name: str | None = None, 174 workspace_id: str | None = None, 175 ) -> None: 176 """Initialize an `AgentConnector`. Prefer `AgentWorkspace.get_connector()`.""" 177 self.connector_id = connector_id 178 """The connector ID.""" 179 180 self.workspace_id = workspace_id 181 """The workspace ID.""" 182 183 self._credentials = credentials 184 self._name = name 185 self._details: AgentConnectorDetails | None = None 186 187 @property 188 def name(self) -> str | None: 189 """The connector name, fetched from the Agents API if not already known.""" 190 if self._name is None: 191 self._name = self.inspect().name 192 return self._name 193 194 def inspect(self, *, force_refresh: bool = False) -> AgentConnectorDetails: 195 """Return connector metadata from the Agents API `inspect` endpoint. 196 197 The result is cached; pass `force_refresh=True` to fetch it again. 198 """ 199 if self._details is None or force_refresh: 200 self._details = AgentConnectorDetails.model_validate( 201 _api_util.inspect_agent_connector( 202 connector_id=self.connector_id, 203 credentials=self._credentials, 204 organization_id=self._credentials.organization_id, 205 ) 206 ) 207 self._name = self._details.name or self._name 208 return self._details 209 210 def execute( # noqa: PLR0913 # Explicit args are the point of this public API. 211 self, 212 entity_type: str, 213 action: AgentAction | str, 214 api_args: dict[str, Any] | None = None, 215 *, 216 select_fields: list[str] | None = None, 217 exclude_fields: list[str] | None = None, 218 page_size: int | None = None, 219 cursor: str | None = None, 220 workspace_id: str | None = None, 221 skip_truncation: bool = True, 222 intent: str | None = None, 223 ) -> AgentExecuteResult: 224 """Execute a single action against one entity type on this connector. 225 226 `entity_type` and `action` are connector-specific, for example `issues` and `list`. 227 Use `inspect()` to see what a connector supports. 228 229 `api_args` holds connector-specific arguments passed through to the connector, for 230 example `{"repository": "airbytehq/PyAirbyte"}`. All other arguments are interpreted 231 by PyAirbyte or by the Agents API itself: 232 233 - `select_fields` and `exclude_fields` prune fields from returned entities. 234 - `page_size` and `cursor` are merged into `api_args` as pagination arguments. Context 235 Store `search` uses `limit` and `cursor`, `sql_select` uses the top-level `cursor`, and 236 direct connector actions use their own pagination arguments in `api_args`. Pass the 237 result cursor according to the action type. 238 - `workspace_id` selects the workspace an action runs against. It defaults to the 239 connector's workspace and is currently sent for `sql_select`, the only action the 240 Agents API scopes by workspace; direct connector actions are scoped by the connector. 241 - `skip_truncation` disables the Agents API's default truncation of large payloads. 242 - `intent` is a free-text description of why the action is being run, which some 243 connectors use to refine results. 244 245 The `download` action is rejected, because it returns a binary stream and PyAirbyte 246 does not yet support streaming responses. 247 """ 248 if action in UNSUPPORTED_ACTIONS: 249 raise PyAirbyteInputError( 250 message=f"The {action!r} action is not supported by PyAirbyte.", 251 guidance=( 252 "This action returns a binary stream instead of JSON, and PyAirbyte does " 253 "not yet support streaming responses." 254 ), 255 context={"entity_type": entity_type, "action": action}, 256 ) 257 258 if action not in {*AgentReadAction, *AgentWriteAction}: 259 action_names = ", ".join( 260 member.value for member in (*AgentReadAction, *AgentWriteAction) 261 ) 262 raise PyAirbyteInputError( 263 message=f"The {action!r} action is not a valid action name for `execute`.", 264 guidance=f"Use one of: {action_names}.", 265 context={"entity_type": entity_type, "action": action}, 266 ) 267 268 action_value = action.value if isinstance(action, Enum) else action 269 params = _build_params(api_args=api_args, page_size=page_size, cursor=cursor) 270 if action_value == AgentReadAction.SQL_SELECT.value: 271 resolved_workspace_id = workspace_id or self.workspace_id 272 if ( 273 resolved_workspace_id is not None 274 and params.get("workspace_id") is None 275 and params.get("workspace_name") is None 276 ): 277 params.pop("workspace_name", None) 278 params["workspace_id"] = resolved_workspace_id 279 request_body: dict[str, Any] = { 280 "entity": entity_type, 281 "action": action_value, 282 "params": params, 283 "skip_truncation": skip_truncation, 284 } 285 if select_fields is not None: 286 request_body["select_fields"] = select_fields 287 if exclude_fields is not None: 288 request_body["exclude_fields"] = exclude_fields 289 if intent is not None: 290 request_body["intent"] = intent 291 292 return AgentExecuteResult.model_validate( 293 _api_util.execute_agent_connector_action( 294 connector_id=self.connector_id, 295 request_body=request_body, 296 credentials=self._credentials, 297 organization_id=self._credentials.organization_id, 298 ) 299 ) 300 301 def list_entities( 302 self, 303 entity_type: str, 304 api_args: dict[str, Any] | None = None, 305 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 306 ) -> AgentExecuteResult: 307 """Run the `list` action, which returns a page of entities of `entity_type`.""" 308 return self.execute(entity_type, "list", api_args, **kwargs) 309 310 def iter_entities( 311 self, 312 entity_type: str, 313 api_args: dict[str, Any] | None = None, 314 *, 315 limit: int | None = None, 316 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `list_entities()`. 317 ) -> Iterator[dict[str, Any]]: 318 """Yield entities of `entity_type`, following the connector's pagination cursor. 319 320 This is the pagination-free way to read entities: each page is fetched lazily as 321 the caller iterates, so no cursor bookkeeping is needed. 322 323 ```python 324 for issue in connector.iter_entities("issues", {"repository": "airbytehq/PyAirbyte"}): 325 print(issue["title"]) 326 ``` 327 328 `limit` caps how many entities are yielded in total, which matters for entity types 329 with no natural end. Pass `page_size` to control how many are fetched per request. 330 331 Iteration stops early if the connector reports another page without advancing its 332 cursor, rather than requesting the same page forever. 333 334 Use `list_entities()` instead when a single page is enough, or when the result's 335 `status`, `warning`, or `execution_metadata` are needed. 336 """ 337 cursor: str | None = kwargs.pop("cursor", None) 338 seen_cursors: set[str] = set() 339 yielded = 0 340 341 while True: 342 result = self.list_entities(entity_type, api_args, cursor=cursor, **kwargs) 343 for agent_entity in result.entities: 344 yield agent_entity 345 yielded += 1 346 if limit is not None and yielded >= limit: 347 return 348 349 cursor = result.end_cursor 350 if not result.has_next_page or cursor is None or cursor in seen_cursors: 351 return 352 seen_cursors.add(cursor) 353 354 def search_entities( 355 self, 356 entity_type: str, 357 api_args: dict[str, Any] | None = None, 358 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 359 ) -> AgentExecuteResult: 360 """Run the `search` action, which returns matching entities of `entity_type`.""" 361 return self.execute(entity_type, "search", api_args, **kwargs) 362 363 def get_entity( 364 self, 365 entity_type: str, 366 api_args: dict[str, Any] | None = None, 367 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 368 ) -> AgentExecuteResult: 369 """Run the `get` action, which returns a single entity of `entity_type`.""" 370 return self.execute(entity_type, "get", api_args, **kwargs) 371 372 def create_entity( 373 self, 374 entity_type: str, 375 api_args: dict[str, Any] | None = None, 376 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 377 ) -> AgentExecuteResult: 378 """Run the `create` action, which creates an entity of `entity_type`.""" 379 return self.execute(entity_type, "create", api_args, **kwargs) 380 381 def update_entity( 382 self, 383 entity_type: str, 384 api_args: dict[str, Any] | None = None, 385 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 386 ) -> AgentExecuteResult: 387 """Run the `update` action, which updates an entity of `entity_type`.""" 388 return self.execute(entity_type, "update", api_args, **kwargs) 389 390 def delete_entity( 391 self, 392 entity_type: str, 393 api_args: dict[str, Any] | None = None, 394 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 395 ) -> AgentExecuteResult: 396 """Run the `delete` action, which deletes an entity of `entity_type`.""" 397 return self.execute(entity_type, "delete", api_args, **kwargs)
A connector in an Airbyte Agents workspace.
Get one from AgentWorkspace.get_connector() rather than constructing it directly.
from airbyte import agents
workspace = agents.AgentWorkspace.from_env()
connector = workspace.get_connector("GitHub") # by ID or name (case insensitive)
result = connector.list_entities("issues", api_args={"repository": "airbytehq/PyAirbyte"})
for entity in result.entities:
print(entity["title"])
168 def __init__( 169 self, 170 connector_id: str, 171 *, 172 credentials: _AirbyteCredentials, 173 name: str | None = None, 174 workspace_id: str | None = None, 175 ) -> None: 176 """Initialize an `AgentConnector`. Prefer `AgentWorkspace.get_connector()`.""" 177 self.connector_id = connector_id 178 """The connector ID.""" 179 180 self.workspace_id = workspace_id 181 """The workspace ID.""" 182 183 self._credentials = credentials 184 self._name = name 185 self._details: AgentConnectorDetails | None = None
Initialize an AgentConnector. Prefer AgentWorkspace.get_connector().
187 @property 188 def name(self) -> str | None: 189 """The connector name, fetched from the Agents API if not already known.""" 190 if self._name is None: 191 self._name = self.inspect().name 192 return self._name
The connector name, fetched from the Agents API if not already known.
194 def inspect(self, *, force_refresh: bool = False) -> AgentConnectorDetails: 195 """Return connector metadata from the Agents API `inspect` endpoint. 196 197 The result is cached; pass `force_refresh=True` to fetch it again. 198 """ 199 if self._details is None or force_refresh: 200 self._details = AgentConnectorDetails.model_validate( 201 _api_util.inspect_agent_connector( 202 connector_id=self.connector_id, 203 credentials=self._credentials, 204 organization_id=self._credentials.organization_id, 205 ) 206 ) 207 self._name = self._details.name or self._name 208 return self._details
Return connector metadata from the Agents API inspect endpoint.
The result is cached; pass force_refresh=True to fetch it again.
210 def execute( # noqa: PLR0913 # Explicit args are the point of this public API. 211 self, 212 entity_type: str, 213 action: AgentAction | str, 214 api_args: dict[str, Any] | None = None, 215 *, 216 select_fields: list[str] | None = None, 217 exclude_fields: list[str] | None = None, 218 page_size: int | None = None, 219 cursor: str | None = None, 220 workspace_id: str | None = None, 221 skip_truncation: bool = True, 222 intent: str | None = None, 223 ) -> AgentExecuteResult: 224 """Execute a single action against one entity type on this connector. 225 226 `entity_type` and `action` are connector-specific, for example `issues` and `list`. 227 Use `inspect()` to see what a connector supports. 228 229 `api_args` holds connector-specific arguments passed through to the connector, for 230 example `{"repository": "airbytehq/PyAirbyte"}`. All other arguments are interpreted 231 by PyAirbyte or by the Agents API itself: 232 233 - `select_fields` and `exclude_fields` prune fields from returned entities. 234 - `page_size` and `cursor` are merged into `api_args` as pagination arguments. Context 235 Store `search` uses `limit` and `cursor`, `sql_select` uses the top-level `cursor`, and 236 direct connector actions use their own pagination arguments in `api_args`. Pass the 237 result cursor according to the action type. 238 - `workspace_id` selects the workspace an action runs against. It defaults to the 239 connector's workspace and is currently sent for `sql_select`, the only action the 240 Agents API scopes by workspace; direct connector actions are scoped by the connector. 241 - `skip_truncation` disables the Agents API's default truncation of large payloads. 242 - `intent` is a free-text description of why the action is being run, which some 243 connectors use to refine results. 244 245 The `download` action is rejected, because it returns a binary stream and PyAirbyte 246 does not yet support streaming responses. 247 """ 248 if action in UNSUPPORTED_ACTIONS: 249 raise PyAirbyteInputError( 250 message=f"The {action!r} action is not supported by PyAirbyte.", 251 guidance=( 252 "This action returns a binary stream instead of JSON, and PyAirbyte does " 253 "not yet support streaming responses." 254 ), 255 context={"entity_type": entity_type, "action": action}, 256 ) 257 258 if action not in {*AgentReadAction, *AgentWriteAction}: 259 action_names = ", ".join( 260 member.value for member in (*AgentReadAction, *AgentWriteAction) 261 ) 262 raise PyAirbyteInputError( 263 message=f"The {action!r} action is not a valid action name for `execute`.", 264 guidance=f"Use one of: {action_names}.", 265 context={"entity_type": entity_type, "action": action}, 266 ) 267 268 action_value = action.value if isinstance(action, Enum) else action 269 params = _build_params(api_args=api_args, page_size=page_size, cursor=cursor) 270 if action_value == AgentReadAction.SQL_SELECT.value: 271 resolved_workspace_id = workspace_id or self.workspace_id 272 if ( 273 resolved_workspace_id is not None 274 and params.get("workspace_id") is None 275 and params.get("workspace_name") is None 276 ): 277 params.pop("workspace_name", None) 278 params["workspace_id"] = resolved_workspace_id 279 request_body: dict[str, Any] = { 280 "entity": entity_type, 281 "action": action_value, 282 "params": params, 283 "skip_truncation": skip_truncation, 284 } 285 if select_fields is not None: 286 request_body["select_fields"] = select_fields 287 if exclude_fields is not None: 288 request_body["exclude_fields"] = exclude_fields 289 if intent is not None: 290 request_body["intent"] = intent 291 292 return AgentExecuteResult.model_validate( 293 _api_util.execute_agent_connector_action( 294 connector_id=self.connector_id, 295 request_body=request_body, 296 credentials=self._credentials, 297 organization_id=self._credentials.organization_id, 298 ) 299 )
Execute a single action against one entity type on this connector.
entity_type and action are connector-specific, for example issues and list.
Use inspect() to see what a connector supports.
api_args holds connector-specific arguments passed through to the connector, for
example {"repository": "airbytehq/PyAirbyte"}. All other arguments are interpreted
by PyAirbyte or by the Agents API itself:
select_fieldsandexclude_fieldsprune fields from returned entities.page_sizeandcursorare merged intoapi_argsas pagination arguments. Context Storesearchuseslimitandcursor,sql_selectuses the top-levelcursor, and direct connector actions use their own pagination arguments inapi_args. Pass the result cursor according to the action type.workspace_idselects the workspace an action runs against. It defaults to the connector's workspace and is currently sent forsql_select, the only action the Agents API scopes by workspace; direct connector actions are scoped by the connector.skip_truncationdisables the Agents API's default truncation of large payloads.intentis a free-text description of why the action is being run, which some connectors use to refine results.
The download action is rejected, because it returns a binary stream and PyAirbyte
does not yet support streaming responses.
301 def list_entities( 302 self, 303 entity_type: str, 304 api_args: dict[str, Any] | None = None, 305 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 306 ) -> AgentExecuteResult: 307 """Run the `list` action, which returns a page of entities of `entity_type`.""" 308 return self.execute(entity_type, "list", api_args, **kwargs)
Run the list action, which returns a page of entities of entity_type.
310 def iter_entities( 311 self, 312 entity_type: str, 313 api_args: dict[str, Any] | None = None, 314 *, 315 limit: int | None = None, 316 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `list_entities()`. 317 ) -> Iterator[dict[str, Any]]: 318 """Yield entities of `entity_type`, following the connector's pagination cursor. 319 320 This is the pagination-free way to read entities: each page is fetched lazily as 321 the caller iterates, so no cursor bookkeeping is needed. 322 323 ```python 324 for issue in connector.iter_entities("issues", {"repository": "airbytehq/PyAirbyte"}): 325 print(issue["title"]) 326 ``` 327 328 `limit` caps how many entities are yielded in total, which matters for entity types 329 with no natural end. Pass `page_size` to control how many are fetched per request. 330 331 Iteration stops early if the connector reports another page without advancing its 332 cursor, rather than requesting the same page forever. 333 334 Use `list_entities()` instead when a single page is enough, or when the result's 335 `status`, `warning`, or `execution_metadata` are needed. 336 """ 337 cursor: str | None = kwargs.pop("cursor", None) 338 seen_cursors: set[str] = set() 339 yielded = 0 340 341 while True: 342 result = self.list_entities(entity_type, api_args, cursor=cursor, **kwargs) 343 for agent_entity in result.entities: 344 yield agent_entity 345 yielded += 1 346 if limit is not None and yielded >= limit: 347 return 348 349 cursor = result.end_cursor 350 if not result.has_next_page or cursor is None or cursor in seen_cursors: 351 return 352 seen_cursors.add(cursor)
Yield entities of entity_type, following the connector's pagination cursor.
This is the pagination-free way to read entities: each page is fetched lazily as the caller iterates, so no cursor bookkeeping is needed.
for issue in connector.iter_entities("issues", {"repository": "airbytehq/PyAirbyte"}):
print(issue["title"])
limit caps how many entities are yielded in total, which matters for entity types
with no natural end. Pass page_size to control how many are fetched per request.
Iteration stops early if the connector reports another page without advancing its cursor, rather than requesting the same page forever.
Use list_entities() instead when a single page is enough, or when the result's
status, warning, or execution_metadata are needed.
354 def search_entities( 355 self, 356 entity_type: str, 357 api_args: dict[str, Any] | None = None, 358 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 359 ) -> AgentExecuteResult: 360 """Run the `search` action, which returns matching entities of `entity_type`.""" 361 return self.execute(entity_type, "search", api_args, **kwargs)
Run the search action, which returns matching entities of entity_type.
363 def get_entity( 364 self, 365 entity_type: str, 366 api_args: dict[str, Any] | None = None, 367 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 368 ) -> AgentExecuteResult: 369 """Run the `get` action, which returns a single entity of `entity_type`.""" 370 return self.execute(entity_type, "get", api_args, **kwargs)
Run the get action, which returns a single entity of entity_type.
372 def create_entity( 373 self, 374 entity_type: str, 375 api_args: dict[str, Any] | None = None, 376 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 377 ) -> AgentExecuteResult: 378 """Run the `create` action, which creates an entity of `entity_type`.""" 379 return self.execute(entity_type, "create", api_args, **kwargs)
Run the create action, which creates an entity of entity_type.
381 def update_entity( 382 self, 383 entity_type: str, 384 api_args: dict[str, Any] | None = None, 385 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 386 ) -> AgentExecuteResult: 387 """Run the `update` action, which updates an entity of `entity_type`.""" 388 return self.execute(entity_type, "update", api_args, **kwargs)
Run the update action, which updates an entity of entity_type.
390 def delete_entity( 391 self, 392 entity_type: str, 393 api_args: dict[str, Any] | None = None, 394 **kwargs: Any, # noqa: ANN401 # Forwarded verbatim to `execute()`. 395 ) -> AgentExecuteResult: 396 """Run the `delete` action, which deletes an entity of `entity_type`.""" 397 return self.execute(entity_type, "delete", api_args, **kwargs)
Run the delete action, which deletes an entity of entity_type.
150class AgentConnectorDetails(BaseModel): 151 """Connector metadata returned by the Agents API `inspect` endpoint.""" 152 153 model_config = ConfigDict(extra="allow", populate_by_name=True) 154 155 connector_id: str 156 """The connector ID.""" 157 158 name: str | None = None 159 """The connector name.""" 160 161 workspace_id: str | None = None 162 """The ID of the workspace the connector belongs to.""" 163 164 organization_id: str | None = None 165 """The ID of the organization the connector belongs to.""" 166 167 source_definition_id: str | None = None 168 """The ID of the underlying Airbyte source definition.""" 169 170 integration_name: str | None = Field(default=None, alias="source_definition_name") 171 """Name of the underlying integration, for example `GitHub` or `Snowflake`.""" 172 173 docs_skill_id: str | None = None 174 """Skill ID to pass to `AgentWorkspace.get_skill(...).read_docs()` (MCP: 175 `read_agent_skill_docs`) for this connector's usage docs.""" 176 177 context_store_readiness: AgentContextStoreReadiness | None = None 178 """Context Store readiness information, when reported.""" 179 180 warnings: list[Any] = Field(default_factory=list) 181 """Warnings reported by the Agents API, for example degraded capabilities.""" 182 183 @property 184 def context_store_entities(self) -> list[str]: 185 """The entity names this connector can cache in the Context Store. 186 187 Note that this lists Context Store-supported entities specifically. The Agents API 188 does not publish an exhaustive list of executable entity and action pairs, so an 189 entity may be executable via `AgentConnector.execute()` without appearing here. 190 """ 191 if self.context_store_readiness is None: 192 return [] 193 return [ 194 entity.entity 195 for entity in self.context_store_readiness.supported_context_store_entities 196 ]
Connector metadata returned by the Agents API inspect endpoint.
Name of the underlying integration, for example GitHub or Snowflake.
Skill ID to pass to AgentWorkspace.get_skill(...).read_docs() (MCP:
read_agent_skill_docs) for this connector's usage docs.
Warnings reported by the Agents API, for example degraded capabilities.
183 @property 184 def context_store_entities(self) -> list[str]: 185 """The entity names this connector can cache in the Context Store. 186 187 Note that this lists Context Store-supported entities specifically. The Agents API 188 does not publish an exhaustive list of executable entity and action pairs, so an 189 entity may be executable via `AgentConnector.execute()` without appearing here. 190 """ 191 if self.context_store_readiness is None: 192 return [] 193 return [ 194 entity.entity 195 for entity in self.context_store_readiness.supported_context_store_entities 196 ]
The entity names this connector can cache in the Context Store.
Note that this lists Context Store-supported entities specifically. The Agents API
does not publish an exhaustive list of executable entity and action pairs, so an
entity may be executable via AgentConnector.execute() without appearing here.
42class AgentConnectorInfo(BaseModel): 43 """Summary information about a connector, as returned by the Agents API.""" 44 45 model_config = ConfigDict(extra="allow") 46 47 id: str 48 """The connector ID.""" 49 50 name: str | None = None 51 """The connector name, for example `GitHub - <workspace_id>`."""
Summary information about a connector, as returned by the Agents API.
211class AgentConnectorMetadata(BaseModel): 212 """Connector-reported metadata about a single action's result, including pagination.""" 213 214 model_config = ConfigDict(extra="allow") 215 216 has_next_page: bool | None = None 217 """Whether more entities are available after this page, when the connector reports it.""" 218 219 end_cursor: str | None = None 220 """The cursor for the next page, when one is available. Pass it as `cursor` for Context Store 221 `search`, or as the connector's own cursor argument in `api_args` for direct connector 222 actions."""
Connector-reported metadata about a single action's result, including pagination.
54class AgentContextStoreEntity(BaseModel): 55 """An entity that a connector supports caching in the Airbyte Context Store.""" 56 57 model_config = ConfigDict(extra="allow") 58 59 entity: str 60 """The entity name, for example `issues`.""" 61 62 suggested: bool | None = None 63 """Whether Airbyte suggests caching this entity."""
An entity that a connector supports caching in the Airbyte Context Store.
66class AgentContextStoreReadiness(BaseModel): 67 """Context Store readiness information for a connector.""" 68 69 model_config = ConfigDict(extra="allow") 70 71 supported_context_store_entities: list[AgentContextStoreEntity] = Field(default_factory=list) 72 """The entities this connector can cache in the Context Store.""" 73 74 configured_cache_entities: list[dict[str, Any]] = Field(default_factory=list) 75 """The entities currently configured for caching, with their sync status."""
Context Store readiness information for a connector.
225class AgentExecuteResult(BaseModel): 226 """The result of executing a single action against an Airbyte Agents connector.""" 227 228 model_config = ConfigDict(extra="allow") 229 230 status: str 231 """The execution status reported by the Agents API, for example `success`.""" 232 233 result: Any = None 234 """The action's payload. Entity-returning actions put a list of entities here.""" 235 236 connector_metadata: AgentConnectorMetadata = Field(default_factory=AgentConnectorMetadata) 237 """Connector-reported metadata about the result, including pagination cursors.""" 238 239 execution_metadata: AgentExecutionMetadata = Field(default_factory=AgentExecutionMetadata) 240 """Metadata describing how the action was executed.""" 241 242 warning: dict[str, Any] | None = None 243 """A warning reported alongside an otherwise successful result.""" 244 245 @field_validator("connector_metadata", "execution_metadata", mode="before") 246 @classmethod 247 def _none_to_empty(cls, value: object) -> object: 248 return {} if value is None else value 249 250 @property 251 def entities(self) -> list[dict[str, Any]]: 252 """The result as a list of entities. 253 254 Raises `PyAirbyteInputError` if the action did not return a list of entities. Use 255 `result` for actions whose payload is not a list of entities. 256 """ 257 if not isinstance(self.result, list): 258 raise PyAirbyteInputError( 259 message="This action did not return a list of entities.", 260 guidance="Use the `result` attribute to read non-entity result payloads.", 261 context={"result_type": type(self.result).__name__}, 262 ) 263 264 invalid_types = sorted( 265 {type(entity).__name__ for entity in self.result if not isinstance(entity, dict)} 266 ) 267 if invalid_types: 268 raise PyAirbyteInputError( 269 message="This action returned a list that is not a list of entities.", 270 guidance="Use the `result` attribute to read non-entity result payloads.", 271 context={"unexpected_item_types": invalid_types}, 272 ) 273 return self.result 274 275 @property 276 def has_next_page(self) -> bool: 277 """Whether the connector reported more entities after this page.""" 278 return bool(self.connector_metadata.has_next_page) 279 280 @property 281 def end_cursor(self) -> str | None: 282 """The cursor for the next page, or `None` when there is no next page.""" 283 return self.connector_metadata.end_cursor
The result of executing a single action against an Airbyte Agents connector.
The execution status reported by the Agents API, for example success.
A warning reported alongside an otherwise successful result.
250 @property 251 def entities(self) -> list[dict[str, Any]]: 252 """The result as a list of entities. 253 254 Raises `PyAirbyteInputError` if the action did not return a list of entities. Use 255 `result` for actions whose payload is not a list of entities. 256 """ 257 if not isinstance(self.result, list): 258 raise PyAirbyteInputError( 259 message="This action did not return a list of entities.", 260 guidance="Use the `result` attribute to read non-entity result payloads.", 261 context={"result_type": type(self.result).__name__}, 262 ) 263 264 invalid_types = sorted( 265 {type(entity).__name__ for entity in self.result if not isinstance(entity, dict)} 266 ) 267 if invalid_types: 268 raise PyAirbyteInputError( 269 message="This action returned a list that is not a list of entities.", 270 guidance="Use the `result` attribute to read non-entity result payloads.", 271 context={"unexpected_item_types": invalid_types}, 272 ) 273 return self.result
The result as a list of entities.
Raises PyAirbyteInputError if the action did not return a list of entities. Use
result for actions whose payload is not a list of entities.
199class AgentExecutionMetadata(BaseModel): 200 """Metadata describing how an Agents connector action was executed.""" 201 202 model_config = ConfigDict(extra="allow") 203 204 connector_instance_id: str | None = None 205 """The connector instance that served the request.""" 206 207 execution_time_ms: int | None = None 208 """The server-side execution time, in milliseconds."""
Metadata describing how an Agents connector action was executed.
77class AgentOrganization: 78 """An organization on the Airbyte Agents platform. 79 80 ```python 81 from airbyte import agents 82 83 organization = agents.AgentOrganization.from_env() 84 workspace = organization.get_workspace("my-workspace") # by ID or name (case insensitive) 85 ``` 86 """ 87 88 def __init__( 89 self, 90 *, 91 organization_id: str | None = None, 92 client_id: str | SecretString | None = None, 93 client_secret: str | SecretString | None = None, 94 bearer_token: str | SecretString | None = None, 95 public_api_root: str | None = None, 96 config_api_root: str | None = None, 97 ) -> None: 98 """Initialize an `AgentOrganization`. 99 100 Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are 101 not passed explicitly. API roots default to the `AIRBYTE_CLOUD_API_URL` / 102 `AIRBYTE_CLOUD_CONFIG_API_URL` environment variables (public Airbyte Cloud when unset); 103 custom roots require `AIRBYTE_AGENTS_API_URL`. The organization ID is optional: the 104 Agents API infers it when the credentials belong to exactly one organization. 105 """ 106 self._credentials = _AirbyteCredentials.from_auth( 107 organization_id=organization_id, 108 client_id=client_id, 109 client_secret=client_secret, 110 bearer_token=bearer_token, 111 public_api_root=public_api_root, 112 config_api_root=config_api_root, 113 # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env 114 # fallback, since an env bearer token plus explicit client creds is rejected 115 # as mutually exclusive auth. 116 env_vars=not (client_id or client_secret or bearer_token), 117 ) 118 119 self.organization_id: str | None = self._credentials.organization_id 120 """The organization ID, when known.""" 121 122 @classmethod 123 def from_env(cls, organization_id: str | None = None) -> AgentOrganization: 124 """Create an `AgentOrganization` from the `AIRBYTE_CLOUD_*` environment variables.""" 125 return cls(organization_id=organization_id) 126 127 def list_workspaces(self) -> list[AgentWorkspace]: 128 """List the workspaces visible to these credentials in the Agents API.""" 129 return [ 130 self._workspace_from_info(info) 131 for info in ( 132 AgentWorkspaceInfo.model_validate(record) 133 for record in _api_util.list_agent_workspaces( 134 credentials=self._credentials, 135 organization_id=self.organization_id, 136 ) 137 ) 138 ] 139 140 def get_workspace( 141 self, 142 id_or_name: str | None = None, 143 /, 144 *, 145 workspace_id: str | None = None, 146 name: str | None = None, 147 ) -> AgentWorkspace: 148 """Get a workspace in this organization, by ID or by name. 149 150 Pass a single positional value to look the workspace up by either its ID or its 151 name, or name the argument to be explicit. 152 153 Lookup by an explicit `workspace_id` does not call the Agents API. Every other form 154 lists the organization's workspaces and matches on ID first, then on an exact name, 155 ignoring case. 156 """ 157 lookup = _resolve_workspace_lookup( 158 id_or_name, 159 workspace_id=workspace_id, 160 name=name, 161 ) 162 163 if lookup.workspace_id and not lookup.name: 164 return self._workspace_from_info(AgentWorkspaceInfo(id=lookup.workspace_id)) 165 166 workspaces = self.list_workspaces() 167 if lookup.workspace_id: 168 id_matches = [ 169 workspace 170 for workspace in workspaces 171 if workspace.workspace_id == lookup.workspace_id 172 ] 173 if id_matches: 174 return id_matches[0] 175 176 name_lower = (lookup.name or "").lower() 177 matches = [ 178 workspace 179 for workspace in workspaces 180 if workspace.name and workspace.name.lower() == name_lower 181 ] 182 if not matches: 183 raise AirbyteError( 184 message="No workspace found with the given ID or name.", 185 guidance="Use `list_workspaces()` to see the available workspaces.", 186 context={"name": lookup.name}, 187 ) 188 if len(matches) > 1: 189 raise AirbyteError( 190 message="Multiple workspaces matched the given name.", 191 guidance="Pass `workspace_id` instead of `name`.", 192 context={"name": lookup.name, "match_count": len(matches)}, 193 ) 194 return matches[0] 195 196 def as_cloud_organization(self) -> CloudOrganization: 197 """Return this organization as an `airbyte.cloud.CloudOrganization`. 198 199 Every Agents organization is also a Cloud organization, so this conversion needs no 200 API call. It requires a known organization ID, and raises `PyAirbyteInputError` 201 when the ID is unknown. 202 """ 203 if not self.organization_id: 204 raise PyAirbyteInputError( 205 message="Organization ID is required to convert to a `CloudOrganization`.", 206 guidance=( 207 "Provide `organization_id`, or set the `AIRBYTE_CLOUD_ORGANIZATION_ID` " 208 "environment variable." 209 ), 210 ) 211 return CloudOrganization( 212 organization_id=self.organization_id, 213 client_id=self._credentials.client_id, 214 client_secret=self._credentials.client_secret, 215 bearer_token=self._credentials.bearer_token, 216 public_api_root=self._credentials.public_api_root, 217 config_api_root=self._credentials.config_api_root, 218 ) 219 220 @classmethod 221 def from_cloud_organization( 222 cls, 223 cloud_organization: CloudOrganization, 224 ) -> AgentOrganization: 225 """Return a Cloud organization as an `AgentOrganization`. 226 227 Whether the organization can actually execute connector actions depends on its 228 Airbyte Agents subscription, which is only knowable per workspace. Use 229 `AgentWorkspace.from_cloud_workspace()` for an authoritative eligibility check. 230 231 Raises `AirbyteAgentsUnavailableError` when the Cloud organization uses non-public Cloud 232 API roots unless `AIRBYTE_AGENTS_API_URL` is set; the Cloud API roots are carried over so 233 the token exchange stays on the same deployment. 234 """ 235 credentials = cloud_organization._credentials # noqa: SLF001 # Same-domain conversion. 236 _api_util.check_public_cloud_api_roots(credentials) 237 return cls( 238 organization_id=cloud_organization.organization_id, 239 client_id=credentials.client_id, 240 client_secret=credentials.client_secret, 241 bearer_token=credentials.bearer_token, 242 public_api_root=credentials.public_api_root, 243 config_api_root=credentials.config_api_root, 244 ) 245 246 def _workspace_from_info(self, info: AgentWorkspaceInfo) -> AgentWorkspace: 247 """Build an `AgentWorkspace` from workspace info, reusing these credentials.""" 248 return AgentWorkspace( 249 workspace_id=info.id, 250 organization_id=info.organization_id or self.organization_id, 251 name=info.name, 252 client_id=self._credentials.client_id, 253 client_secret=self._credentials.client_secret, 254 bearer_token=self._credentials.bearer_token, 255 public_api_root=self._credentials.public_api_root, 256 config_api_root=self._credentials.config_api_root, 257 )
An organization on the Airbyte Agents platform.
from airbyte import agents
organization = agents.AgentOrganization.from_env()
workspace = organization.get_workspace("my-workspace") # by ID or name (case insensitive)
88 def __init__( 89 self, 90 *, 91 organization_id: str | None = None, 92 client_id: str | SecretString | None = None, 93 client_secret: str | SecretString | None = None, 94 bearer_token: str | SecretString | None = None, 95 public_api_root: str | None = None, 96 config_api_root: str | None = None, 97 ) -> None: 98 """Initialize an `AgentOrganization`. 99 100 Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are 101 not passed explicitly. API roots default to the `AIRBYTE_CLOUD_API_URL` / 102 `AIRBYTE_CLOUD_CONFIG_API_URL` environment variables (public Airbyte Cloud when unset); 103 custom roots require `AIRBYTE_AGENTS_API_URL`. The organization ID is optional: the 104 Agents API infers it when the credentials belong to exactly one organization. 105 """ 106 self._credentials = _AirbyteCredentials.from_auth( 107 organization_id=organization_id, 108 client_id=client_id, 109 client_secret=client_secret, 110 bearer_token=bearer_token, 111 public_api_root=public_api_root, 112 config_api_root=config_api_root, 113 # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env 114 # fallback, since an env bearer token plus explicit client creds is rejected 115 # as mutually exclusive auth. 116 env_vars=not (client_id or client_secret or bearer_token), 117 ) 118 119 self.organization_id: str | None = self._credentials.organization_id 120 """The organization ID, when known."""
Initialize an AgentOrganization.
Credentials fall back to the AIRBYTE_CLOUD_* environment variables when they are
not passed explicitly. API roots default to the AIRBYTE_CLOUD_API_URL /
AIRBYTE_CLOUD_CONFIG_API_URL environment variables (public Airbyte Cloud when unset);
custom roots require AIRBYTE_AGENTS_API_URL. The organization ID is optional: the
Agents API infers it when the credentials belong to exactly one organization.
122 @classmethod 123 def from_env(cls, organization_id: str | None = None) -> AgentOrganization: 124 """Create an `AgentOrganization` from the `AIRBYTE_CLOUD_*` environment variables.""" 125 return cls(organization_id=organization_id)
Create an AgentOrganization from the AIRBYTE_CLOUD_* environment variables.
127 def list_workspaces(self) -> list[AgentWorkspace]: 128 """List the workspaces visible to these credentials in the Agents API.""" 129 return [ 130 self._workspace_from_info(info) 131 for info in ( 132 AgentWorkspaceInfo.model_validate(record) 133 for record in _api_util.list_agent_workspaces( 134 credentials=self._credentials, 135 organization_id=self.organization_id, 136 ) 137 ) 138 ]
List the workspaces visible to these credentials in the Agents API.
140 def get_workspace( 141 self, 142 id_or_name: str | None = None, 143 /, 144 *, 145 workspace_id: str | None = None, 146 name: str | None = None, 147 ) -> AgentWorkspace: 148 """Get a workspace in this organization, by ID or by name. 149 150 Pass a single positional value to look the workspace up by either its ID or its 151 name, or name the argument to be explicit. 152 153 Lookup by an explicit `workspace_id` does not call the Agents API. Every other form 154 lists the organization's workspaces and matches on ID first, then on an exact name, 155 ignoring case. 156 """ 157 lookup = _resolve_workspace_lookup( 158 id_or_name, 159 workspace_id=workspace_id, 160 name=name, 161 ) 162 163 if lookup.workspace_id and not lookup.name: 164 return self._workspace_from_info(AgentWorkspaceInfo(id=lookup.workspace_id)) 165 166 workspaces = self.list_workspaces() 167 if lookup.workspace_id: 168 id_matches = [ 169 workspace 170 for workspace in workspaces 171 if workspace.workspace_id == lookup.workspace_id 172 ] 173 if id_matches: 174 return id_matches[0] 175 176 name_lower = (lookup.name or "").lower() 177 matches = [ 178 workspace 179 for workspace in workspaces 180 if workspace.name and workspace.name.lower() == name_lower 181 ] 182 if not matches: 183 raise AirbyteError( 184 message="No workspace found with the given ID or name.", 185 guidance="Use `list_workspaces()` to see the available workspaces.", 186 context={"name": lookup.name}, 187 ) 188 if len(matches) > 1: 189 raise AirbyteError( 190 message="Multiple workspaces matched the given name.", 191 guidance="Pass `workspace_id` instead of `name`.", 192 context={"name": lookup.name, "match_count": len(matches)}, 193 ) 194 return matches[0]
Get a workspace in this organization, by ID or by name.
Pass a single positional value to look the workspace up by either its ID or its name, or name the argument to be explicit.
Lookup by an explicit workspace_id does not call the Agents API. Every other form
lists the organization's workspaces and matches on ID first, then on an exact name,
ignoring case.
196 def as_cloud_organization(self) -> CloudOrganization: 197 """Return this organization as an `airbyte.cloud.CloudOrganization`. 198 199 Every Agents organization is also a Cloud organization, so this conversion needs no 200 API call. It requires a known organization ID, and raises `PyAirbyteInputError` 201 when the ID is unknown. 202 """ 203 if not self.organization_id: 204 raise PyAirbyteInputError( 205 message="Organization ID is required to convert to a `CloudOrganization`.", 206 guidance=( 207 "Provide `organization_id`, or set the `AIRBYTE_CLOUD_ORGANIZATION_ID` " 208 "environment variable." 209 ), 210 ) 211 return CloudOrganization( 212 organization_id=self.organization_id, 213 client_id=self._credentials.client_id, 214 client_secret=self._credentials.client_secret, 215 bearer_token=self._credentials.bearer_token, 216 public_api_root=self._credentials.public_api_root, 217 config_api_root=self._credentials.config_api_root, 218 )
Return this organization as an airbyte.cloud.CloudOrganization.
Every Agents organization is also a Cloud organization, so this conversion needs no
API call. It requires a known organization ID, and raises PyAirbyteInputError
when the ID is unknown.
220 @classmethod 221 def from_cloud_organization( 222 cls, 223 cloud_organization: CloudOrganization, 224 ) -> AgentOrganization: 225 """Return a Cloud organization as an `AgentOrganization`. 226 227 Whether the organization can actually execute connector actions depends on its 228 Airbyte Agents subscription, which is only knowable per workspace. Use 229 `AgentWorkspace.from_cloud_workspace()` for an authoritative eligibility check. 230 231 Raises `AirbyteAgentsUnavailableError` when the Cloud organization uses non-public Cloud 232 API roots unless `AIRBYTE_AGENTS_API_URL` is set; the Cloud API roots are carried over so 233 the token exchange stays on the same deployment. 234 """ 235 credentials = cloud_organization._credentials # noqa: SLF001 # Same-domain conversion. 236 _api_util.check_public_cloud_api_roots(credentials) 237 return cls( 238 organization_id=cloud_organization.organization_id, 239 client_id=credentials.client_id, 240 client_secret=credentials.client_secret, 241 bearer_token=credentials.bearer_token, 242 public_api_root=credentials.public_api_root, 243 config_api_root=credentials.config_api_root, 244 )
Return a Cloud organization as an AgentOrganization.
Whether the organization can actually execute connector actions depends on its
Airbyte Agents subscription, which is only knowable per workspace. Use
AgentWorkspace.from_cloud_workspace() for an authoritative eligibility check.
Raises AirbyteAgentsUnavailableError when the Cloud organization uses non-public Cloud
API roots unless AIRBYTE_AGENTS_API_URL is set; the Cloud API roots are carried over so
the token exchange stays on the same deployment.
26class AgentSkill: 27 """A skill on the Airbyte Agents platform. 28 29 Get one from `AgentWorkspace.get_skill()` or `AgentWorkspace.list_skills()` rather than 30 constructing it directly. 31 """ 32 33 def __init__( 34 self, 35 skill_id: str, 36 *, 37 credentials: _AirbyteCredentials, 38 workspace_id: str | None = None, 39 info: AgentSkillInfo | None = None, 40 ) -> None: 41 """Initialize an `AgentSkill`. Prefer `AgentWorkspace.get_skill()`.""" 42 self.skill_id = skill_id 43 """The skill ID.""" 44 45 self._credentials = credentials 46 self._workspace_id = workspace_id 47 self._info = info 48 49 @property 50 def info(self) -> AgentSkillInfo: 51 """The skill's metadata, fetched from the Agents API if not already known.""" 52 if self._info is None: 53 self._info = self.read_docs().metadata 54 return self._info 55 56 @property 57 def title(self) -> str | None: 58 """The human-readable skill title.""" 59 return self.info.title 60 61 @property 62 def kind(self) -> str | None: 63 """The skill category, for example `static` or `connector_source`.""" 64 return self.info.kind 65 66 def read_docs(self, *, section: str | None = None) -> AgentSkillDocs: 67 """Read this skill's docs, optionally scoped to a single section. 68 69 Omit `section` for metadata, guidance, and the outline of available sections, or 70 pass an exact section `id` from the outline to read that section. 71 """ 72 docs = AgentSkillDocs.model_validate( 73 _api_util.read_agent_skill_docs( 74 skill_id=self.skill_id, 75 credentials=self._credentials, 76 organization_id=self._credentials.organization_id, 77 workspace_id=self._workspace_id, 78 section=section, 79 ) 80 ) 81 if self._info is None: 82 self._info = docs.metadata 83 return docs
A skill on the Airbyte Agents platform.
Get one from AgentWorkspace.get_skill() or AgentWorkspace.list_skills() rather than
constructing it directly.
33 def __init__( 34 self, 35 skill_id: str, 36 *, 37 credentials: _AirbyteCredentials, 38 workspace_id: str | None = None, 39 info: AgentSkillInfo | None = None, 40 ) -> None: 41 """Initialize an `AgentSkill`. Prefer `AgentWorkspace.get_skill()`.""" 42 self.skill_id = skill_id 43 """The skill ID.""" 44 45 self._credentials = credentials 46 self._workspace_id = workspace_id 47 self._info = info
Initialize an AgentSkill. Prefer AgentWorkspace.get_skill().
49 @property 50 def info(self) -> AgentSkillInfo: 51 """The skill's metadata, fetched from the Agents API if not already known.""" 52 if self._info is None: 53 self._info = self.read_docs().metadata 54 return self._info
The skill's metadata, fetched from the Agents API if not already known.
56 @property 57 def title(self) -> str | None: 58 """The human-readable skill title.""" 59 return self.info.title
The human-readable skill title.
61 @property 62 def kind(self) -> str | None: 63 """The skill category, for example `static` or `connector_source`.""" 64 return self.info.kind
The skill category, for example static or connector_source.
66 def read_docs(self, *, section: str | None = None) -> AgentSkillDocs: 67 """Read this skill's docs, optionally scoped to a single section. 68 69 Omit `section` for metadata, guidance, and the outline of available sections, or 70 pass an exact section `id` from the outline to read that section. 71 """ 72 docs = AgentSkillDocs.model_validate( 73 _api_util.read_agent_skill_docs( 74 skill_id=self.skill_id, 75 credentials=self._credentials, 76 organization_id=self._credentials.organization_id, 77 workspace_id=self._workspace_id, 78 section=section, 79 ) 80 ) 81 if self._info is None: 82 self._info = docs.metadata 83 return docs
Read this skill's docs, optionally scoped to a single section.
Omit section for metadata, guidance, and the outline of available sections, or
pass an exact section id from the outline to read that section.
132class AgentSkillDocs(BaseModel): 133 """Documentation for a single skill, as returned by the Agents API.""" 134 135 model_config = ConfigDict(extra="allow") 136 137 metadata: AgentSkillInfo 138 """Metadata for the requested skill.""" 139 140 outline: list[AgentSkillSection] = Field(default_factory=list) 141 """The sections available for this skill.""" 142 143 section_id: str | None = None 144 """The requested section ID, or `None` for the default docs response.""" 145 146 content: list[dict[str, Any]] = Field(default_factory=list) 147 """Rendered docs content blocks, such as headings, paragraphs, and code blocks."""
Documentation for a single skill, as returned by the Agents API.
78class AgentSkillInfo(BaseModel): 79 """Summary information about a skill, as returned by the Agents API.""" 80 81 model_config = ConfigDict(extra="allow") 82 83 id: str 84 """The skill ID. Pass it to `read_skill_docs` to read this skill's docs.""" 85 86 kind: str | None = None 87 """The skill category, for example `static` or `connector_source`.""" 88 89 title: str | None = None 90 """The human-readable skill title.""" 91 92 summary: str | None = None 93 """A short summary of what the skill documents.""" 94 95 tags: list[str] = Field(default_factory=list) 96 """Search and categorization tags for the skill.""" 97 98 warnings: list[Any] = Field(default_factory=list) 99 """Non-fatal issues reported while building or reading the skill's docs."""
Summary information about a skill, as returned by the Agents API.
102class AgentSkillList(BaseModel): 103 """A page of skills, as returned by the Agents API.""" 104 105 model_config = ConfigDict(extra="allow") 106 107 data: list[AgentSkillInfo] 108 """The skills on this page.""" 109 110 next_cursor: str | None = None 111 """The cursor to pass as `cursor` to fetch the next page, when one is available."""
A page of skills, as returned by the Agents API.
114class AgentSkillSection(BaseModel): 115 """A section of a skill's docs, as listed in the docs outline.""" 116 117 model_config = ConfigDict(extra="allow") 118 119 id: str 120 """The section ID. Pass it as `section` to read this section.""" 121 122 title: str | None = None 123 """The human-readable section title.""" 124 125 summary: str | None = None 126 """A short summary of the section content.""" 127 128 available: bool = True 129 """Whether this section can currently be read."""
A section of a skill's docs, as listed in the docs outline.
34class AgentWorkspace: 35 """A workspace on the Airbyte Agents platform. 36 37 Airbyte Cloud credentials authenticate against the Agents API, so this class reads the 38 same `AIRBYTE_CLOUD_*` environment variables as `airbyte.cloud.CloudWorkspace`. 39 40 ```python 41 from airbyte import agents 42 43 workspace = agents.AgentWorkspace.from_env() 44 for connector in workspace.list_connectors(): 45 print(connector.name) 46 ``` 47 """ 48 49 def __init__( 50 self, 51 *, 52 workspace_id: str | None = None, 53 organization_id: str | None = None, 54 name: str | None = None, 55 client_id: str | SecretString | None = None, 56 client_secret: str | SecretString | None = None, 57 bearer_token: str | SecretString | None = None, 58 public_api_root: str | None = None, 59 config_api_root: str | None = None, 60 ) -> None: 61 """Initialize an `AgentWorkspace`. 62 63 Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are 64 not passed explicitly. API roots default to the `AIRBYTE_CLOUD_API_URL` / 65 `AIRBYTE_CLOUD_CONFIG_API_URL` environment variables (public Airbyte Cloud when unset); 66 custom roots require `AIRBYTE_AGENTS_API_URL`. 67 """ 68 credentials = _AirbyteCredentials.from_auth( 69 workspace_id=workspace_id, 70 organization_id=organization_id, 71 client_id=client_id, 72 client_secret=client_secret, 73 bearer_token=bearer_token, 74 public_api_root=public_api_root, 75 config_api_root=config_api_root, 76 # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env 77 # fallback, since an env bearer token plus explicit client creds is rejected 78 # as mutually exclusive auth. 79 env_vars=not (client_id or client_secret or bearer_token), 80 ) 81 if not credentials.workspace_id: 82 raise PyAirbyteInputError( 83 message="Workspace ID is required.", 84 guidance=( 85 "Provide `workspace_id`, or set the `AIRBYTE_CLOUD_WORKSPACE_ID` " 86 "environment variable." 87 ), 88 ) 89 90 self._credentials = credentials 91 92 self.workspace_id: str = credentials.workspace_id 93 """The workspace ID.""" 94 95 self.organization_id: str | None = credentials.organization_id 96 """The organization ID, sent to the Agents API when it is known.""" 97 98 self.name: str | None = name 99 """The workspace name, when known. Use `get_info()` to fetch it from the API.""" 100 101 @classmethod 102 def from_env( 103 cls, 104 workspace_id: str | None = None, 105 *, 106 organization_id: str | None = None, 107 ) -> AgentWorkspace: 108 """Create an `AgentWorkspace` from the `AIRBYTE_CLOUD_*` environment variables. 109 110 The variables used are `AIRBYTE_CLOUD_BEARER_TOKEN` or the 111 `AIRBYTE_CLOUD_CLIENT_ID` and `AIRBYTE_CLOUD_CLIENT_SECRET` pair, along with 112 `AIRBYTE_CLOUD_WORKSPACE_ID` and `AIRBYTE_CLOUD_ORGANIZATION_ID`. 113 """ 114 return cls(workspace_id=workspace_id, organization_id=organization_id) 115 116 def get_info(self) -> AgentWorkspaceInfo: 117 """Fetch this workspace from the Agents API. 118 119 A successful call is authoritative proof that the workspace is reachable through 120 the Agents API with these credentials. 121 """ 122 return AgentWorkspaceInfo.model_validate( 123 _api_util.get_agent_workspace( 124 workspace_id=self.workspace_id, 125 credentials=self._credentials, 126 organization_id=self.organization_id, 127 ) 128 ) 129 130 def list_connectors(self) -> list[AgentConnector]: 131 """List the connectors configured in this workspace.""" 132 return [ 133 AgentConnector( 134 connector_id=info.id, 135 name=info.name, 136 credentials=self._credentials, 137 workspace_id=self.workspace_id, 138 ) 139 for info in ( 140 AgentConnectorInfo.model_validate(record) 141 for record in _api_util.list_agent_connectors( 142 workspace_id=self.workspace_id, 143 credentials=self._credentials, 144 organization_id=self.organization_id, 145 ) 146 ) 147 ] 148 149 def list_skills(self) -> list[AgentSkill]: 150 """List all skills available to this workspace, following pagination.""" 151 return [ 152 AgentSkill( 153 skill_id=info.id, 154 credentials=self._credentials, 155 workspace_id=self.workspace_id, 156 info=info, 157 ) 158 for info in _skills.iter_skills( 159 credentials=self._credentials, 160 workspace_id=self.workspace_id, 161 ) 162 ] 163 164 def get_skill(self, skill_id: str) -> AgentSkill: 165 """Get a skill by ID, without calling the Agents API.""" 166 return AgentSkill( 167 skill_id, 168 credentials=self._credentials, 169 workspace_id=self.workspace_id, 170 ) 171 172 def read_skill_docs( 173 self, 174 skill_id: str, 175 *, 176 section: str | None = None, 177 ) -> AgentSkillDocs: 178 """Read a skill's docs, optionally scoped to a single section. 179 180 Omit `section` for metadata, guidance, and the outline of available sections, or 181 pass an exact section `id` from the outline to read that section. Connector usage 182 docs use the `docs_skill_id` reported by `AgentConnector.inspect()`. 183 """ 184 return self.get_skill(skill_id).read_docs(section=section) 185 186 def get_connector( 187 self, 188 id_or_name: str | None = None, 189 /, 190 *, 191 id: str | None = None, # noqa: A002 # Shadows `id` deliberately, as a short alias. 192 connector_id: str | None = None, 193 name: str | None = None, 194 ) -> AgentConnector: 195 """Get a connector in this workspace, by ID or by name. 196 197 Pass a single positional value to look the connector up by either its ID or its 198 name, or name the argument to be explicit: `id` and `connector_id` are synonyms, 199 so pass whichever reads better. 200 201 Lookup by an explicit ID does not call the Agents API. Every other form lists the 202 workspace's connectors and matches on ID first, then on an exact name, then on a 203 unique substring, so `name="GitHub"` finds a connector named 204 `GitHub - <workspace_id>`. Name matching is case-insensitive. 205 """ 206 lookup = _resolve_connector_lookup( 207 id_or_name, 208 id=id, 209 connector_id=connector_id, 210 name=name, 211 ) 212 213 if lookup.connector_id and not lookup.name: 214 return AgentConnector( 215 connector_id=lookup.connector_id, 216 credentials=self._credentials, 217 workspace_id=self.workspace_id, 218 ) 219 220 connectors = self.list_connectors() 221 if lookup.connector_id: 222 id_matches = [ 223 connector 224 for connector in connectors 225 if connector.connector_id == lookup.connector_id 226 ] 227 if id_matches: 228 return id_matches[0] 229 230 name_lower = (lookup.name or "").lower() 231 matches = [ 232 connector 233 for connector in connectors 234 if connector.name and connector.name.lower() == name_lower 235 ] or [ 236 connector 237 for connector in connectors 238 if connector.name and name_lower in connector.name.lower() 239 ] 240 if not matches: 241 raise AirbyteError( 242 message="No connector found with the given ID or name.", 243 guidance="Use `list_connectors()` to see the available connectors.", 244 context={"lookup": lookup.name, "workspace_id": self.workspace_id}, 245 ) 246 if len(matches) > 1: 247 raise AirbyteError( 248 message="Multiple connectors matched the given name.", 249 guidance="Pass `connector_id`, or a name that matches only one connector.", 250 context={ 251 "name": lookup.name, 252 "matched_names": [connector.name for connector in matches], 253 }, 254 ) 255 return matches[0] 256 257 def as_cloud_workspace(self) -> CloudWorkspace: 258 """Return this workspace as an `airbyte.cloud.CloudWorkspace`. 259 260 Every Agents workspace is also a Cloud workspace, so this conversion always 261 succeeds without calling either API. 262 """ 263 return CloudWorkspace( 264 workspace_id=self.workspace_id, 265 client_id=self._credentials.client_id, 266 client_secret=self._credentials.client_secret, 267 bearer_token=self._credentials.bearer_token, 268 api_root=self._credentials.public_api_root, 269 config_api_root=self._credentials.config_api_root, 270 ) 271 272 @classmethod 273 def from_cloud_workspace( 274 cls, 275 cloud_workspace: CloudWorkspace, 276 *, 277 organization_id: str | None = None, 278 verify: bool = True, 279 ) -> AgentWorkspace: 280 """Return a Cloud workspace as an `AgentWorkspace`. 281 282 Cloud workspace IDs are also Agents workspace IDs, but not every Cloud workspace is 283 reachable through the Agents API: the organization needs an Airbyte Agents 284 subscription. By default this is verified by fetching the workspace from the Agents 285 API, which raises `AirbyteError` when it is not eligible. Pass `verify=False` to 286 skip that call. 287 288 Raises `AirbyteAgentsUnavailableError` when the Cloud workspace uses non-public Cloud API 289 roots unless `AIRBYTE_AGENTS_API_URL` is set; the Cloud API roots are carried over so the 290 token exchange stays on the same deployment. 291 """ 292 _api_util.check_public_cloud_api_roots( 293 cloud_workspace._credentials, # noqa: SLF001 # Same-domain conversion. 294 ) 295 workspace = cls( 296 workspace_id=cloud_workspace.workspace_id, 297 organization_id=organization_id, 298 client_id=cloud_workspace.client_id, 299 client_secret=cloud_workspace.client_secret, 300 bearer_token=cloud_workspace.bearer_token, 301 public_api_root=cloud_workspace._credentials.public_api_root, # noqa: SLF001 302 config_api_root=cloud_workspace._credentials.config_api_root, # noqa: SLF001 303 ) 304 if verify: 305 workspace.get_info() 306 return workspace
A workspace on the Airbyte Agents platform.
Airbyte Cloud credentials authenticate against the Agents API, so this class reads the
same AIRBYTE_CLOUD_* environment variables as airbyte.cloud.CloudWorkspace.
from airbyte import agents
workspace = agents.AgentWorkspace.from_env()
for connector in workspace.list_connectors():
print(connector.name)
49 def __init__( 50 self, 51 *, 52 workspace_id: str | None = None, 53 organization_id: str | None = None, 54 name: str | None = None, 55 client_id: str | SecretString | None = None, 56 client_secret: str | SecretString | None = None, 57 bearer_token: str | SecretString | None = None, 58 public_api_root: str | None = None, 59 config_api_root: str | None = None, 60 ) -> None: 61 """Initialize an `AgentWorkspace`. 62 63 Credentials fall back to the `AIRBYTE_CLOUD_*` environment variables when they are 64 not passed explicitly. API roots default to the `AIRBYTE_CLOUD_API_URL` / 65 `AIRBYTE_CLOUD_CONFIG_API_URL` environment variables (public Airbyte Cloud when unset); 66 custom roots require `AIRBYTE_AGENTS_API_URL`. 67 """ 68 credentials = _AirbyteCredentials.from_auth( 69 workspace_id=workspace_id, 70 organization_id=organization_id, 71 client_id=client_id, 72 client_secret=client_secret, 73 bearer_token=bearer_token, 74 public_api_root=public_api_root, 75 config_api_root=config_api_root, 76 # Mirrors `CloudWorkspace.__init__`: any explicit credential disables env 77 # fallback, since an env bearer token plus explicit client creds is rejected 78 # as mutually exclusive auth. 79 env_vars=not (client_id or client_secret or bearer_token), 80 ) 81 if not credentials.workspace_id: 82 raise PyAirbyteInputError( 83 message="Workspace ID is required.", 84 guidance=( 85 "Provide `workspace_id`, or set the `AIRBYTE_CLOUD_WORKSPACE_ID` " 86 "environment variable." 87 ), 88 ) 89 90 self._credentials = credentials 91 92 self.workspace_id: str = credentials.workspace_id 93 """The workspace ID.""" 94 95 self.organization_id: str | None = credentials.organization_id 96 """The organization ID, sent to the Agents API when it is known.""" 97 98 self.name: str | None = name 99 """The workspace name, when known. Use `get_info()` to fetch it from the API."""
Initialize an AgentWorkspace.
Credentials fall back to the AIRBYTE_CLOUD_* environment variables when they are
not passed explicitly. API roots default to the AIRBYTE_CLOUD_API_URL /
AIRBYTE_CLOUD_CONFIG_API_URL environment variables (public Airbyte Cloud when unset);
custom roots require AIRBYTE_AGENTS_API_URL.
101 @classmethod 102 def from_env( 103 cls, 104 workspace_id: str | None = None, 105 *, 106 organization_id: str | None = None, 107 ) -> AgentWorkspace: 108 """Create an `AgentWorkspace` from the `AIRBYTE_CLOUD_*` environment variables. 109 110 The variables used are `AIRBYTE_CLOUD_BEARER_TOKEN` or the 111 `AIRBYTE_CLOUD_CLIENT_ID` and `AIRBYTE_CLOUD_CLIENT_SECRET` pair, along with 112 `AIRBYTE_CLOUD_WORKSPACE_ID` and `AIRBYTE_CLOUD_ORGANIZATION_ID`. 113 """ 114 return cls(workspace_id=workspace_id, organization_id=organization_id)
Create an AgentWorkspace from the AIRBYTE_CLOUD_* environment variables.
The variables used are AIRBYTE_CLOUD_BEARER_TOKEN or the
AIRBYTE_CLOUD_CLIENT_ID and AIRBYTE_CLOUD_CLIENT_SECRET pair, along with
AIRBYTE_CLOUD_WORKSPACE_ID and AIRBYTE_CLOUD_ORGANIZATION_ID.
116 def get_info(self) -> AgentWorkspaceInfo: 117 """Fetch this workspace from the Agents API. 118 119 A successful call is authoritative proof that the workspace is reachable through 120 the Agents API with these credentials. 121 """ 122 return AgentWorkspaceInfo.model_validate( 123 _api_util.get_agent_workspace( 124 workspace_id=self.workspace_id, 125 credentials=self._credentials, 126 organization_id=self.organization_id, 127 ) 128 )
Fetch this workspace from the Agents API.
A successful call is authoritative proof that the workspace is reachable through the Agents API with these credentials.
130 def list_connectors(self) -> list[AgentConnector]: 131 """List the connectors configured in this workspace.""" 132 return [ 133 AgentConnector( 134 connector_id=info.id, 135 name=info.name, 136 credentials=self._credentials, 137 workspace_id=self.workspace_id, 138 ) 139 for info in ( 140 AgentConnectorInfo.model_validate(record) 141 for record in _api_util.list_agent_connectors( 142 workspace_id=self.workspace_id, 143 credentials=self._credentials, 144 organization_id=self.organization_id, 145 ) 146 ) 147 ]
List the connectors configured in this workspace.
149 def list_skills(self) -> list[AgentSkill]: 150 """List all skills available to this workspace, following pagination.""" 151 return [ 152 AgentSkill( 153 skill_id=info.id, 154 credentials=self._credentials, 155 workspace_id=self.workspace_id, 156 info=info, 157 ) 158 for info in _skills.iter_skills( 159 credentials=self._credentials, 160 workspace_id=self.workspace_id, 161 ) 162 ]
List all skills available to this workspace, following pagination.
164 def get_skill(self, skill_id: str) -> AgentSkill: 165 """Get a skill by ID, without calling the Agents API.""" 166 return AgentSkill( 167 skill_id, 168 credentials=self._credentials, 169 workspace_id=self.workspace_id, 170 )
Get a skill by ID, without calling the Agents API.
172 def read_skill_docs( 173 self, 174 skill_id: str, 175 *, 176 section: str | None = None, 177 ) -> AgentSkillDocs: 178 """Read a skill's docs, optionally scoped to a single section. 179 180 Omit `section` for metadata, guidance, and the outline of available sections, or 181 pass an exact section `id` from the outline to read that section. Connector usage 182 docs use the `docs_skill_id` reported by `AgentConnector.inspect()`. 183 """ 184 return self.get_skill(skill_id).read_docs(section=section)
Read a skill's docs, optionally scoped to a single section.
Omit section for metadata, guidance, and the outline of available sections, or
pass an exact section id from the outline to read that section. Connector usage
docs use the docs_skill_id reported by AgentConnector.inspect().
186 def get_connector( 187 self, 188 id_or_name: str | None = None, 189 /, 190 *, 191 id: str | None = None, # noqa: A002 # Shadows `id` deliberately, as a short alias. 192 connector_id: str | None = None, 193 name: str | None = None, 194 ) -> AgentConnector: 195 """Get a connector in this workspace, by ID or by name. 196 197 Pass a single positional value to look the connector up by either its ID or its 198 name, or name the argument to be explicit: `id` and `connector_id` are synonyms, 199 so pass whichever reads better. 200 201 Lookup by an explicit ID does not call the Agents API. Every other form lists the 202 workspace's connectors and matches on ID first, then on an exact name, then on a 203 unique substring, so `name="GitHub"` finds a connector named 204 `GitHub - <workspace_id>`. Name matching is case-insensitive. 205 """ 206 lookup = _resolve_connector_lookup( 207 id_or_name, 208 id=id, 209 connector_id=connector_id, 210 name=name, 211 ) 212 213 if lookup.connector_id and not lookup.name: 214 return AgentConnector( 215 connector_id=lookup.connector_id, 216 credentials=self._credentials, 217 workspace_id=self.workspace_id, 218 ) 219 220 connectors = self.list_connectors() 221 if lookup.connector_id: 222 id_matches = [ 223 connector 224 for connector in connectors 225 if connector.connector_id == lookup.connector_id 226 ] 227 if id_matches: 228 return id_matches[0] 229 230 name_lower = (lookup.name or "").lower() 231 matches = [ 232 connector 233 for connector in connectors 234 if connector.name and connector.name.lower() == name_lower 235 ] or [ 236 connector 237 for connector in connectors 238 if connector.name and name_lower in connector.name.lower() 239 ] 240 if not matches: 241 raise AirbyteError( 242 message="No connector found with the given ID or name.", 243 guidance="Use `list_connectors()` to see the available connectors.", 244 context={"lookup": lookup.name, "workspace_id": self.workspace_id}, 245 ) 246 if len(matches) > 1: 247 raise AirbyteError( 248 message="Multiple connectors matched the given name.", 249 guidance="Pass `connector_id`, or a name that matches only one connector.", 250 context={ 251 "name": lookup.name, 252 "matched_names": [connector.name for connector in matches], 253 }, 254 ) 255 return matches[0]
Get a connector in this workspace, by ID or by name.
Pass a single positional value to look the connector up by either its ID or its
name, or name the argument to be explicit: id and connector_id are synonyms,
so pass whichever reads better.
Lookup by an explicit ID does not call the Agents API. Every other form lists the
workspace's connectors and matches on ID first, then on an exact name, then on a
unique substring, so name="GitHub" finds a connector named
GitHub - <workspace_id>. Name matching is case-insensitive.
257 def as_cloud_workspace(self) -> CloudWorkspace: 258 """Return this workspace as an `airbyte.cloud.CloudWorkspace`. 259 260 Every Agents workspace is also a Cloud workspace, so this conversion always 261 succeeds without calling either API. 262 """ 263 return CloudWorkspace( 264 workspace_id=self.workspace_id, 265 client_id=self._credentials.client_id, 266 client_secret=self._credentials.client_secret, 267 bearer_token=self._credentials.bearer_token, 268 api_root=self._credentials.public_api_root, 269 config_api_root=self._credentials.config_api_root, 270 )
Return this workspace as an airbyte.cloud.CloudWorkspace.
Every Agents workspace is also a Cloud workspace, so this conversion always succeeds without calling either API.
272 @classmethod 273 def from_cloud_workspace( 274 cls, 275 cloud_workspace: CloudWorkspace, 276 *, 277 organization_id: str | None = None, 278 verify: bool = True, 279 ) -> AgentWorkspace: 280 """Return a Cloud workspace as an `AgentWorkspace`. 281 282 Cloud workspace IDs are also Agents workspace IDs, but not every Cloud workspace is 283 reachable through the Agents API: the organization needs an Airbyte Agents 284 subscription. By default this is verified by fetching the workspace from the Agents 285 API, which raises `AirbyteError` when it is not eligible. Pass `verify=False` to 286 skip that call. 287 288 Raises `AirbyteAgentsUnavailableError` when the Cloud workspace uses non-public Cloud API 289 roots unless `AIRBYTE_AGENTS_API_URL` is set; the Cloud API roots are carried over so the 290 token exchange stays on the same deployment. 291 """ 292 _api_util.check_public_cloud_api_roots( 293 cloud_workspace._credentials, # noqa: SLF001 # Same-domain conversion. 294 ) 295 workspace = cls( 296 workspace_id=cloud_workspace.workspace_id, 297 organization_id=organization_id, 298 client_id=cloud_workspace.client_id, 299 client_secret=cloud_workspace.client_secret, 300 bearer_token=cloud_workspace.bearer_token, 301 public_api_root=cloud_workspace._credentials.public_api_root, # noqa: SLF001 302 config_api_root=cloud_workspace._credentials.config_api_root, # noqa: SLF001 303 ) 304 if verify: 305 workspace.get_info() 306 return workspace
Return a Cloud workspace as an AgentWorkspace.
Cloud workspace IDs are also Agents workspace IDs, but not every Cloud workspace is
reachable through the Agents API: the organization needs an Airbyte Agents
subscription. By default this is verified by fetching the workspace from the Agents
API, which raises AirbyteError when it is not eligible. Pass verify=False to
skip that call.
Raises AirbyteAgentsUnavailableError when the Cloud workspace uses non-public Cloud API
roots unless AIRBYTE_AGENTS_API_URL is set; the Cloud API roots are carried over so the
token exchange stays on the same deployment.
24class AgentWorkspaceInfo(BaseModel): 25 """Summary information about a workspace, as returned by the Agents API.""" 26 27 model_config = ConfigDict(extra="allow") 28 29 id: str 30 """The workspace ID.""" 31 32 name: str | None = None 33 """The workspace name.""" 34 35 organization_id: str | None = None 36 """The ID of the organization the workspace belongs to.""" 37 38 status: str | None = None 39 """The workspace status, for example `active`."""
Summary information about a workspace, as returned by the Agents API.