Skip to main content

Python SDK for Ophanix, including the Tool Gateway client.

Project description

Ophanix Python SDK

Python SDK for Ophanix. The current package provides the Tool Gateway client for agents that call governed tools through the Ophanix Tool Gateway.

Install

When the package is published to your configured Python index:

pip install ophanix-python-sdk
export OPHANIX_GATEWAY_TOKEN="replace-with-a-gateway-token"

If the package has not been published to your configured package index yet, install from a validated wheel generated by python3 scripts/validate_release.py, or install from the repository path during internal testing:

pip install .

The package distribution is named ophanix-python-sdk; the stable import path for the Tool Gateway client remains ophanix_tool_gateway.

Gateway tokens are issued by Ophanix for a specific agent, organization, environment, scope, and tool resource. Do not hard-code them in application source or commit them to configuration files. Prefer environment variables, secret managers, or a custom token provider that reads from your runtime's credential store. Production tokens must be generated by the Ophanix credential issuance flow or an equivalent cryptographically random issuer; short, human-readable fixture tokens are only acceptable in local tests. Token providers must return the raw token without the Bearer prefix. Raw tokens may contain only A-Z, a-z, 0-9, ., _, ~, +, /, =, and -, with no whitespace, and must be 4096 characters or fewer.

Use https:// gateway URLs outside local development. Plain http:// is accepted for localhost and loopback addresses only; non-local HTTP requires the explicit allow_insecure_http=True opt-in.

Sync Usage

from ophanix_tool_gateway import (
    EnvironmentTokenProvider,
    OphanixToolGatewayClient,
    ToolAuthenticationError,
    ToolDeniedError,
    ToolGatewayError,
)

with OphanixToolGatewayClient(
    base_url="https://gateway.example.com",
    token_provider=EnvironmentTokenProvider(),
) as client:
    try:
        compatibility = client.check_compatibility()
        if not compatibility.compatible:
            raise RuntimeError("SDK and gateway contract versions do not match")
        tools = client.list_all_tools()
        result = client.call_tool("claims.lookup", {"claim_id": "claim_123"})
    except ToolDeniedError as exc:
        print(f"Denied by gateway policy: {exc.reason_code}")
    except ToolAuthenticationError:
        print("Gateway token was rejected")
    except ToolGatewayError as exc:
        print(f"Gateway call failed: {exc.code or exc.status_code}")
    else:
        print([tool.name for tool in tools])
        print(result.result)

API Reference

OphanixToolGatewayClient is the synchronous client. Use it as a context manager so the underlying HTTP client is closed when the agent shuts down.

AsyncOphanixToolGatewayClient mirrors the synchronous API for async runtimes. It accepts either a sync token provider or an async token provider.

Use ToolGatewayClientConfig with OphanixToolGatewayClient.from_config(...) or AsyncOphanixToolGatewayClient.from_config(...) when several workers share the same timeout, cache, retry, and response-limit settings.

Common constructor options:

  • base_url: absolute gateway URL. Non-local http:// is rejected unless allow_insecure_http=True.
  • token_provider: object with get_token(). Use EnvironmentTokenProvider() for OPHANIX_GATEWAY_TOKEN, or StaticTokenProvider(token) for tests and short-lived scripts.
  • timeout_seconds: per-request timeout, default 5.0.
  • max_payload_bytes: client-side serialized payload cap, default 1_000_000.
  • max_response_bytes: client-side gateway response cap, default 1_000_000. The built-in HTTPX clients enforce this while streaming response bytes and before JSON parsing. Custom injected clients must expose stream() by default. Set allow_buffered_custom_http_client=True only when the injected client enforces equivalent response-size limits before materializing bodies.
  • cache_tools: opt-in discovery cache, default False.
  • cache_ttl_seconds: cache TTL when cache_tools=True, default 300.0.
  • max_cache_entries: maximum process-local discovery cache entries when caching is enabled, default 256.
  • event_hook: optional callable that receives immutable, token-free telemetry events for tool-call start/success/denial/error. Hook exceptions are swallowed and debug-logged by default so observability code cannot break tool calls.
  • raise_event_hook_errors: when True, event hook failures are surfaced to the caller. Use this in tests or strict observability environments.
  • discovery_max_retries: retry count for discovery only, default 2.
  • discovery_retry_backoff_seconds: base exponential backoff, default 0.2.
  • discovery_retry_max_sleep_seconds: cap for retry sleeps and Retry-After, default 5.0.
  • discovery_retry_jitter_ratio: client-side retry jitter ratio, default 0.2.
  • invocation_max_retries: retry count for idempotent tool invocations, default 2. The SDK only uses this when call_tool(..., idempotency_key=...) is provided.
  • invocation_retry_backoff_seconds: base invocation retry backoff, default 0.2.
  • invocation_retry_max_sleep_seconds: cap for invocation retry sleeps and Retry-After, default 5.0.
  • invocation_retry_jitter_ratio: invocation retry jitter ratio, default 0.2.
  • allow_buffered_custom_http_client: default False; set only for injected clients that enforce equivalent response-size limits without SDK streaming.

