Skip to content

Clients

Classes for interacting with other services.

AsiteClient

Bases: BaseSessionAuthClient

Manages Asite session-ID lifecycle and exposes high-level read APIs.

Stored credentials in Key Vault are a JSON object:

{"emailId": "user@example.com", "password": "..."}

Attributes:

Name Type Description
user_id

Unique ID for the Hoppa user, used to scope the KeyVault secret.

create_url_generator

create_url_generator(workspace_name, folder_path, file_name)

Closure that re-mints the signed URL on demand.

Mirrors SharePointClient.create_url_generator. The closure delegates to :meth:resolve_download_uri, which validates the session (re-logging in if stale) and returns a fresh URL.

Pass the result as url_generator= to AzureBlobDocumentVersion; the base SignedEntity machinery calls it whenever the cached URL has aged past its TTL.

invalidate_session

invalidate_session()

Clear the cached session ID and all derived per-session caches.

list_documents

list_documents(doclist_uri, *, share_links=True, expiry='10h')

Documents in a folder, optionally with public share links.

Each entry
  • file_name: FileName (stable, human-readable)
  • file_name_display: FileNameDisplay
  • folder_path: FolderPath (backslash-separated, includes workspace name as prefix and file name as suffix)
  • size_bytes / size_human: parsed from the two FileSize elements (Asite emits both, in that order).
  • document_id / document_id_prefix
  • revision_id / revision_id_prefix
  • revision_no, doc_ref, doc_title, issue_no
  • publisher_name, publisher_org, published_date (ISO)
  • share_link: public adoddle.asite.com/lnk/ URL or None if the folder doesn't have "Enable Public Links" on
  • download_uri: session-authenticated download URI (always present, fallback when share link unavailable)
  • raw: full documentVO converted to a dict

Cached per (doclist_uri, share_links, expiry) for the lifetime of this client. Cache is cleared by invalidate_session().

list_documents_in_folder

list_documents_in_folder(workspace_name, folder_path, *, share_links=True)

Walk workspaces → folders → doclist by stable identifiers.

Convenience for the connector: instead of doing the three calls and the recursive folder-name match manually, hand it the stable identifiers stored in connection_details and get back the list of documents in that folder.

Returns None if any step of the walk doesn't find a match (workspace not accessible, folder renamed/deleted, etc.).

list_folders

list_folders(folders_uri)

Recursive folder tree under a workspace's folders URI.

Folder hierarchy is encoded by XML nesting in Asite's response, so each entry has a children list with the same shape: - name / id / id_prefix - doclist_uri: URI for firstpage_doclist (None for system folders that don't expose one) - children: nested folder dicts - raw: full folderVO element converted to a dict

Cached per folders_uri for the lifetime of this client.

list_workspaces

list_workspaces()

Workspaces accessible to the authenticated user.

Each entry
  • name: human-readable workspace name (stable identifier)
  • id: session-scoped Workspace_Id
  • id_prefix: stable numeric prefix of the id
  • folders_uri: URI to fetch the folder tree for this workspace
  • raw: full workspaceVO element converted to a dict

Cached for the lifetime of this client. invalidate_session() clears the cache.

resolve_download_uri

resolve_download_uri(workspace_name, folder_path, file_name)

Return a fresh self-authenticating download URL for the document.

Re-walks workspace → folder → doclist from scratch (caches cleared) so a stale session is reliably detected by the inherited _execute_with_reauth machinery and triggers a transparent re-login before the URL is minted. The returned URL embeds the current session_id as a query parameter, which Asite's /downloadDocument/{RevisionId} endpoint accepts as auth (verified empirically May 2026).

Raises:

Type Description
RuntimeError

when the workspace, folder, or file is no longer reachable. Callers (typically DocumentVersion.get via url_generator) should let this propagate so the bind surfaces clearly.

AsiteSessionAuthProvider

Bases: AuthenticationProvider

Kiota authentication provider that injects Asite's ASessionID header.

Wraps an AsiteClient and reads its (lazily-refreshed) session_id on every outgoing request. To force a re-login after a 401, callers should call client.invalidate_session() and retry.

AutodeskClient

Bases: BaseOAuthClient

A class for interacting with Autodesk Platform Services APIs for ACC:

  • Data Management API
  • BIM 360 API
  • Autodesk Construction Cloud API

Handles differences in API conventions and schema, building on top of the Autodesk API to provide specialist methods for data translation / exchange use cases.

https://aps.autodesk.com/developer/documentation

Attributes:

Name Type Description
user_id

Unique ID for the user tethered to the client. Used to scope delegated access.

client_id

Autodesk client ID. Defaults to environment variable AUTODESK_CLIENT_ID.

access_token str

Three-legged OAuth token for calling Autodesk APIs.

__init__

__init__(user_id, access_token=None, client_id=os.getenv('AUTODESK_ACC_CLIENT_ID'))

Initialize the client.

Parameters:

Name Type Description Default
user_id str

Unique ID for the user. Used to obtain three-legged access token. .

required
access_token str | None

Optionally pass an OAuth access token directly to the constructor.

None
client_id str | None

Three-legged OAuth token for calling Autodesk APIs.

getenv('AUTODESK_ACC_CLIENT_ID')

build_tree

build_tree()

Constructs a hierarchical tree structure of hubs and their associated projects.

This method fetches all accessible hubs from the Autodesk Forge API and then retrieves all projects within each hub, organizing them into a nested tree structure suitable for display in UI components like tree views or dropdowns.

The tree structure follows a hub -> project hierarchy where each hub contains multiple projects as children nodes.

Returns:

Type Description
list[dict[str, Any]]

A list of dictionaries representing the tree structure. Each hub dictionary contains:

  • 'id' (str): The hub's unique identifier
  • 'name' (str): The hub's display name
  • 'type' (str): Always 'hub' for hub entries
  • 'children' (list): List of project dictionaries, each containing:
    • 'id' (str): The project's unique identifier
    • 'name' (str): The project's display name
    • 'type' (str): Always 'project' for project entries
    • 'children' (list): Empty list (reserved for future expansion)

build_tree_project

build_tree_project(hub_id, project_id)

