Skip to main content

immiscible

Govern what your AI agents spend, share and do, from Python. Before a tool runs, the agent asks Immiscible; Immiscible checks the mandates a person wrote, asks that person when it should, and hands back a signed receipt. Spend control, approvals and TokenOps for the OpenAI Agents SDK, LangChain, LangGraph and MCP. Standard library only: urllib, json, hashlib and a pure-Python Ed25519 verifier. Python 3.9+. Type hints throughout (py.typed). Fails closed.

pip install immiscible
from immiscible import Immiscible, tool_action

immiscible = Immiscible()                        # IMMISCIBLE_AGENT_KEY, IMMISCIBLE_URL
run = immiscible.run()                           # one trace and one session per task

with run.guard(tool_action("deploy", {"service": "api"}, domain="mycompany.com")):
    deploy()

guard asks, waits for a person if one is asked, runs your block only if allowed, and settles completed (or failed if it raised). A refusal raises ImmiscibleDeniedError with plain-English reasons; your block never ran. As a decorator it takes a function that maps the call to an action, and works on async def too.

Quickstart: immiscible.fly.dev/docs/quickstart. SDKs: immiscible.fly.dev/docs/sdks. API: immiscible.fly.dev/docs/api. JavaScript: @immiscible/sdk on npm. MIT licence.

The client

Call Does
Immiscible(api_key, base_url, timeout=30, max_retries=2, session_id=, traceparent=) Defaults from IMMISCIBLE_AGENT_KEY and IMMISCIBLE_URL (or ASSAY_AGENT_KEY, ASSAY_URL).
immiscible.run(session_id=None, client="custom", traceparent=None) A client for a new run.
authorize(action, idempotency_key=None) Ask. Returns a Decision (a dict with .allowed, .receipt, .reasons, .approval_url, ...).
wait_for_decision(id, timeout=600, initial_delay=0.5, max_delay=8, factor=1.6, cancel=None, on_poll=None) Poll with backoff and jitter. cancel is a threading.Event.
decide(action, ...) Authorize and wait; returns an allow or raises a refusal.
settle(id, status="completed", amount=None) Record what happened.
guard(action_or_mapper, wait=True, on_approval_required=None, settle=True, settle_amount=None, ...) All of it: with block or decorator.
pay(...), request_data(...), Immiscible.payment_action(...), Immiscible.data_action(...), tool_action(...) Build and send actions.
outcome(task_id, status) TokenOps.
mcp_proxy(upstream_id) MCP proxy client: list_tools, call, call_with_approval, retry_after_approval.
gateway.openai(), gateway.anthropic(), gateway.openai_client(), gateway.anthropic_client(), gateway.env() Model SDKs through the gateway, inside the run.
context The run: trace_id, session_id, headers(), traceparent(), httpx_event_hooks(), last_server_traceparent.

Integrations

from immiscible.integrations import guard_tools, guard_langchain_tools, guarded
guard_tools(tools, client=, map_to_action=) OpenAI Agents SDK function tools
guard_langchain_tools(tools, client=, map_to_action=) LangChain tools, for ToolNode, bind_tools, create_react_agent
@guarded(map_to_action, client=) any function a framework turns into a tool; signature and docstring kept

Setups for each framework: immiscible.fly.dev/docs/sdks.

Receipts

from immiscible import fetch_jwks, verify_receipt

jwks = fetch_jwks("https://immiscible.example")                  # pin: fetch once, store with your config
r = verify_receipt(token, "https://immiscible.example", jwks=jwks, online=True,
                   expect={"amount": 6420, "currency": "GBP", "merchant": "ocado.com"})

The same checks as the TypeScript SDK, with a pure-Python Ed25519 verifier checked against RFC 8032 and against Node's signatures.

Testing

python3 -m unittest discover -s tests -t .

immiscible.testing.start_fake() is a stdlib fake server (real Ed25519 receipts, a fake gateway and model, the MCP proxy, a person's approvals) for your own tests. tests/test_frameworks.py runs against the real openai, openai-agents, langchain-core and langgraph when they are installed; tests/test_cross_language.py runs against the TypeScript SDK's fake when Node is available.

Crypto payments: decide, then sign

Immiscible never holds keys or signs. decide_then_sign asks first and calls your wallet only after an allow whose signed receipt covers the exact transfer; x402_request does the same for HTTP 402 (x402 v1 and v2) resources.

from immiscible import Immiscible
from immiscible.crypto import decide_then_sign, x402_request

decide_then_sign(Immiscible(), {"asset": "USDC", "network": "base", "amount": "12.50", "recipient": "0x..."},
                 lambda decision: {"txHash": wallet.send()})
status, headers, body = x402_request(Immiscible(), "https://api.example.com/report", pay=my_x402_signer)

Amounts are decimal strings, never floats. See crypto payments and x402.

Metadata

Release files for immiscible 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for immiscible 0.1.0
File Size Uploaded
immiscible-0.1.0.tar.gz 57.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for immiscible 0.1.0
File Interpreter ABI Platform
immiscible-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 105.5 kB

Release files / immiscible-0.1.0.tar.gz

Download URL immiscible-0.1.0.tar.gz
Size 57.6 kB
Tags Source
SHA-256 checksum
How to use checksums
2404527db7773ed6fd69dc93400aaf2648484e57291ab4e810fc1ca2c7c30c18
BLAKE2b-256 checksum
How to use checksums
a22d71ac30b0b345f505717997b1e60c6fafa8064c73c0e49c6c9f17cebbc97d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / immiscible-0.1.0-py3-none-any.whl

Download URL immiscible-0.1.0-py3-none-any.whl
Size 47.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
27e363115c4fafefd24bf262c8a85a2ba0f9b9d5f755cc2c2c9fe9ffc68419e4
BLAKE2b-256 checksum
How to use checksums
25a7646440aaa26555c247e1b3ff59f48b22b450fe36b9e766375d07a4192a23
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release history Release notifications | RSS feed

0.1.1

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page