Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,59 @@

All notable changes to this project will be documented in this file.

## [2.0.2] - 2026-05-06
### Added
- Dict context support for Conditional Data Access.

## [2.0.1] - 2026-04-29
### Fixed
- Fern client re-initialisation on token refresh.

## [2.0.0] - 2025-11-11
### Added
- Multi-vault and multi-connection support via fluent builder (`Skyflow.builder()`).
- New typed request and response classes for all vault operations (`InsertRequest`, `GetRequest`, `UpdateRequest`, `DeleteRequest`, `QueryRequest`, `DetokenizeRequest`, `TokenizeRequest`, `FileUploadRequest`).
- Detect API: `deidentify_text`, `reidentify_text`, `deidentify_file`, and `get_detect_run`.
- File upload support via `vault().upload_file()`.
- Flexible credential types: API key, static bearer token, service account credentials string, credentials file path, and `SKYFLOW_CREDENTIALS` environment variable.
- `SkyflowError` now includes `http_code`, `grpc_code`, `http_status`, `request_id`, and `details` fields.
- `set_log_level()` on the client for runtime log level changes.

### Changed
- Complete rewrite of the SDK public API. See [docs/migrate_to_v2.md](docs/migrate_to_v2.md) for migration instructions.

## [1.16.0] - 2025-09-23
### Fixed
- Remote disconnect error in vault operations.

## [1.15.8] - 2025-09-30
### Fixed
- Retry logic when `continue_on_error` is set to `true` in insert.

## [1.15.7] - 2025-09-23
### Fixed
- Retry handling for errors in insert method.

## [1.15.6] - 2025-09-22
### Fixed
- Added retry logic for transient errors.

## [1.15.5] - 2025-09-18
### Fixed
- Remote disconnected errors in vault operations.

## [1.15.4] - 2025-09-12
### Fixed
- Retry on exception during vault requests.

## [1.15.3] - 2025-09-12
### Fixed
- Retry on exception during vault requests.

## [1.15.2] - 2025-09-12
### Fixed
- Retry on connection error in insert method.

## [1.15.1] - 2023-12-07
## Fixed
- Not receiving tokens when calling Get with options tokens as true.
Expand Down
15 changes: 15 additions & 0 deletions skyflow/client/skyflow.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import warnings
from collections import OrderedDict
from typing_extensions import deprecated
from skyflow import LogLevel
from skyflow.error import SkyflowError
from skyflow.utils import SkyflowMessages
Expand Down Expand Up @@ -59,6 +61,15 @@ def set_log_level(self, log_level):
self.__builder._Builder__set_log_level(log_level)
return self

@deprecated("[DEPRECATED] Use set_log_level() instead.")
def update_log_level(self, log_level):
warnings.warn(
SkyflowMessages.Warning.UPDATE_LOG_LEVEL_DEPRECATED.value,
DeprecationWarning,
stacklevel=2,
)
return self.set_log_level(log_level)

def get_log_level(self):
return self.__builder._Builder__log_level

Expand Down Expand Up @@ -111,6 +122,8 @@ def remove_vault_config(self, vault_id):
def update_vault_config(self, config):
validate_update_vault_config(self.__logger, config)
vault_id = config.get(OptionField.VAULT_ID)
if vault_id not in self.__vault_configs:
raise SkyflowError(SkyflowMessages.Error.VAULT_ID_NOT_IN_CONFIG_LIST.value.format(vault_id), SkyflowMessages.ErrorCodes.INVALID_INPUT.value)
vault_config = self.__vault_configs[vault_id]
vault_config.get(OptionField.VAULT_CLIENT).update_config(config)

Expand Down Expand Up @@ -152,6 +165,8 @@ def remove_connection_config(self, connection_id):
def update_connection_config(self, config):
validate_update_connection_config(self.__logger, config)
connection_id = config[OptionField.CONNECTION_ID]
if connection_id not in self.__connection_configs:
raise SkyflowError(SkyflowMessages.Error.CONNECTION_ID_NOT_IN_CONFIG_LIST.value.format(connection_id), SkyflowMessages.ErrorCodes.INVALID_INPUT.value)
connection_config = self.__connection_configs[connection_id]
connection_config.get(OptionField.VAULT_CLIENT).update_config(config)