Constructs a hierarchical tree structure of folders and items within a specific project.

This method fetches the top-level contents of a project and recursively builds a complete tree structure that includes all nested folders and their contents. The resulting tree is suitable for displaying the project's file/folder hierarchy in UI components.

Parameters:

Name Type Description Default
hub_id str

The unique identifier of the hub containing the project

required
project_id str

The unique identifier of the project to build the tree for

required

Returns:

Type Description
list[dict[str, Any]]

A list of dictionaries representing the project's folder tree structure. Each folder dictionary contains:

  • 'id' (str): The folder's unique identifier
  • 'name' (str): The folder's display name
  • 'type' (str): Always 'folder' for folder entries
  • 'children' (list): Nested list of subfolders and items within this folder

create_folder_custom_attribute

create_folder_custom_attribute(project_id, folder_id, name, attr_type, array_values=None)

Create a new custom attribute definition on a folder (BIM 360 / Forma).

POST .../bim360/docs/v1/projects/{project_id}/folders/{folder_id}/custom-attribute-definitions. The definition applies to this folder and its subfolders. attr_type is one of "string" | "date" | "array"; array_values supplies the drop-list options for an "array" attribute. The Data-Management "b." project-id prefix is stripped for the BIM 360 API. Raises requests.HTTPError on a non-2xx response (e.g. 403 no permission, 400 name already exists).

download_file_bytes

download_file_bytes(file_href)

Downloads a file from Autodesk Forge and returns its content as bytes.

This method handles the multi-step process required to download files from Autodesk Forge: 1. Fetches file metadata using the provided file reference URL 2. Extracts the storage download URL from the metadata 3. Cleans the URL by removing query parameters that may interfere with the download 4. Requests a signed S3 download URL from Autodesk's storage service 5. Downloads the actual file content using the signed URL

Parameters:

Name Type Description Default
file_href str

The file reference URL obtained from Autodesk Forge API, typically from item or version data

required

Returns:

Name Type Description
bytes bytes

The raw file content as bytes

Raises:

Type Description
Exception

If the download fails

get_document_versions

get_document_versions(project_id, item_id)

Retrieve all versions for a specific document item.

This method fetches all available versions of a document item using the Autodesk Data Management API. Versions represent different iterations or revisions of the same document.

Parameters:

Name Type Description Default
project_id str

The project ID in Data Management API format (no "b." prefix)

required
item_id str

The unique identifier for the document item

required

Returns:

Type Description
list[dict[str, Any]]

List of document versions

get_folder_contents

get_folder_contents(hub_id, project_id, folder_id=None)

Retrieve the contents of a folder in an Autodesk project, or get top-level folders if no folder ID is specified.

Parameters:

Name Type Description Default
hub_id str

The hub ID where the project is located

required
project_id str

The project ID in Data Management API format (no "b." prefix)

required
folder_id str | None

The folder ID to get contents for. If None, returns top-level folders for the project

None

Returns:

Type Description
list[Any]

A list containing folder contents at the folder-level only (not nested folders)

get_folder_contents_recursive

get_folder_contents_recursive(hub_id, project_id, folder_id)

Recursively retrieve all contents of a folder and its subfolders in an Autodesk project.

Parameters:

Name Type Description Default
hub_id str

The hub ID where the project is located

required
project_id str

The project ID in Data Management API format (no "b." prefix)

required
folder_id str

The folder ID to recursively get contents for

required

Returns:

Type Description
list[dict[str, Any]]

A list of dictionaries representing the folder structure, where each item contains:

  • id: The item ID
  • project_id: The project ID
  • hub_id: The hub ID
  • name: The display name of the item
  • name: The display name of the item
  • type: 'folder' or 'file' (canonicalised from APS 'folders'/'items')
  • children: List of child items (only present for folders)
  • For files: canonical audit metadata (created_at, last_modified_at, created_by, last_modified_by, size, web_url, source).

get_folder_named_attributes

get_folder_named_attributes(project_id, folder_id=None, hub_id=None)

Retrieves all custom attribute definitions for a folder within a specified project.

Custom attributes are user-defined metadata fields that can be attached to folders and items in BIM 360 / ACC.

Parameters:

Name Type Description Default
project_id str

The project identifier from the Data Management API (with "b." prefix, which will be automatically removed)

required
folder_id str | None

The folder identifier from the Data Management API. If not provided function will retrieve the naming standard for the root project folder.

None
hub_id str | None

The unique identifier of the hub containing the project. Must be provided if folder_id is not provided

None

Returns:

Type Description
list[dict[str, Any]]

A list of dictionaries containing custom attribute definitions from the folders in the project. Each dictionary represents an attribute definition with properties like name, type, validation rules, etc.

get_folder_naming_standard

get_folder_naming_standard(hub_id, project_id, folder_id=None)

Retrieves the naming standard configuration from a BIM 360 / ACC project for the specified folder. Returns None if no naming standard is applied to the folder

Naming standards define rules and patterns for how files and folders should be named within a project to maintain consistency and organization.

Parameters:

Name Type Description Default
hub_id str

The unique identifier of the hub containing the project

required
project_id str

The project identifier from the Data Management API (no "b." prefix).

required
folder_id str | None

The folder identifier from the Data Management API. If not provided function will retrieve the naming standard for the root project folder

None

Returns:

Type Description
dict[str, Any]

A dictionary containing the naming standard configuration data, including rules, patterns, and validation criteria defined for the standard

get_hubs

get_hubs()

Retrieves all hubs accessible to the authenticated user from the Autodesk Forge API.

Makes a GET request to the Autodesk Project API to fetch hub information. Hubs represent the highest level in the Autodesk data hierarchy and typically correspond to accounts or organizations that contain projects and folders.

Returns:

Type Description
list[dict[str, Any]]

JSON response containing hub data from the API, including hub IDs, names, and other metadata. The exact structure depends on the Autodesk API response format.

get_naming_standard

get_naming_standard(project_id, naming_standard_id)

Retrieves a specific naming standard configuration from a BIM 360 / ACC project.

This method fetches the details of a naming standard by its ID from the specified project using the BIM 360 Docs API. Naming standards define rules and patterns for how files and folders should be named within a project to maintain consistency and organization.