Main methods:

  • call_tool(tool_name, payload, correlation_id=None, idempotency_key=None): invokes one tool. Payload must be a JSON object with string keys and finite numeric values. When idempotency_key is set, the SDK sends Idempotency-Key and may retry transient invocation failures with the same key.
  • check_compatibility(): calls authenticated /api/v1/gateway/capabilities and returns GatewayCompatibility.
  • list_tools(owner_team=None, limit=50, offset=0): lists one page of callable active tools for the configured credential. The gateway discovery API is active-only; the legacy status="active" argument remains accepted only for compatibility and emits DeprecationWarning.
  • list_all_tools(owner_team=None, page_size=200, max_total=None): follows discovery pagination and can fail closed if discovery exceeds max_total.
  • get_tool(tool_name_or_id): finds one callable tool by name or id.
  • clear_tool_cache(): clears cached discovery results when cache_tools=True.

get_tool() searches only the definitions visible to the current credential. A missing result raises ToolGatewayError with code="tool_not_visible" because the SDK cannot safely distinguish a nonexistent tool from a tool hidden by authorization.

Async Usage

from ophanix_tool_gateway import AsyncOphanixToolGatewayClient, EnvironmentTokenProvider

async with AsyncOphanixToolGatewayClient(
    base_url="https://gateway.example.com",
    token_provider=EnvironmentTokenProvider(),
) as client:
    tool = await client.get_tool("claims.lookup")
    result = await client.call_tool(tool.name, {"claim_id": "claim_123"})
    print(result.request_id, result.result)

Error Handling

The SDK raises ToolGatewayValidationError for invalid local configuration or caller input, ToolAuthenticationError for rejected gateway credentials, ToolDeniedError for gateway policy denials, and ToolGatewayError for transport, gateway, upstream, and response-contract failures. ToolGatewayValidationError is a ValueError subclass for backward-compatible callers that already catch ValueError. Exception messages are intentionally generic so application logs do not inherit arbitrary upstream text. Use status_code, code, reason_code, request_id, correlation_id, retry_after_seconds, and the sanitized response_body for debugging.

try:
    result = client.call_tool("claims.lookup", {"claim_id": "claim_123"})
except ToolAuthenticationError as exc:
    refresh_or_rotate_token = exc.code
except ToolDeniedError as exc:
    audit_reason = exc.reason_code
except ToolGatewayError as exc:
    request_id = exc.request_id
    diagnostic_code = exc.code or exc.status_code
    retry_after = exc.retry_after_seconds

Reliability And Safety

Discovery uses the gateway-authenticated /api/v1/gateway/tools route and only returns active tools callable by the configured credential. list_all_tools() follows pagination for you, while list_tools() exposes explicit limit and offset controls. Pagination is offset-based in 0.1.x; consumers that need very large or high-churn catalogs should set max_total and re-run discovery after contract changes until the gateway exposes cursor pagination. The optional owner_team discovery filter is an exact, case-sensitive server filter; use the owner-team value from the tool definition rather than display text.

Discovery requests retry transient 408, 429, and 5xx responses by default with bounded exponential backoff, Retry-After support, and jitter. Tool invocation requests retry transient transport, 408, 429, and 5xx failures only when the caller supplies idempotency_key; the same key is sent on every attempt so the gateway can replay a completed result or reject a conflicting payload.

The SDK validates payloads as strict JSON objects, rejects non-finite numbers, non-string object keys, and cyclic Python payloads, rejects header control characters, rejects raw gateway tokens longer than 4096 characters, redacts sensitive fields from exception diagnostic bodies, caps gateway responses while streaming bytes before JSON parsing when using the built-in HTTPX clients, and keeps static tokens out of generated repr() output.

When cache_tools=True, discovery cache entries are partitioned by a process-local HMAC fingerprint of the current bearer token and expire after cache_ttl_seconds. Cached results do not cross credential rotations, and list_all_tools() / get_tool() use one credential context across their paginated requests. Cached tool definitions are copied before they are returned so caller mutation cannot poison later cache reads. The cache is process-local and bounded by max_cache_entries; call clear_tool_cache() after urgent permission or tool-contract changes instead of waiting for TTL expiry.

