SDK Reference
Client
Section titled “Client”Low-level access to the Komodo Platform HTTP APIs (IAM, etc.). get_snowflake_connection() constructs a Client internally; use Client directly when you need client.iam or other generated API wrappers.
Import
Section titled “Import”from komodo import ClientConstructor
Section titled “Constructor”def __init__( self, account_id: str | None = None, auth_session: Session | None = None, **kwargs,) -> Noneaccount_id— Komodo account UUID. If omitted, taken from the session / credentials when available.auth_session— Optional KomodoSession(komodo.auth.Session). If omitted, a defaultSession()is created andconnect()is called.
Notable members
Section titled “Notable members”| Member | Description |
|---|---|
auth | Active Session (tokens, refresh). |
iam | Generated IAM API client (IamApi). |
marmot | Marmot Analysis API client (MarmotApi). |
x_account_id | Resolved account ID for API calls. |
komodo_api_client() | Underlying OpenAPI ApiClient. |
Async context manager
Section titled “Async context manager”classmethod async def create(...) -> Clientasync def close()async def __aenter__ / __aexit__Use async with await Client.create(...) as client: when you need explicit cleanup of the HTTP session.
get_snowflake_connection
Section titled “get_snowflake_connection”Returns a DB-API 2.0 Connection to your account’s Snowflake warehouse through the Marmot Development Kit, with query tags applied for observability.
Import
Section titled “Import”from komodo import get_snowflake_connectionSignature
Section titled “Signature”def get_snowflake_connection( account_id: str | None = None, jwt: str | None = None, client_id: str | None = None, client_secret: str | None = None, profile: str | None = None, use_main_account: bool = False, app_id: str | None = None, query_tag_extra: Mapping[str, Any] | None = None,) -> ConnectionParameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
account_id | str | None | Komodo account UUID. Required unless the session / profile supplies it. Cannot be combined with profile if the profile already defines an account. |
jwt | str | None | Bearer token for user-style auth. Cannot be combined with client_id / client_secret. |
client_id | str | None | Service principal client ID (M2M). Must be used with client_secret and account_id (unless profile supplies them). |
client_secret | str | None | Service principal secret (M2M). |
profile | str | None | Named section in the credentials file (M2M + account). Mutually exclusive with passing jwt or split client_id/client_secret in the same call. |
use_main_account | bool | Route to the shared main Snowflake account instead of the caller’s tenant. Cannot be combined with account_id. |
app_id | str | None | Overrides the app id reported to the gateway. Defaults to $KOMODO_APP_ID or kdk. |
query_tag_extra | Mapping[str, Any] | None | Optional caller metadata shallow-merged into the structured Snowflake QUERY_TAG under client.properties (e.g. a request or job id, for attribution in query history). Keys and values must be JSON-serializable. Must not contain secrets or PHI — the tag is stored in Snowflake’s query history. |
Returns
Section titled “Returns”Connection — DB-API 2.0 connection (Komodo Snowflake proxy). Use conn.cursor() to obtain a KomodoSnowflakeCursor.
Raises
Section titled “Raises”| Exception | When |
|---|---|
ValueError | Invalid combination of arguments (e.g. JWT + client secret, or profile with explicit creds), or a non-JSON-serializable query_tag_extra. |
UnsetAccountError | No account_id and none could be resolved from the session / file. |
Example
Section titled “Example”from komodo import get_snowflake_connection
conn = get_snowflake_connection()cur = conn.cursor()cur.execute("SELECT 1")print(cur.fetchone())conn.close()Attaching query-tag metadata
Section titled “Attaching query-tag metadata”# Tag every query on this connection with caller context for attribution# in Snowflake query history. Values must be JSON-serializable; no secrets/PHI.conn = get_snowflake_connection(query_tag_extra={"request_id": "abc-123", "job": "nightly-refresh"})execute_query_async
Section titled “execute_query_async”Async helper: execute_async on the cursor, poll until the query finishes, then attach results to the cursor and return that cursor.
Import
Section titled “Import”from komodo import execute_query_asyncSignature
Section titled “Signature”async def execute_query_async( cursor: KomodoSnowflakeCursor, query: str, query_params: list[str] | dict[str, str] | None = None,) -> KomodoSnowflakeCursorParameters
Section titled “Parameters”| Name | Description |
|---|---|
cursor | Open cursor from get_snowflake_connection().cursor(). |
query | SQL string. |
query_params | Optional bind parameters for the query. |
Returns
Section titled “Returns”The same cursor after results are loaded — call fetchall(), fetch_pandas_all(), etc.
Example
Section titled “Example”import asynciofrom komodo import get_snowflake_connection, execute_query_async
async def main(): conn = get_snowflake_connection() cur = conn.cursor() cur = await execute_query_async(cur, "SELECT CURRENT_TIMESTAMP()") print(cur.fetchone()) conn.close()
asyncio.run(main())get_query_diagnostics
Section titled “get_query_diagnostics”Fetch diagnostic detail for a previously executed query so you can self-diagnose failures: the error code/message, execution status, warehouse, and timing Snowflake recorded.
Import
Section titled “Import”from komodo import get_query_diagnosticsSignature
Section titled “Signature”def get_query_diagnostics( connection: Connection, query_id: str,) -> dict[str, Any] | NoneParameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
connection | Connection | An open connection from get_snowflake_connection(). |
query_id | str | The Snowflake query id (cursor.sfqid, also shown as Query ID: in surfaced errors). |
Returns
Section titled “Returns”A dict of the recorded fields (QUERY_ID, QUERY_TEXT, EXECUTION_STATUS, ERROR_CODE, ERROR_MESSAGE, WAREHOUSE_NAME, START_TIME, END_TIME, TOTAL_ELAPSED_TIME), or None if no matching query is found.
Backed by Snowflake’s INFORMATION_SCHEMA.QUERY_HISTORY table function, scoped to the connection’s role and account. A current database must be set (e.g. USE DATABASE DATA) for INFORMATION_SCHEMA to resolve, and the query must be within the history retention window.
Example
Section titled “Example”from komodo import get_snowflake_connection, get_query_diagnostics
conn = get_snowflake_connection()cur = conn.cursor()cur.execute("USE DATABASE DATA")try: cur.execute("SELECT * FROM SOME_SECURE_VIEW")except Exception as err: print(err) print(get_query_diagnostics(conn, cur.sfqid))conn.close()is_still_running
Section titled “is_still_running”def is_still_running(map_connection: Connection, query_id: str) -> boolReturns whether a given Snowflake query id is still running. Used internally by execute_query_async; may raise SnowflakeException if status checks fail.
Snowflake-related exceptions
Section titled “Snowflake-related exceptions”| Name | Module | Description |
|---|---|---|
UnsetAccountError | komodo.snowflake | Missing account context when opening a connection. |
SnowflakeException | komodo.snowflake | Wrapped errors around query status / Map connection operations. |
from komodo.snowflake import UnsetAccountError, SnowflakeExceptionUnsetAccountError is also available from the top-level komodo package (from komodo import UnsetAccountError).
OpenAPI / REST exceptions
Section titled “OpenAPI / REST exceptions”Generated clients raise subclasses of OpenApiException (for example ApiException, ApiValueError). Import from komodo:
from komodo import ApiException, ApiValueError, OpenApiExceptionCursor operations
Section titled “Cursor operations”After cursor = conn.cursor():
| Method / attribute | Typical use |
|---|---|
execute(sql, params=None) | Run SQL. |
fetchone, fetchmany, fetchall | DB-API results. |
fetch_pandas_all, fetch_pandas_batches | Pandas DataFrames (Snowflake cursor). |
execute_async, get_results_from_sfqid | Advanced / used by execute_query_async. |
description | Column metadata after a select. |
For SQL and CLI patterns, see Executing Queries and Pandas Integration.
SecretsApi
Section titled “SecretsApi”Programmatic access to the Komodo Secrets Service for storing, reading, updating,
and deleting secrets. Secrets can be scoped to a user, an application, or shared
across the organization. Every synchronous method has an _async counterpart
(e.g. create_secret_async) for use in async runtimes.
Import
Section titled “Import”from komodo.secrets_api import SecretsApi, SecretTypeSecretType
Section titled “SecretType”Enum that determines the scope/namespace of a secret.
class SecretType(str, Enum): USER = "user" APPLICATION = "application" SHARED = "shared"| Value | Description |
|---|---|
SecretType.USER | Personal secrets tied to the authenticated user |
SecretType.APPLICATION | Application-scoped secrets (requires app_id) |
SecretType.SHARED | Organization-wide secrets accessible by all account members |
Class Signature
Section titled “Class Signature”class SecretsApi: def __init__( self, api_client: Optional[ApiClient] = None, session: Optional[Session] = None, environment: Optional[str] = None, access_token: Optional[str] = None, account_id: Optional[str] = None, )| Parameter | Type | Description |
|---|---|---|
api_client | ApiClient | Komodo API client instance. Uses the default client if omitted. |
session | Session | Komodo auth session. Creates a new session if omitted. |
environment | str | "production". Defaults to KOMODO_ENVIRONMENT. |
access_token | str | Override token (useful in MCP/service contexts). |
account_id | str | Override account ID. |
Methods
Section titled “Methods”create_secret / create_secret_async
Section titled “create_secret / create_secret_async”Store a new secret.
def create_secret( self, secret_type: SecretType, secret_path: str, data: Dict[str, Any], metadata: Optional[Dict[str, str]] = None, app_id: Optional[str] = None, platform: bool = False,) -> Union[SecretResponse, PlatformSecretResponse]| Parameter | Type | Description |
|---|---|---|
secret_type | SecretType | Scope of the secret |
secret_path | str | Path for the secret (e.g. "my-api-key") |
data | dict | Key-value pairs to store |
metadata | dict | Optional metadata |
app_id | str | Required when secret_type is APPLICATION |
platform | bool | Use the platform secrets endpoint (advanced; default False) |
read_secret / read_secret_async
Section titled “read_secret / read_secret_async”Read a secret’s value and metadata.
def read_secret( self, secret_type: SecretType, secret_path: str, app_id: Optional[str] = None, platform: bool = False,) -> Union[SecretResponse, PlatformSecretResponse]The returned SecretResponse has .data (dict), .metadata (dict or None),
.success (bool), and .message (str).
list_secrets / list_secrets_async
Section titled “list_secrets / list_secrets_async”List secret paths within a scope.
def list_secrets( self, secret_type: SecretType, prefix: Optional[str] = None, app_id: Optional[str] = None, platform: bool = False, next_token: Optional[str] = None,) -> Union[SecretListResponse, PlatformSecretListResponse]| Parameter | Type | Description |
|---|---|---|
prefix | str | Filter to paths starting with this prefix |
next_token | str | Pagination token from a previous response |
The returned SecretListResponse has .secrets (list), .has_more (bool),
and .next_token (str or None).
update_secret / update_secret_async
Section titled “update_secret / update_secret_async”Replace a secret’s data. Creates a new version.
def update_secret( self, secret_type: SecretType, secret_path: str, data: Dict[str, Any], metadata: Optional[Dict[str, str]] = None, app_id: Optional[str] = None, platform: bool = False,) -> Union[SecretResponse, PlatformSecretResponse]delete_secret / delete_secret_async
Section titled “delete_secret / delete_secret_async”Permanently remove a secret.
def delete_secret( self, secret_type: SecretType, secret_path: str, app_id: Optional[str] = None, platform: bool = False,) -> Dict[str, Any]move_secret / move_secret_async
Section titled “move_secret / move_secret_async”Copy a secret from one scope to another, optionally deleting the original.
def move_secret( self, source_type: SecretType, source_path: str, dest_type: SecretType, dest_path: str, source_app_id: Optional[str] = None, dest_app_id: Optional[str] = None, delete_original: bool = False,) -> SecretResponseExample
Section titled “Example”from komodo.secrets_api import SecretsApi, SecretType
api = SecretsApi(environment="production")
# Create a user secretapi.create_secret( secret_type=SecretType.USER, secret_path="my-api-key", data={"key": "abc123"},)
# List all user secretsresult = api.list_secrets(SecretType.USER)for path in result.secrets: print(path)
# Read a secretsecret = api.read_secret(SecretType.USER, "my-api-key")print(secret.data) # {"key": "abc123"}
# Update a secretapi.update_secret( SecretType.USER, "my-api-key", data={"key": "new-value"},)
# Create an app-scoped secretapi.create_secret( secret_type=SecretType.APPLICATION, secret_path="db-password", data={"password": "s3cret"}, app_id="4cb8894f-90ff-43a8-beba-451f2da3033a",)
# Delete a secretapi.delete_secret(SecretType.USER, "my-api-key")Async Example
Section titled “Async Example”import asynciofrom komodo.secrets_api import SecretsApi, SecretType
async def main(): api = SecretsApi(environment="production")
await api.create_secret_async( secret_type=SecretType.USER, secret_path="async-key", data={"token": "xyz"}, )
secret = await api.read_secret_async(SecretType.USER, "async-key") print(secret.data)
asyncio.run(main())MarmotApi
Section titled “MarmotApi”Client for the Marmot Analysis API: create analyses, chat inside them, and read
the files Marmot produces while working (queries, cohort definitions, reports).
This is the programmatic counterpart of komodo marmot -c. Available on the
SDK client as client.marmot, or standalone.
An analysis is Marmot’s unit of work: it owns a file tree and one or more conversations. A chat turn is: create an analysis, create a conversation with an initial message, wait for processing to finish, then read the messages back. Follow-up messages go to the same conversation and keep full context.
Requires a Marmot subscription on the account; calls fail with MarmotApiError
(HTTP 401/403) when the account is not entitled.
Import
Section titled “Import”from komodo.marmot_api import MarmotApi, MarmotApiError, MarmotConnectionErrorClass Signature
Section titled “Class Signature”class MarmotApi: def __init__( self, session: Session | None = None, access_token: str | None = None, account_id: str | None = None, environment: str | None = None, base_url: str | None = None, extra_headers: dict[str, str] | None = None, )| Parameter | Type | Description |
|---|---|---|
session | Session | None | Komodo auth session. When neither session nor access_token is given, a default Session() is created and connected. |
access_token | str | None | Bearer token override (useful in service contexts). |
account_id | str | None | Komodo account UUID. Resolved from the session if omitted. |
environment | str | None | "production" (default). Selects the Marmot URL for the environment. |
base_url | str | None | Explicit Marmot URL override (e.g. a staging host). |
extra_headers | dict[str, str] | None | Additional headers sent with every request. |
Methods
Section titled “Methods”create_analysis
Section titled “create_analysis”Create a new analysis. Returns the analysis record; keep id for all further calls.
def create_analysis(self, title: str, description: str | None = None) -> dictcreate_conversation
Section titled “create_conversation”Start a conversation inside an analysis. Passing initial_message sends the
first user message immediately, so you can go straight to wait_until_idle.
def create_conversation( self, analysis_id: str, title: str | None = None, initial_message: str | None = None, agent_id: int | None = None,) -> dictsend_message
Section titled “send_message”Send a follow-up user message to an existing conversation. Sending while the conversation is still processing is rejected by the server (HTTP 409) — wait for the previous turn to finish first.
def send_message(self, analysis_id: str, conversation_id: str, content: str) -> dictget_status / wait_until_idle
Section titled “get_status / wait_until_idle”get_status returns the conversation status payload. wait_until_idle polls
until the status leaves the processing states (processing, waiting) and
returns the final status — typically idle; error and interrupted are also
terminal for a turn.
def get_status(self, analysis_id: str, conversation_id: str) -> dict
def wait_until_idle( self, analysis_id: str, conversation_id: str, timeout_seconds: float = 300.0, poll_interval: float = 2.0,) -> strget_messages
Section titled “get_messages”Return the conversation’s messages. Each message has a role and a content
list of blocks; render the text blocks of role: "assistant" messages for a
plain-text reply.
def get_messages(self, analysis_id: str, conversation_id: str) -> list[dict]list_files / get_file
Section titled “list_files / get_file”Read the analysis’s file tree. list_files returns the manifest — filename,
size, and a short description per file, with small files’ content inlined.
get_file returns one file with its full content (base64-encoded when
binary, with content_type set); version_sha pins a historical version.
def list_files(self, analysis_id: str, prefix: str | None = None) -> dict
def get_file(self, analysis_id: str, filename: str, version_sha: str | None = None) -> dicthas_access
Section titled “has_access”Whether the account has the Marmot entitlement (checked against the Komodo subscription service). Optional preflight: the Marmot API enforces entitlement on every call regardless — use this when you want a clear yes/no (for example to decide whether to surface Marmot features at all) instead of handling an HTTP error.
def has_access(self) -> boolRaises
Section titled “Raises”| Exception | When |
|---|---|
MarmotApiError | Marmot returned a non-success response. Carries status_code, detail, and operation. |
MarmotConnectionError | Network-level failure reaching Marmot, or the entitlement check could not complete. |
TimeoutError | wait_until_idle exceeded timeout_seconds. |
Example
Section titled “Example”from komodo import Clientfrom komodo.marmot_api import MarmotApiError
client = Client()marmot = client.marmot
try: analysis = marmot.create_analysis("Q3 oncology cohort review") conversation = marmot.create_conversation( analysis["id"], initial_message="Pull the top 10 diagnosis codes in this cohort by patient count.", )
marmot.wait_until_idle(analysis["id"], conversation["id"])
for message in marmot.get_messages(analysis["id"], conversation["id"]): if message["role"] == "assistant": for block in message["content"]: if "text" in block: print(block["text"])
# Read the files Marmot produced while working for f in marmot.list_files(analysis["id"])["files"]: print(f["filename"], f.get("description")) cohort_sql = marmot.get_file(analysis["id"], "cohort.sql")["content"]except MarmotApiError as e: print(f"Marmot request failed ({e.status_code}): {e.detail}")