Parameters:

Name Type Description Default
project_id str

The project identifier from the Data Management API (with "b." prefix, which will be automatically removed)

required
naming_standard_id str

The unique identifier of the naming standard to retrieve

required

Returns:

Type Description
dict[str, Any]

A dictionary containing the naming standard configuration data, including rules, patterns, and validation criteria defined for the standard

get_projects

get_projects(hub_id)

Retrieves all projects within a specified hub from the Autodesk Forge API.

Makes a GET request to the Autodesk Project API to fetch project information for a given hub. Projects are containers within hubs that hold folders, items, and other project-related data.

Parameters:

Name Type Description Default
hub_id str

The unique identifier of the hub to retrieve projects from. This should be obtained from a previous call to get_hubs().

required

Returns:

Type Description
list[dict[str, Any]]

JSON response containing project data from the API, including project IDs, names, and other metadata. The exact structure depends on the Autodesk API response format.

get_signed_url

get_signed_url(file_href, minutes=60)

Fetches a signed URL for downloading a file from an AWS S3 bucket.

This function retrieves file details using the provided file reference URL (file_href) and an access token for authorization. It then constructs a signed URL for the file by stripping unnecessary query parameters, appending a specific download endpoint, and extracting the signed URL from the download response.

Parameters:

Name Type Description Default
file_href str

The URL pointing to the file reference details in the storage system

required
minutes int

Time duration for validity of URL. Maximum 60 minutes.

60

Returns:

Type Description
str

The signed URL for downloading the file.

get_versions_custom_attributes

get_versions_custom_attributes(project_id, urns)

Batch-read custom attribute VALUES for document versions (BIM 360 / Forma).

POST .../bim360/docs/v1/projects/{project_id}/versions:batch-get with {"urns": [...]}. urns are version ids (or item ids → tip version). Returns the API results list; each entry has a urn and a customAttributes array of {id, type, name, value}. Only attributes that have been assigned a value are returned (unset ones are omitted — use get_folder_named_attributes for the full definition list). Chunks at the API's 50-urn limit. The "b." project-id prefix is stripped for the BIM 360 API.

patch_version_attributes

patch_version_attributes(project_id, version_id, attributes)

Update attributes for a document version using the Data Management API.

This method updates version-level attributes for a specific document version using the Autodesk Data Management API's PATCH endpoint.

Parameters:

Name Type Description Default
project_id str

The project ID in Data Management API format (no "b." prefix)

required
version_id str

The unique identifier for the document version.

required
attributes dict[str, Any]

Dict of attribute objects to update.

required

Returns:

Type Description
dict[str, Any]

A dictionary containing the updated document version

post_version_custom_attributes

post_version_custom_attributes(project_id, version_id, attributes)

Update custom attributes for a document version in BIM 360.

This method performs a batch update of custom attributes for a specific document version using the BIM 360 Docs API. It converts the Data Management API project ID format to the BIM 360 API format by removing the "b." prefix.

Parameters:

Name Type Description Default
project_id str

The project ID from either Data Management API or BIM 360 API. "b." prefix will be removed

required
version_id str

The unique identifier for the document version

required
attributes list[dict[str, Any]]

List of custom attribute objects to update. Each dictionary should contain attribute data

required

Returns:

Type Description
dict[str, Any]

A dictionary containing the updated custom attributes for the document

revoke

revoke()

Revoke the user's Autodesk refresh token.

Retrieves the stored refresh token from KeyVault and calls the Autodesk token revocation endpoint to invalidate it.

Returns:

Type Description
bool

True on successful revocation, False if no refresh token

bool

was found in KeyVault.

upload_file_bytes

upload_file_bytes(project_id, folder_id, file_name, file_bytes)

Upload file byte content to an Autodesk project folder. If a file with file_name already exists then will append file_bytes as a new item version.

Parameters:

Name Type Description Default
project_id str

The project ID in Data Management API format (no "b." prefix)

required
folder_id str

The folder ID

required
file_name str

The name of the file

required
file_bytes bytes

Contents of the file

required

Returns:

Type Description
dict[str, Any]

A dictionary containing upload information with the following keys:

  • object_id: The storage object ID
  • bucket: The bucket key
  • object_key: The object key
  • upload_status: Status of the upload (should be 'success')
  • action: Type of action performed ('new file' or 'new version')
  • version_id: The ID of the created version
  • version_number: The version number assigned
  • existing_item_id: The item ID if file already existed, None otherwise

Raises:

Type Description
Exception

If folder information cannot be retrieved

Exception

If storage location creation fails

Exception

If signed URL generation fails

Exception

If file upload to signed URL fails

Exception

If upload validation fails

Exception

If version/item creation fails

AzureBlobStorageClient

Wrapper around the Azure BlobServiceClient that implements additional specialist methods.

https://learn.microsoft.com/en-us/python/api/overview/azure/storage-blob-readme?view=azure-python

Attributes:

Name Type Description
organization str

Organization containing the workspace session to connect to. Must match an Azure Blob Storage container name.

workspace str

The workspace containing the session.

session_id str

The id of the session.

directory str

Default directory where new blobs will be created. Concatenation of workspace and session_id.

connection_string str

Credential for connecting to the Azure Blob Storage resource.

url_prefix str

The root URL to the Azure Blob Storage resource.

client BlobServiceClient

Blob service client. Use to access all sub methods.

__init__

__init__(organization, workspace, session_id, connection_string=None, url_prefix=None, connection_pool_maxsize=DEFAULT_CONNECTION_POOL_MAXSIZE)

Initialize client.

Parameters:

Name Type Description Default
organization str

Organization containing the workspace session to connect to. Must match an Azure Blob Storage container name.

required
workspace str

The workspace containing the session. Must match a folder in the parent container.

required
session_id str

The id of the session. Must match a folder in the parent workspace folder in the organization container.

required
connection_string str | None

Credential for connecting to the Azure Blob Storage resource. Defaults to environment variable BLOB_CONNECTION_STRING.

None
url_prefix str | None

The root URL to the Azure Blob Storage resource. Defaults to environment variable BLOB_URL_PREFIX.