Client cache mutation is internally guarded, but applications should still prefer one client per worker process or event loop so timeout, token-provider, and lifecycle behavior remains easy to reason about. Async calls follow normal asyncio cancellation semantics. If a task awaiting call_tool() is cancelled, treat the invocation outcome as unknown unless your tool contract is idempotent or your application can reconcile by request_id or correlation_id.

Use a unique idempotency key per logical invocation when retrying or when the caller cannot tolerate duplicate upstream side effects:

result = client.call_tool(
    "claims.lookup",
    {"claim_id": "claim_123"},
    idempotency_key="claim-123-refresh-2026-05-11T09:00Z",
)

Reusing a key with a different payload returns idempotency_conflict. Reusing a key while the original call is still running returns idempotency_in_progress.

EnvironmentTokenProvider reads the configured environment variable on each request. For high-throughput agents, prefer a custom provider backed by your runtime secret manager or refresh cache:

class CachedTokenProvider:
    def __init__(self, loader):
        self._loader = loader
        self._token = None

    def get_token(self) -> str:
        if self._token is None:
            self._token = self._loader()
        return self._token

SDK event hooks receive immutable mappings with no bearer tokens:

Event Fields
tool_call.start tool_name, correlation_id, idempotent
tool_call.success tool_name, request_id, correlation_id, reason_code, elapsed_ms
tool_call.denied tool_name, status_code, elapsed_ms
tool_call.error tool_name, status_code or code, elapsed_ms
tool_call.retry tool_name, attempt, delay_seconds, status_code, code
tool_discovery.retry attempt, delay_seconds, status_code

Sanitized exception response_body values redact common credential keys and PII-like keys including authorization, api_key, credential, password, secret, token, key, email, phone, address, and ssn.

Credential Issuance

The SDK consumes gateway bearer tokens; it does not mint them. Production tokens must be issued through Product Platform agent credential issuance:

  1. Register or select an active agent identity.
  2. Approve the capability scope the credential needs.
  3. Bind the scope to the exact tool resource where possible.
  4. Issue a short-lived bearer credential from the Product Platform API or operator workflow.
  5. Store the raw token in a runtime secret store and expose it to the SDK through EnvironmentTokenProvider or a custom provider.
  6. Rotate before expiry and clear discovery caches after emergency permission changes.

Local fixture tokens in direct HTTP examples are deterministic demo data and are not production credentials.

Minimal local issuance flow for demo data:

cd packages/product-platform
python3 -m product_platform.cli db migrate
python3 -m product_platform.cli db seed
python3 -m product_platform.cli serve --host 127.0.0.1 --port 8088

export OPHANIX_BASE_URL="http://127.0.0.1:8088"
export OPHANIX_OPERATOR_TOKEN="<operator-api-token-from-your-local-login-or-admin-flow>"

curl -sS -X POST "$OPHANIX_BASE_URL/api/v1/agents/agent_claims_demo/credentials" \
  -H "Authorization: Bearer $OPHANIX_OPERATOR_TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-Environment-ID: env_default" \
  --data '{
    "credential_type": "bearer",
    "issuer": "local-demo",
    "ttl_seconds": 900,
    "scopes": [
      {"scope": "claims.lookup:read", "resource_type": "tool", "resource_id": "claims.lookup"}
    ]
  }'

The response includes the raw token once. Store it in a local secret store or OPHANIX_GATEWAY_TOKEN; do not commit it. Exact endpoint names may differ in private operator builds, so verify the local API shape against your deployment before writing automation.

Stability

The package is still 0.1.0 and classified as Beta. Constructor names, typed error classes, and the main sync/async methods are treated as the supported surface for this line; compatibility shims such as list_tools(status="active") may emit deprecation warnings before removal in a future pre-1.0 release.

Compatibility matrix:

SDK version Expected gateway contract Python versions Notes
0.1.x /api/v1/gateway/capabilities, /api/v1/gateway/tools, and /api/v1/tools/{name}/invoke 3.11, 3.12, 3.13 Beta MVP. product_platform.tool_gateway imports are compatibility shims; prefer ophanix_tool_gateway.

MVP support boundary: the SDK is suitable for controlled internal or design partner pilots where gateway operators control tokens, upstream targets, ingress limits, and rollout scope. It is not an enterprise production certification. Idempotent invocation retries are implemented for callers that provide Idempotency-Key; cursor pagination, externally enforced ingress limits, and broader release-governance evidence remain post-MVP hardening.

