Manage sessions
Fund, inspect, reuse and finish a session.
On this page
Fund and reuse a session
Use the installed SDK, authenticated wallet and configuration from the quickstart. The Python fragments below run inside its configured Client context. For a complete script that inspects and closes your session, follow the quickstart's final step.
A session holds USDC for one provider and fixed wallet/signer authorities. A caller-selected positive initial_session_tokens value is converted to a conservative deposit using the greatest applicable rate and rounding up. It is working-balance guidance, not a guarantee of a number of output tokens.
opened = client.sessions.open(
"https://YOUR_PROVIDER",
model="YOUR_PROVIDER/MODEL",
initial_session_tokens=10_000,
)
print(opened.operation.state)
if opened.session is not None:
print(opened.session.session_id.hex())A usable OpenResult.session requires confirmed funding and a ready authenticated attachment. Retain the operation reference if that outcome is pending or unknown. Reuse the same provider, configuration and session rather than creating replacement funding after an error.
One session can run many requests at once. Share one Client between threads or sub-agents; a second client on the same configuration is refused, and a session has one Control attachment. A run is admitted only when the deposit covers the usage so far and every run in flight at its reserved bound, which for a new run is its prefill plus at least the provider's output buffer. Size initial_session_tokens for all of them together. Each admission may need one voucher round trip. Provider SDKs from 0.2.0 admit waiting runs in the order they first waited; with 0.1.2, a later run can be admitted first.
How usage becomes USDC
Four amounts have different jobs: the deposit funds the session, the signed ceiling authorizes collection, reported usage determines SDK accounting, and confirmed capture records what has actually been paid.
ILLUSTRATIVE · ONE RUN · NO CACHE
1,000 input + 500 output tokens
1 USDC / MTok input · 2 USDC / MTok output
- Deposit
- 1.000000USDC
- Funds available in escrow
- Signed ceiling
- 0.010000USDC
- Authorized maximum
- Reported usage
- 0.002000USDC
- 1,000 × 1 + 500 × 2, per million
- Confirmed capture
- 0.002000USDC
- Paid only after chain confirmation
The example uses illustrative prices of 1 USDC per million input tokens and 2 USDC per million output tokens, with no cached input. It assumes one run, no previous capture and a timely successful close. Network fees are separate; the refund is an escrow amount, not the wallet's net balance after gas.
run_numerator = (input_tokens - cache_tokens) * input_rate
+ cache_tokens * cache_rate
+ output_tokens * output_rate
session_usage = floor(sum(run_numerator) / 1_000_000)
required_coverage = ceil((usage_numerator + reserved_numerator
+ candidate_numerator) / 1_000_000)Use exact integers. Apply the floor once to the accumulated session numerator, across all accepted model/price versions. A per-run receipt amount is informative and non-additive. Cache counts are a subset of input counts and require an accepted cache price; with no cache and zero cache counts, use input and output only.
Unused authorization is not a charge. A capture changes confirmed settlement; it does not add usage or restore consumed reservation. The contract bounds capture by the signed ceiling, deposit and eligibility, but does not verify token counts.
Add funding explicitly
result = client.sessions.topUp(session_id, 1_000_000)
print(result.state, result.operation_ref)This requests an additional 1 USDC deposit. The amount must be a positive integer in six-decimal base units. It adds no inference usage and changes no accepted run prices. The added funding is usable only after canonical confirmation.
Voucher renewal can happen automatically inside confirmed funding. It never creates an automatic deposit. A failed request does not resume when a later top-up confirms.
To react to low funding, pass on_funding_notice to an explicitly composed Client. It is called only when the confirmed deposit cannot cover what the provider requires, with a FundingNotice whose shortfall is the missing amount in base units, worked out when it is delivered. Requests keep running while a top-up confirms.
Read the final records
Record / What it establishes
- Terminal · Complete
- The definitive reported run outcome and final counts when validated; successful streaming completion also requires gRPC OK.
- Receipt · Control
- Final run accounting, checked against the run's accepted terms as its Terminal is. Its chain observation and settled total are the provider's report; the SDK reads no chain for them.
amount- The floored per-run amount; informative, not a separately additive bill.
settled_cumulative- The provider's reported session-wide capture at its stated observation, not necessarily a fresh transaction for this run. The SDK proves the settled total at a cooperative close and checks it at every attachment;
sessions.getshows the value its chain follower last read. - Optional settlement reference
- A claimed Settled event with a positive delta. The SDK verifies a Terminal's reference on chain; a Receipt's is the provider's report.
Final records do not wait for a new settlement transaction merely to attach a reference. They must agree on session, run, model, prices, counts, status, amount and final meter sequence. Exact repeats do not add usage; conflicts cannot advance validated accounting.
Inspect retained sessions
page = client.sessions.list(limit=100)
for session in page.sessions:
print(session.session_id.hex(), session.deposit, session.settled_cumulative)
# The built-in client returns at most its one retained session.
view = client.sessions.get(session_id)list returns at most the built-in client’s one retained session, with next_cursor=None. Pass no cursor; the checked limit is 1–1,000, but this client does not paginate. get reports observed deposit, capture, status, freshness and readiness. Missing local usage is unknown, not zero. Restored inspection and eligible wallet exits can use retained local evidence and the chain without the provider.
Close the quickstart session
After the agent quickstart, return to its folder and terminal with the same provider, wallet and activated environment. Save this as close_session.py to close the retained session and request unused escrow back:
import os
from mppi_agent import Client
with Client.from_config(os.environ["MPPI_PROVIDER"], testnet=True) as client:
page = client.sessions.list()
if not page.sessions:
raise SystemExit("No retained session in this configuration.")
for session in page.sessions:
print(session.session_id.hex(), session.status)
selected = input("Paste the session ID above to close it: ").strip()
session_id = bytes.fromhex(selected)
if session_id not in {s.session_id for s in page.sessions}:
raise SystemExit("That session is not listed in this configuration.")
result = client.sessions.close(session_id)
print(result.state, result.operation_ref)python close_session.pyThe script lists the session retained by this configuration and asks you to paste its ID. A confirmed result establishes the close operation. If it returns pending or unknown, retain the operation reference and run this close script again for the same session; do not repeat inference to resolve payment uncertainty.
circle wallet balance --address "$MPPI_WALLET" --chain ARC-TESTNETUnused escrow returns to the funding wallet. Network fees are separate from the inference charge, so the wallet's net balance is not itself an inference receipt. If the provider cannot cooperate, use the session lifecycle guide for wallet-authorized exits.
Close and refund
- 01Open
Funded sessions can admit covered work. Eligible capture keeps the session open.
- 02Closing
A wallet requestClose starts a one-hour collection window. Stop new grants.
- 03Closed
An eligible close or exit transaction completes the transition and returns unused escrow.
- 04Cooperative path
A ready cooperative close can go directly from Open to Closed, without the wallet's waiting period.
Use sessions.close(session_id) for cooperative close. It stops new grants, accounts for final work, confirms the chain result and refunds unused escrow. Repeat the same close to inspect pending or unknown close intent. A local timeout does not clear that intent.
Exit / Eligibility and effect
requestClose- Funding wallet starts the on-chain Closing interval before open-session idle expiry.
withdraw- Funding wallet exits after the Closing collection deadline; remaining escrow is refunded.
releaseIdle- Permissionless when idle eligibility is met; refund still goes to the funding wallet.
Open-session collection ends 24 hours after qualifying on-chain activity. A timely requestClose starts a full one-hour closing collection window. Positive confirmed top-up and increasing confirmed capture refresh open-session activity; off-chain requests, vouchers and meters do not. Both clocks continue through pauses and outages. At the cutoff, further collection is ineligible; a refund still needs an eligible transaction.
client.close() and stream.close() release local resources. Neither closes escrow. Contract pause and USDC transfer restrictions can also affect exits; inspect confirmed status and operation results.Reconcile the original operation
result = client.sessions.reconcile(operation_ref)
print(result.state)
# Authenticated recovery needs the provider and retained evidence.
view = client.sessions.recover(session_id)Wallet reconciliation uses the original operation reference. Full session recovery needs authenticated Control and complete retained evidence; it does not generate a new deposit or replay inference. Cooperative close is reconciled by repeating the same close request.
If a confirmed close exceeds validated usage, CloseAccountingError retains the confirmed operation. Treat it as an accounting discrepancy, not a failed transaction to submit again. Preserve evidence and investigate with the provider.