None
connection_pool_maxsize int

Per-host HTTP connection-pool size for the underlying blob service client. Set this >= the concurrency of any parallel op run through this client so threads don't contend for the default 10-slot pool.

DEFAULT_CONNECTION_POOL_MAXSIZE

Raises:

Type Description
ValueError

If required parameters are not provided and environment variables are not set.

batch_download_blob_jsons

batch_download_blob_jsons(blob_names, max_workers=4, max_retries=3)

Download multiple JSON blobs in parallel with retry logic.

Parameters:

Name Type Description Default
blob_names list[str]

List of blob paths to download.

required
max_workers int

Number of parallel download threads (default 4 to avoid overwhelming Azure connections).

4
max_retries int

Maximum retry attempts per blob on transient failures.

3

Returns:

Type Description
dict[str, Any]

Dictionary mapping blob_name to parsed JSON content.

dict[str, Any]

Failed downloads are logged and excluded from results.

download_blob_json

download_blob_json(blob_name)

Used to download and access JSON files.

Parameters:

Name Type Description Default
blob_name str

The path to the blob in the container.

required

Returns:

Type Description
Any

The JSON content of the blob, likely a list or dictionary.

get_blob_client

get_blob_client(blob_name)

Return a client for interacting with blob objects.

Parameters:

Name Type Description Default
blob_name str

Name of the blob.

required

Returns:

Type Description
BlobClient

Blob client.

get_signed_url

get_signed_url(blob_name, minutes=720)

Generate a signed URL for an Azure Blob Storage object, valid for specified duration.

Parameters:

Name Type Description Default
blob_name str

Name of the blob.

required
minutes int

Time duration for validity of URL.

720

Returns:

Type Description
str

The signed URL.

index_blobs

index_blobs(all_blobs, document_ids)

Index a pre-fetched blob list by document ID. Pure, no I/O.

Lets callers that already hold a full LIST (e.g. Session.initialize) build the child-blob index without paying for a second LIST.

Parameters:

Name Type Description Default
all_blobs list[BlobProperties]

Blobs to index, typically from list_all_blobs().

required
document_ids list[str]

Document IDs (full path prefixes) to index against.

required

Returns:

Type Description
dict[str, list[BlobProperties]]

Dictionary mapping document ID to list of child BlobProperties.

dict[str, list[BlobProperties]]

Blobs that don't match any document ID are not included.

list_all_blobs

list_all_blobs(max_retries=3)

List every blob under the session directory in a single API call.

Resilient to transient listing failures (exponential back-off). Raises the last exception once retries are exhausted so callers can fail loud; an empty session simply returns an empty list (no exception).

Parameters:

Name Type Description Default
max_retries int

Maximum retry attempts on transient failures.

3

Returns:

Type Description
list[BlobProperties]

All blobs under {directory}/, unfiltered.

list_all_blobs_indexed

list_all_blobs_indexed(document_ids, max_retries=3)

List all blobs in the session directory and index them by document ID.

This method fetches all blobs in a single API call and builds an index mapping each document ID to its child blobs. This is much more efficient than calling list_blobs_with_prefix() for each document individually.

Parameters:

Name Type Description Default
document_ids list[str]

List of document IDs to build the index for. Each ID should be the full path prefix for that document.

required
max_retries int

Maximum retry attempts on transient failures.

3

Returns:

Type Description
dict[str, list[BlobProperties]]

Dictionary mapping document ID to list of child BlobProperties.

dict[str, list[BlobProperties]]

Blobs that don't match any document ID are not included. Returns an

dict[str, list[BlobProperties]]

empty index if listing fails after all retries (allows partial

dict[str, list[BlobProperties]]

processing for callers that prefer degradation over raising).

list_blobs_in_directory

list_blobs_in_directory(ignore_files=None)

Lists all blobs in the client directory within an Azure Blob container.

Parameters:

Name Type Description Default
ignore_files list[str] | None

Blob names containing any string in this list will not be included in the outputted list.

None

Returns:

Type Description
list[BlobProperties]

A list of blobs in the client directory.

list_blobs_with_prefix

list_blobs_with_prefix(prefix, ignore_files=None)

Lists all blobs in the Azure Blob container with the supplied prefix.

Parameters:

Name Type Description Default
prefix str

The prefix to search for

required
ignore_files list[str] | None

Blob names containing any string in this list will not be included in the outputted list

None

Returns:

Type Description
list[BlobProperties]

A list of blobs with the matching prefix.

upload_blob_json

upload_blob_json(blob_name, blob_content)

Used to upload JSON files to a blob in the container.

Parameters:

Name Type Description Default
blob_name str

The path to the blob in the container.

required
blob_content str

The JSON-formatted content to be uploaded.

required

Returns:

Type Description
dict[str, Any]

Blob updated property dictionary.

AzureOpenAIClient

Bases: OpenAISDKClient

A wrapper around the AzureOpenAi class.

https://github.com/openai/openai-python?tab=readme-ov-file#microsoft-azure-openai

Chat/embedding calls, retry logic and token-metric logging are provided by the shared OpenAISDKClient base; this class only resolves Azure-specific configuration and constructs the AzureOpenAI SDK client.

Attributes:

Name Type Description
api_key str

API key for Azure resource. If not provided will default to environment variable AZURE_FOUNDRY_API_KEY, then AZURE_OPENAI_API_KEY.

azure_endpoint str

Your Azure endpoint, including the resource, e.g. https://example-resource.azure.openai.com/. if not provided will default to environment variable AZURE_OPENAI_ENDPOINT.

api_version str

API version for Azure resource. If not provided will default to environment variable OPENAI_API_VERSION, then 2024-10-21.

model str | None

Model deployment name within the Azure resource. If not provided will default to environment variable AZURE_OPENAI_DEPLOYMENT.

response_format dict[Any, Any] | None

The type of response to request from the client. For example for JSON: { "type": "json_object" }.

client AzureOpenAI | OpenAI

The AzureOpenAI client. Can be used to access other sub methods.

__init__

__init__(api_key=None, azure_endpoint=None, api_version=None, model=None, response_format=None, temperature=0.1, max_connections=50, max_keepalive_connections=20, timeout=600.0)