Troubleshooting

  • ToolGatewayValidationError: base_url must use https: use an https:// gateway URL outside localhost development, or explicitly opt into non-local HTTP for a trusted test network.
  • ToolGatewayValidationError: token is required: check that OPHANIX_GATEWAY_TOKEN is present and not blank.
  • ToolGatewayValidationError: token must be the raw gateway token without the Bearer prefix: remove Bearer from the value returned by your token provider. The SDK adds the authorization scheme itself.
  • ToolDeniedError: the credential authenticated successfully, but policy, permission, scope, or resource binding denied the call.
  • ToolGatewayError(code="invalid_response"): the gateway returned a successful HTTP response that did not match the SDK contract. Capture the request_id and sanitized response_body when reporting the issue.
  • ToolGatewayError(code="transport_error"): the SDK could not reach the gateway. Check DNS, TLS, connectivity, proxy settings, and timeout_seconds.
  • ToolGatewayError(code="response_too_large"): the gateway response exceeded max_response_bytes before SDK JSON parsing.
  • ToolGatewayError(code="client_closed"): the SDK client was used after close() or context-manager exit. Create a new client for further calls.

Production Adoption Checklist

  • Use an https:// gateway URL outside localhost development.
  • Use the SDK instead of direct HTTP examples for production agents.
  • Issue high-entropy gateway tokens through the Product Platform credential workflow, store them in a secret manager, and rotate them before expiry.
  • Scope credentials to the minimum tool resource and operation required.
  • Set timeout_seconds and max_payload_bytes for the caller's workload.
  • Use idempotency_key for any invocation that may be retried or reconciled after an unknown outcome.
  • Enable discovery caching only when stale contract/permission data is acceptable for up to cache_ttl_seconds.
  • Capture request_id, correlation_id, code, and sanitized response_body in application diagnostics.
  • Use custom HTTP clients only when they support streaming response limits, or set allow_buffered_custom_http_client=True with an equivalent safety control.

Release Validation

Before publishing a standalone SDK release, install release tooling and validate both artifact shapes:

python3 -m pip install '.[release]'
python3 scripts/validate_release.py

The release check builds a wheel and sdist, verifies that both artifacts contain ophanix_tool_gateway/__init__.py, ophanix_tool_gateway/sdk.py, and ophanix_tool_gateway/py.typed, rejects local/generated files such as SQLite databases and __pycache__, verifies parity with the product-platform vendored SDK copy, installs the built wheel with runtime dependencies into a temporary target, writes a local CycloneDX SBOM with artifact hashes, and runs twine check over the generated artifacts. Dependency audit can be enforced in CI with:

python3 -m pip install '.[security]'
python3 scripts/validate_release.py --require-dependency-audit
python3 scripts/validate_release.py --strict-git

The SDK depends only on httpx. It is also re-exported from product_platform.tool_gateway for compatibility with earlier internal imports. CI exercises the SDK on Python 3.11, 3.12, and 3.13. Dependency range changes must keep that matrix green before release.

--strict-git additionally requires a clean SDK package worktree and an exact release tag matching v<project.version> unless --expected-tag is supplied.

Publishing is intentionally separate from local artifact validation. A release owner must publish only artifacts produced by the validated workflow, preserve the build logs, artifact checksums, provenance attestations, and SBOM, and document whether the final upload is performed through GitHub trusted publishing, Azure DevOps, or another approved internal release pipeline.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ophanix_python_sdk-0.1.0.tar.gz (33.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ophanix_python_sdk-0.1.0-py3-none-any.whl (23.9 kB view details)

Uploaded Python 3

File details

Details for the file ophanix_python_sdk-0.1.0.tar.gz.

File metadata

  • Download URL: ophanix_python_sdk-0.1.0.tar.gz
  • Upload date:
  • Size: 33.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for ophanix_python_sdk-0.1.0.tar.gz
Algorithm Hash digest
SHA256 233c05a88ab97507938968e4e127431472ef5a6983e875ecfcc3a09ce31e0aa2
MD5 22c19c5b56ad43a538763abf18fbf7da
BLAKE2b-256 2f887c74258bf34a4b6df4e0b0e865c075c499bcb450b86e093a31dc62b30c81

See more details on using hashes here.

Provenance

The following attestation bundles were made for ophanix_python_sdk-0.1.0.tar.gz:

Publisher: publish.yml on igordjuric404/ophanix-python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ophanix_python_sdk-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for ophanix_python_sdk-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a26b54ba1debd8b04633b056b791850037907f59299c7d3635823fdee9f8ccb
MD5 2f30ac20860116e9b9d7b1046d028e9c
BLAKE2b-256 d2685a8f9e536e675a8d889962073232967d0b3eb9706f02b02b977c3c302f2e

See more details on using hashes here.

Provenance

The following attestation bundles were made for ophanix_python_sdk-0.1.0-py3-none-any.whl:

Publisher: publish.yml on igordjuric404/ophanix-python-sdk

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page