Expand Down
4 changes: 2 additions & 2 deletions skyflow/error/_skyflow_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ def __init__(self,
request_id = None,
grpc_code = None,
http_status = None,
details = []):
details = None):
self.message = message
self.http_code = http_code
self.grpc_code = grpc_code
self.http_status = http_status if http_status else SkyflowMessages.HttpStatus.BAD_REQUEST.value
self.details = details
self.details = details if details else None
self.request_id = request_id
super().__init__(message)
31 changes: 17 additions & 14 deletions skyflow/service_account/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,16 @@ def is_expired(token, logger = None):
def generate_bearer_token(credentials_file_path, options = None, logger = None):
log_info(SkyflowMessages.Info.GET_BEARER_TOKEN_TRIGGERED.value, logger)
try:
credentials_file = open(credentials_file_path, 'r')
with open(credentials_file_path, 'r') as credentials_file:
try:
credentials = json.load(credentials_file)
except Exception:
log_error_log(SkyflowMessages.ErrorLogs.INVALID_CREDENTIALS_FILE.value, logger=logger)
raise SkyflowError(SkyflowMessages.Error.FILE_INVALID_JSON.value.format(credentials_file_path), invalid_input_error_code)
except SkyflowError:
raise
except Exception:
raise SkyflowError(SkyflowMessages.Error.INVALID_CREDENTIAL_FILE_PATH.value, invalid_input_error_code)
with credentials_file:
try:
credentials = json.load(credentials_file)
except Exception:
log_error_log(SkyflowMessages.ErrorLogs.INVALID_CREDENTIALS_FILE.value, logger=logger)
raise SkyflowError(SkyflowMessages.Error.FILE_INVALID_JSON.value.format(credentials_file_path), invalid_input_error_code)
result = get_service_account_token(credentials, options, logger)
return result

Expand Down Expand Up @@ -179,6 +180,7 @@ def get_signed_jwt(options, client_id, key_id, token_uri, private_key, logger):


def get_signed_tokens(credentials_obj, options):
options = options if options is not None else {}
credentials_obj = _normalize_credentials(credentials_obj)
expiry_time = int(time.time()) + options.get(OptionField.TIME_TO_LIVE, 60)
prefix = JWT.SIGNED_TOKEN_PREFIX
Expand Down Expand Up @@ -218,15 +220,16 @@ def get_signed_tokens(credentials_obj, options):
def generate_signed_data_tokens(credentials_file_path, options):
log_info(SkyflowMessages.Info.GET_SIGNED_DATA_TOKENS_TRIGGERED.value)
try:
credentials_file = open(credentials_file_path, 'r')
with open(credentials_file_path, 'r') as credentials_file:
try:
credentials = json.load(credentials_file)
except Exception:
raise SkyflowError(SkyflowMessages.Error.FILE_INVALID_JSON.value.format(credentials_file_path),
invalid_input_error_code)
except SkyflowError:
raise
except Exception:
raise SkyflowError(SkyflowMessages.Error.INVALID_CREDENTIAL_FILE_PATH.value, invalid_input_error_code)
with credentials_file:
try:
credentials = json.load(credentials_file)
except Exception:
raise SkyflowError(SkyflowMessages.Error.FILE_INVALID_JSON.value.format(credentials_file_path),
invalid_input_error_code)
return get_signed_tokens(credentials, options)

def generate_signed_data_tokens_from_creds(credentials, options):
Expand Down
19 changes: 15 additions & 4 deletions skyflow/utils/_skyflow_messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ class Error(Enum):
INVALID_CONTINUE_ON_ERROR_TYPE = f"{error_prefix} Validation error. Invalid type of continue on error. Specify continue on error as a boolean."
TOKENS_PASSED_FOR_TOKEN_MODE_DISABLE = f"{error_prefix} Validation error. 'token_mode' wasn't specified. Set 'token_mode' to 'ENABLE' to insert tokens."
INSUFFICIENT_TOKENS_PASSED_FOR_TOKEN_MODE_ENABLE_STRICT = f"{error_prefix} Validation error. 'token_mode' is set to 'ENABLE_STRICT', but some fields are missing tokens. Specify tokens for all fields."
NO_TOKENS_IN_INSERT = f"{error_prefix} Validation error. Tokens weren't specified for records while 'token_strict' was {{}}. Specify tokens."
MISMATCH_OF_FIELDS_AND_TOKENS = f"{error_prefix} Validation error. Keys for values and tokens are not matching. Ensure each values entry and its corresponding tokens entry have the same keys."
NO_TOKENS_IN_INSERT = f"{error_prefix} Validation error. Tokens weren't specified for records while 'token_mode' was {{}}. Specify tokens."
BATCH_INSERT_FAILURE = f"{error_prefix} Insert operation failed."
GET_FAILURE = f"{error_prefix} Get operation failed."
HOMOGENOUS_NOT_SUPPORTED_WITH_UPSERT = f"{error_prefix} Validation error. Homogenous is not supported when upsert is passed."
Expand Down Expand Up @@ -317,6 +318,8 @@ class Info(Enum):
DETECT_REQUEST_RESOLVED = f"{INFO}: [{error_prefix}] Detect request is resolved."