Initialize the client with connection pooling configuration.

Parameters:

Name Type Description Default
api_key str | None

API key for Azure resource. If not provided will default to environment variable AZURE_FOUNDRY_API_KEY, then AZURE_OPENAI_API_KEY.

None
azure_endpoint str | None

Your Azure endpoint, including the resource, e.g. https://example-resource.azure.openai.com/. if not provided will default to environment variable AZURE_OPENAI_ENDPOINT.

None
api_version str | None

API version for Azure resource. If not provided will default to environment variable OPENAI_API_VERSION, then 2024-10-21.

None
model str | None

Model deployment name within the Azure resource. If not provided will default to environment variable AZURE_OPENAI_DEPLOYMENT.

None
response_format dict[Any, Any] | None

The type of response to request from the client. For example for JSON: { "type": "json_object" }.

None
temperature float | None

Sampling temperature for chat completions. Defaults to 0.1 — low enough for near-deterministic factual extraction, with a small amount of stochasticity retained. Pass None to defer to the API default. Pass a higher value (e.g. 0.7) per-instance for creativity-leaning operations such as description generation.

0.1
max_connections int

Maximum number of concurrent connections (default: 50).

50
max_keepalive_connections int

Maximum number of keepalive connections to maintain (default: 20).

20
timeout float

Read/write timeout in seconds (default: 600.0 / 10 minutes, matching OpenAI defaults).

600.0

BasicAuthProvider

Bases: WsgTokenProvider

HTTP Basic auth from a logical ProjectWise account's username/password.

Sends Authorization: Basic <base64(username:password)>. Example advertises a Basic realm on its datasource-scoped WWW-Authenticate (§3), so this is the probe for the logical-account route; whether the gateway accepts it still depends on the account being provisioned inside the datasource.

Credentials are held in memory and the header is recomputed on each call, so rotating them (by re-instantiating) is picked up without touching the client.

Parameters:

Name Type Description Default
username str

The logical ProjectWise account username.

required
password str

The account password.

required

EnvBearerTokenProvider

Bases: WsgBearerTokenProvider

PoC provider: returns a bearer token read from an environment variable.

A single token serves every WSG instance for the proof-of-concept. The value is read fresh on each call so a manually-refreshed token is picked up without re-instantiating the client.

Parameters:

Name Type Description Default
env_var str

Name of the environment variable holding the token. Defaults to PW_WSG_BEARER_TOKEN.

'PW_WSG_BEARER_TOKEN'

KeyVaultBasicAuthProvider

Bases: WsgTokenProvider

Basic auth for a logical PW account whose credentials live in Key Vault.

The production path. A {"username","password"} secret is written by the Hoppa web app at connection-config time and read here. The secret is fetched once via the shared :func:load_credential_blob and cached for this provider's lifetime (one session); the header itself is produced by delegating to :class:BasicAuthProvider, so there is no duplicated encoding logic.

The secret-name convention is a contract with the Hoppa web app (the writer): make_secret_name(organization, workspace, "projectwise", repository_id) — scoped per (tenant partition, datasource) so each datasource's logical account is isolated to its owning org/workspace. The credential source (Key Vault) is deliberately separate from the handshake (Basic): a future creds→token strategy would reuse the same fetch and only change what it does with the credentials.

Parameters:

Name Type Description Default
organization str

The Hoppa organization id (tenant partition).

required
workspace str

The Hoppa workspace id (tenant partition).

required
repository_id str

The WSG repository id identifying the datasource.

required

LanguageModelClient

Bases: Protocol

Structural interface every language-model client conforms to.

Callers depend only on this surface — never on provider-specific SDK objects. Both AzureOpenAIClient and OpenAICompatibleClient satisfy it (as does the test double MockAzureOpenAIClient).

call_chat

call_chat(messages, max_retries=5, max_completion_tokens=None)

Return the chat completion text for messages.

call_embedding

call_embedding(batch, max_retries=5)

Return one embedding vector per string in batch.

close

close()

Release any underlying connections.

ModelProvider

Bases: StrEnum

Supported language-model hosting providers.

OpenAICompatibleClient

Bases: OpenAISDKClient

A client for any OpenAI-compatible chat/embeddings endpoint.