class ErrorLogs(Enum):
INVALID_LOG_LEVEL = f"{ERROR}: [{error_prefix}] Invalid log level. Specify a valid log level."
INVALID_KEY = f"{ERROR}: [{error_prefix}] Invalid key {{}} in config."
VAULTID_IS_REQUIRED = f"{ERROR}: [{error_prefix}] Invalid vault config. Vault ID is required."
EMPTY_VAULTID = f"{ERROR}: [{error_prefix}] Invalid vault config. Vault ID can not be empty."
CLUSTER_ID_IS_REQUIRED = f"{ERROR}: [{error_prefix}] Invalid vault config. Cluster ID is required."
Expand Down Expand Up @@ -363,8 +366,8 @@ class ErrorLogs(Enum):
EMPTY_OR_NULL_ID_IN_IDS = f"{ERROR}: [{error_prefix}] Invalid {{}} request. Id can not be null or empty in ids at index {{}}."
TOKENIZATION_NOT_SUPPORTED_WITH_REDACTION= f"{ERROR}: [{error_prefix}] Invalid {{}} request. Tokenization is not supported when redaction is applied."
TOKENIZATION_SUPPORTED_ONLY_WITH_IDS=f"{ERROR}: [{error_prefix}] Invalid {{}} request. Tokenization is not supported when column name and values are passed."
TOKENS_NOT_ALLOWED_WITH_BYOT_DISABLE = f"{ERROR}: [{error_prefix}] Invalid {{}} request. Tokens are not allowed when token_strict is DISABLE."
INSUFFICIENT_TOKENS_PASSED_FOR_BYOT_ENABLE_STRICT =f"{ERROR}: [{error_prefix}] Invalid {{}} request. For tokenStrict as ENABLE_STRICT, tokens should be passed for all fields."
TOKENS_NOT_ALLOWED_WITH_BYOT_DISABLE = f"{ERROR}: [{error_prefix}] Invalid {{}} request. Tokens are not allowed when token_mode is DISABLE."
INSUFFICIENT_TOKENS_PASSED_FOR_BYOT_ENABLE_STRICT =f"{ERROR}: [{error_prefix}] Invalid {{}} request. For token_mode as ENABLE_STRICT, tokens should be passed for all fields."
TOKENS_REQUIRED = f"{ERROR}: [{error_prefix}] Invalid {{}} request. Tokens are required."
EMPTY_FIELDS = f"{ERROR}: [{error_prefix}] Invalid {{}} request. Fields can not be empty."
EMPTY_OFFSET = f"{ERROR}: [{error_prefix}] Invalid {{}} request. Offset ca not be empty."
Expand Down Expand Up @@ -413,7 +416,15 @@ class HttpStatus(Enum):
BAD_REQUEST = "Bad Request"

class Warning(Enum):
WARNING_MESSAGE = "WARNING MESSAGE"
UPDATE_LOG_LEVEL_DEPRECATED = (
"[DEPRECATED] Skyflow.update_log_level() is deprecated. "
"Use Skyflow.set_log_level() instead — identical behavior."
)
FILE_UPLOAD_REQUEST_ARG_ORDER_DEPRECATED = (
"[DEPRECATED] FileUploadRequest: argument order changed. "
"Old positional order: (table, skyflow_id, column_name). "
"New order: FileUploadRequest(table, column_name=..., skyflow_id=...)."
)



Loading
Loading