Uses the plain openai.OpenAI SDK pointed at a custom base_url, which covers OpenAI directly, local runtimes such as Ollama (http://localhost:11434/v1) and vLLM, and most hosted open-model providers. Chat/embedding calls, retry logic and token-metric logging are inherited from the shared OpenAISDKClient base, so token counts land under the same counts.chat.* / counts.embeddings.* metric paths as the Azure client.

Attributes:

Name Type Description
api_key str

API key for the endpoint. Defaults to environment variable OPENAI_COMPATIBLE_API_KEY, then OPENAI_API_KEY. Many local runtimes ignore this; a placeholder is used if none is set.

base_url str

The endpoint base URL, e.g. http://localhost:11434/v1. Defaults to environment variable OPENAI_COMPATIBLE_BASE_URL.

model str | None

Model name to request. Defaults to environment variable OPENAI_COMPATIBLE_MODEL.

response_format dict[Any, Any] | None

The type of response to request. For example for JSON: { "type": "json_object" }.

client AzureOpenAI | OpenAI

The OpenAI SDK client.

__init__

__init__(api_key=None, base_url=None, model=None, response_format=None, temperature=0.1, timeout=600.0)

Initialize the OpenAI-compatible client.

Parameters:

Name Type Description Default
api_key str | None

API key for the endpoint. If not provided, defaults to OPENAI_COMPATIBLE_API_KEY, then OPENAI_API_KEY. If still unset a placeholder ("not-needed") is used, since many local runtimes (e.g. Ollama) do not require a key.

None
base_url str | None

The endpoint base URL, e.g. http://localhost:11434/v1. If not provided, defaults to OPENAI_COMPATIBLE_BASE_URL.

None
model str | None

Model name to request. If not provided, defaults to OPENAI_COMPATIBLE_MODEL.

None
response_format dict[Any, Any] | None

The type of response to request, e.g. {"type": "json_object"}.

None
temperature float | None

Sampling temperature. Pass None to defer to the endpoint default.

0.1
timeout float

Read/write timeout in seconds (default: 600.0 / 10 minutes).

600.0

ProjectWiseClient

Read access to one ProjectWise datasource via the WSG REST API.

Parameters:

Name Type Description Default
base_url str

WSG API root up to and including the version, e.g. https://example-pw-ws.bentley.com/ws/v2.9. A trailing slash is tolerated.

required
repository_id str

The WSG repository id, e.g. Bentley.PW--example-pw.bentley.com~3AEXAMPLE. Passed through verbatim — the ~3A (WSG's encoding of :) must NOT be URL-encoded again.

required
token_provider WsgTokenProvider

Supplies the Authorization header WSG accepts on demand (Bearer or Basic). The PoC uses :class:EnvBearerTokenProvider or :class:BasicAuthProvider; production swaps in an IMS exchange.

required
timeout int

Per-request timeout in seconds.

60

auth_headers

auth_headers()

Authorization header for an authenticated WSG request.

The provider owns the full header value (Bearer … or Basic …), so the client is auth-scheme agnostic. Also passed (as a callable) to AzureBlobDocumentVersion so the content-streaming fetch can authenticate $file — WSG requires the Authorization header and offers no self-authenticating URL on this datasource. Recomputed on every call so a refreshed token / rotated credential is picked up transparently.

build_web_url staticmethod

build_web_url(repository_id, instance_id)

Build the ProjectWise Web permalink from just a repository id + GUID.

Static (no client/credentials needed) so a metadata-only bind — where no authenticated client was stood up — can still produce the permalink.

ds is the repository id minus the Bentley.PW-- plugin prefix (~3A kept literal); doc is the document GUID with hyphens removed.

file_url

file_url(instance_id)

Return the $file content URL for a document.

The bytes stream through the gateway and the request must carry the bearer header (see :meth:auth_headers); there is no SAS/self-authenticating URL on this datasource (§8.4).

get_document

get_document(instance_id)

Fetch a single Document instance by GUID, or None if not found.

get_document_attributes

get_document_attributes(instance_id, doc=None, environment_id=None)

Return a document's custom/environment attributes as a flat dict.

Generic — nothing about the datasource is hardcoded. Resolves the document's EnvironmentId to its Env_<id>_* dynamic class at runtime, then reaches the per-document attribute instance by traversing the DocumentEnvironment relationship from the document. This is keyed by the document GUID (fast, and version-correct — the env instance's own id carries an unguessable _<n> suffix), and names the relationship explicitly to disambiguate it from DocumentDefaultEnvironment (WSG errors on the ambiguous form). Returns the related instance's properties, or an empty dict when the document has no environment.

Parameters:

Name Type Description Default
instance_id str

The document GUID.

required
doc dict | None

An already-fetched Document instance, to read EnvironmentId from without a re-fetch.

None
environment_id Any

The document's EnvironmentId if already known (e.g. from a config-blob snapshot) — avoids fetching the document.

None

get_documents

get_documents(instance_ids)

Fetch Document instances by GUID, batched into one query.

Uses the collection + $id in [...] filter form the ProjectWise Web UI uses (confirmed working on Example), rather than the per-instance path or the relationship-filter form (which 500s on this plugin — §6 #2). !poly includes derived classes.

get_dynamic_classes

get_dynamic_classes()

Cached list of PW_WSG_Dynamic class names (Env_*, PrType_*).

The Schema property on ECClassDef is the versioned name (e.g. PW_WSG_Dynamic.01.02), so it is matched by prefix, not eq.

get_instance

get_instance(schema, class_name, instance_id)

Fetch any single class instance by exact instance id, or None.

list_nav_children

list_nav_children(node_id=None)

List child nodes of a Navigation node (or the roots when None).

Each node's properties carry Key_ClassName (Project = folder, Document = doc) and Key_InstanceId (the real GUID for metadata / $file). HasChildren is unreliable (§6 #4) — don't gate recursion on it; attempt to descend any Project node within a depth bound.

list_repositories

list_repositories()

List the datasources (repositories) this token can see on the host.

Gateway-level call (no datasource session required), so it doubles as an auth/health probe and as the source for instance discovery. Each instance carries instanceId (the repository id) and a DisplayLabel.

query

query(schema, class_name, *, filter_expr=None, select=None, top=None, poly=False)

Run a scoped query against any schema/class; return instances.

Schema-agnostic primitive. filter_expr / select are raw OData $filter / $select values (e.g. "$id+in+['abc']" and "DocumentEnvironment-forward-PW_WSG_Dynamic.Env_104_EXAMPLE.*") — built into the URL so requests doesn't reshape the operators. Always scope queries (a folder, an id, …); never sweep a class unscoped (§6 #1).

web_url

web_url(instance_id)

Stable ProjectWise Web permalink that opens the document in a browser.

The pwlink app=webview form opens the document viewer directly and needs only the datasource + document GUID — no work-area/project ids — so it is built purely from data already held, with no network call. It is GUID/URN-based, so it survives the document being moved or renamed.

SharePointClient

Bases: BaseOAuthClient

Client for interacting with Microsoft Graph SharePoint API routes.

Attributes:

Name Type Description
user_id

Unique ID for the user tethered to the client. Used to scope delegated access.

client_id

Entra client ID. Defaults to environment variable ENTRA_CLIENT_ID.

access_token str

Three-legged OAuth token for calling Graph APIs.

__init__

__init__(user_id, access_token=None, client_id=os.getenv('ENTRA_CLIENT_ID'), client_secret=os.getenv('ENTRA_CLIENT_SECRET'))

Initialize the client.

Parameters:

Name Type Description Default
user_id str

Unique ID for the user tethered to the client. Used to scope delegated access.

required
access_token str | None

Three-legged OAuth token for calling Graph APIs.

None
client_id str | None

Entra client ID. Defaults to environment variable ENTRA_CLIENT_ID.

getenv('ENTRA_CLIENT_ID')
client_secret str | None

Entra client secret. Defaults to environment variable ENTRA_CLIENT_SECRET.

getenv('ENTRA_CLIENT_SECRET')

batch_get_download_urls

batch_get_download_urls(file_requests, max_workers=4)

Get multiple download URLs in parallel batch requests.

Parameters:

Name Type Description Default
file_requests list[dict[str, str]]

List of batch requests. Each request is a dictionary matching the schema in get_download_url()

required
max_workers int

Number of parallel threads to split requests across.

4

Returns:

Type Description
dict[str, str]

Key-value pairs of file_id and download_url.

batch_get_folder_contents

batch_get_folder_contents(items, max_workers=4)

Fetch children for many folders in as few Graph round-trips as possible.

Uses Microsoft Graph's $batch endpoint to bundle up to 20 sub-requests per HTTP call and dispatches chunks in parallel. Designed to back the bulk lazy-load endpoint on the Hoppa API, replacing thousands of sequential single-folder calls during tree expansion.

Parameters:

Name Type Description Default
items list[dict[str, Any]]

List of dicts. Each dict must contain site_id and drive_id. folder_id is optional; None or missing means "drive root".

required
max_workers int

Maximum number of parallel $batch POSTs.

4

Returns:

Type Description
list[dict[str, Any]]

List of dicts in the same length and order as items. Each output

list[dict[str, Any]]

dict contains:

  • site_id, drive_id, folder_id: echoed from input
  • children: shaped folder/file entries identical to build_folder_id_content's output (empty list on failure)
  • error: None on success, or {"status": int, "message": str} on failure

build_folder_id_content

build_folder_id_content(site_id, drive_id, folder_id=None)

Retrieves folder contents (non-recursive) from folder_id with pagination using pooled connection.

https://learn.microsoft.com/en-us/graph/api/driveitem-list-children?view=graph-rest-1.0&tabs=http

Parameters:

Name Type Description Default
site_id str

ID of the site

required
drive_id str

ID of the drive in the site

required
folder_id str | None

Folder ID, child item of drive with drive_id. If not provided then method will return the children of the drive.

None

Returns:

Type Description
list[dict[str, Any]]

List of children. Each child item has the following fields:

  • id (str): Unique Id of the drive item
  • name (str): Display name for the drive item
  • type (str): 'folder' or 'file'
  • siteId (str): ID of the site the item belongs to
  • driveId (str): ID of the parent drive
  • children: Placeholder list of children, ready for lazy loading in a UI table or tree component
  • Other attributes specific to the drive item type

build_folder_tree

build_folder_tree(site_id, drive_id, folder_path=None)

Recursively build folder tree.

Parameters:

Name Type Description Default
site_id str

ID of a SharePoint site

required
drive_id str

ID of a SharePoint drive in the site

required
folder_path str | None

Relative folder path from the drive root. If not provided then function will recurse from the drive root

None

Returns:

Type Description
list[dict[str, Any]]

Dictionary fields are:

  • id (str): ID of the drive item
  • name (str): Display name for the item
  • type (str): 'folder' or 'file'
  • children (List): List of children. Adopts the same schema.

build_tree

build_tree()

Build tree of all sites

Returns:

Type Description
list[dict[str, Any]]

Dictionary fields are:

  • id (str): ID of the SharePoint site
  • name (str): Display name
  • type (str): Always 'site'
  • children (List): An empty list, ready for lazy-loading when used in conjunction with `build_tree_site()

build_tree_site

build_tree_site(site_id)

Build tree for a specific site. Recurses through all drives and sub-folders to gather the full folder tree.

Parameters:

Name Type Description Default
site_id str

The ID of a SharePoint site

required

Returns:

Type Description
list[dict[str, Any]]

Dictionary fields are:

  • id (str): ID of the SharePoint drive
  • name (str): Display name
  • type (str): Always 'drive'
  • children (List): A list of child items. See build_folder_tree()

create_list_column

create_list_column(site_id, list_id, column_definition)

Create a new column on a SharePoint list.

https://learn.microsoft.com/en-us/graph/api/list-post-columns?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
list_id str

ID of the list.

required
column_definition dict[str, Any]

Column body as returned by metadata_property_to_column_definition.

required

Returns:

Type Description
dict[str, Any]

The created column resource (includes id).

create_url_generator

create_url_generator(site_id, drive_id, file_id)

Create a URL generator function that uses caching and connection pooling.

Returns:

Type Description
Callable

Calling this function will regenerate the download URL for a file. Passing it to a DocumentVersion constructor will ensure the file content remains accessible if the initial download_url expires.

delete_list_column

delete_list_column(site_id, list_id, column_id)

Delete a column from a SharePoint list.

https://learn.microsoft.com/en-us/graph/api/columndefinition-delete?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
list_id str

ID of the list.

required
column_id str

ID of the column to delete.

required

Returns:

Type Description
dict[str, Any]

Empty dict on success.

download_file

download_file(download_url, local_path, file_name)

Download file using pooled connection.

Parameters:

Name Type Description Default
download_url str

The direct download URL for the SharePoint file

required
local_path str

The local directory path where the file should be saved

required
file_name str

The name to use for the downloaded file

required

download_file_bytes

download_file_bytes(download_url)

Downloads a file and returns content as bytes using pooled connection.

Parameters:

Name Type Description Default
download_url str

The direct download URL for the file

required

Returns:

Type Description
bytes

The file content as bytes

Raises:

Type Description
RequestException

If the download fails

download_folder_content

download_folder_content(site_id, drive_id, folder_id, local_folder_path)

Recursively download folder contents to a local folder using pooled connection.

Parameters:

Name Type Description Default
site_id str

The ID of the SharePoint site containing the folder to download

required
drive_id str

The ID of the SharePoint drive containing the folder to download

required
folder_id str

The ID of the SharePoint folder to download

required
local_folder_path str

The local path to download files to

required

get_download_url

get_download_url(site_id, drive_id, file_id)

Get the download URL for a file.

Parameters:

Name Type Description Default
site_id str

ID of the site where the file is held

required
drive_id str

ID of the drivr where the file is held

required
file_id str

ID of the file

required

Returns:

Type Description
str | None

Download URL if file is found, else None.

get_drive_item_fields

get_drive_item_fields(site_id, drive_id, item_id)

Get the field values for a drive item's list item.

https://learn.microsoft.com/en-us/graph/api/listitem-get?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
drive_id str

ID of the drive.

required
item_id str

ID of the drive item.

required

Returns:

Type Description
dict[str, Any]

Field values dictionary.

get_drive_item_list_item

get_drive_item_list_item(site_id, drive_id, item_id)

Resolve a drive item to its underlying list item.

https://learn.microsoft.com/en-us/graph/api/listitem-get?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
drive_id str

ID of the drive.

required
item_id str

ID of the drive item.

required

Returns:

Type Description
dict[str, Any]

List item resource (includes id and fields).

get_drive_list

get_drive_list(site_id, drive_id)

Get the list backing a document library drive.

Bridges from drive_id to list_id, which is needed for column and list-item operations.

https://learn.microsoft.com/en-us/graph/api/drive-get?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
drive_id str

ID of the drive.

required

Returns:

Type Description
dict[str, Any]

List resource associated with the drive.

get_drives

get_drives(site_id)

Get drives for a site using pooled connection.

https://learn.microsoft.com/en-us/graph/api/drive-list?view=graph-rest-1.0&tabs=http

Parameters:

Name Type Description Default
site_id str

ID of the site

required

Returns:

Type Description
dict[str, Any]

List of drives can be accessed at the dictionary's value key.

get_folder_content

get_folder_content(site_id, drive_id, folder_path=None)

Get folder contents (non-recursive) from folder_path using pooled connection.

https://learn.microsoft.com/en-us/graph/api/driveitem-list-children?view=graph-rest-1.0&tabs=http

Parameters:

Name Type Description Default
site_id str

ID of the site

required
drive_id str

ID of the drive in the site

required
folder_path str | None

Folder path, relative to the drive root. If not provided then method will return the children of the drive.

None

Returns:

Type Description
list[dict[str, Any]]

List of children. Each child item has the following fields:

  • id (str): ID of the drive item
  • name (str): Name of the drive item
  • type (str): 'folder' or 'file'
  • mimeType (str): Content type of the item. Set as 'None' for folder drive items.

get_folder_id

get_folder_id(folder_contents, folder_name)

Extract folder ID from folder contents

get_list

get_list(site_id, list_id)

Get a specific list in a SharePoint site.

https://learn.microsoft.com/en-us/graph/api/list-get?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
list_id str

ID of the list.

required

Returns:

Type Description
dict[str, Any]

List resource.

get_list_columns

get_list_columns(site_id, list_id)

Get all columns defined on a SharePoint list.

https://learn.microsoft.com/en-us/graph/api/list-list-columns?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
list_id str

ID of the list.

required

Returns:

Type Description
dict[str, Any]

Column definitions can be accessed at the dictionary's value key.

get_lists

get_lists(site_id)

Get all lists in a SharePoint site.

https://learn.microsoft.com/en-us/graph/api/list-list?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required

Returns:

Type Description
dict[str, Any]

List of lists can be accessed at the dictionary's value key.

get_raw_response

get_raw_response(item_url, json=None, headers=None, method='GET')

General-purpose method for calling Microsoft Graph API using pooled connection.

Parameters:

Name Type Description Default
item_url str

URL to call

required
json dict | None

Body content

None
headers dict | None

HTTP request headers

None
method str

HTTP method.

'GET'

Returns:

Type Description
dict[str, Any]

JSON body, if present on the response

get_sites

get_sites()

Get all SharePoint sites using pooled connection.

https://learn.microsoft.com/en-us/graph/api/site-search?view=graph-rest-1.0&tabs=http

Returns:

Type Description
dict[str, Any]

List of sites can be accessed at the dictionary's value key.

update_list_column

update_list_column(site_id, list_id, column_id, column_update)

Update an existing column on a SharePoint list.

https://learn.microsoft.com/en-us/graph/api/columndefinition-update?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
list_id str

ID of the list.

required
column_id str

ID of the column to update.

required
column_update dict[str, Any]

Partial column body with fields to update.

required

Returns:

Type Description
dict[str, Any]

The updated column resource.

update_list_item_fields

update_list_item_fields(site_id, list_id, item_id, fields)

Update field values on a list item.

https://learn.microsoft.com/en-us/graph/api/listitem-update?view=graph-rest-1.0

Parameters:

Name Type Description Default
site_id str

ID of the site.

required
list_id str

ID of the list.

required
item_id str

ID of the list item (not the drive item).

required
fields dict[str, Any]

Dictionary of field internal names → values.

required

Returns:

Type Description
dict[str, Any]

Updated fields dictionary.

WsgBearerTokenProvider

Bases: WsgTokenProvider

Base for bearer-token strategies: supply a token, get Bearer framing.

Subclasses implement :meth:get_token; this class wraps it as Authorization: Bearer <token>. Re-reads the token on every call so a refreshed token is picked up transparently.

get_token abstractmethod

get_token()

Return a currently-valid WSG bearer token (no Bearer prefix).

WsgTokenProvider

Bases: ABC

Single seam for authorizing a WSG request.

Implementations return the full Authorization header value — scheme included ("Bearer <token>", "Basic <base64>", …) — which the client sets verbatim.

authorization abstractmethod

authorization()

Return the full Authorization header value (scheme + credential).

create_language_model_client

create_language_model_client(provider=None, **config)

Construct a language-model client for the given provider.

Parameters:

Name Type Description Default
provider ModelProvider | str | None

ModelProvider (or its string value). If omitted, defaults to the MODEL_PROVIDER environment variable, then Azure OpenAI. This preserves existing behaviour: with no provider set, calls go to Azure exactly as before.

None
**config Any

Connection/config kwargs. Keys not relevant to the selected provider are ignored, so a single loosely-populated config dict can drive any provider. Azure keys: api_key, azure_endpoint, api_version, model, response_format, temperature, timeout. OpenAI-compatible keys: api_key, base_url, model, response_format, temperature, timeout.

{}

Returns:

Type Description
LanguageModelClient

A concrete client satisfying LanguageModelClient.

Raises:

Type Description
ValueError

If provider is unrecognised, or required config is missing.