> ## Documentation Index
> Fetch the complete documentation index at: https://mirrors.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Python SDK reference

> Synchronous context retrieval, background trace capture, and lifecycle methods.

This reference covers the Python source package `mirrors-sdk` 0.1.0, which requires
Python 3.11 or later and is not yet published to PyPI. Start with the
[Python quickstart](https://mirrors.dev/docs/quickstart-python.md) for installation and integration.

## Client and context

Create `Mirrors(api_url=..., api_key=..., agent_id=...)` once per agent configuration
and reuse it across executions. Keep the API key in server secrets. Construct a new
client inside each process if your application forks workers.

`get_context(trace, timeout_ms=1000, cancel=None)` is synchronous. It returns a
`ContextResult` with attributes, not a dictionary:

| Attribute | Meaning |
| - | - |
| `context` | Optional text to append to the system prompt; empty on no-match or fallback. |
| `mode` | `active`, `shadow`, or `off`. |
| `delivery` | `accepted`, `unconfirmed`, or `canceled`. |
| `reason` | Decision or fallback reason. |
| `elapsed_ms` | Time spent in retrieval. |
| `freshness` | `None`, or bindings with `request_id`, `trace_id`, `decision_id`, `mode_version`, `issued_at`, and `expires_at`. |

Call once before assembling the user turn's model request. The total wait budget
is 1–1,000 ms, including copying and scheduling; it does not wait for capture
uploads. Scheduling delays can exceed the budget. `accepted` confirms a decision
arrived, not that your application used it or the model succeeded.

Use `result.context` promptly. Check `result.freshness.expires_at` if dispatch is
delayed, and retrieve again if original history changes. Never reuse returned text
for another turn. Shadow, off, and unknown/expired mode return empty immediately;
see [runtime modes](https://mirrors.dev/docs/guides/runtime-modes.md).

For synchronous cancellation, pass a `threading.Event` as `cancel`. A set event
produces `delivery == "canceled"`; preserve your application's cancellation check
before calling the model. Async callers can use
`await mirrors.aget_context(trace, timeout_ms=1000, cancel=event)` without blocking
their event loop. Async task cancellation propagates as `asyncio.CancelledError`.

## Trace capture

`capture_trace(trace, final=False, terminal=None)` returns a `DeliveryTicket`
without waiting for network delivery. `trace` is a dictionary of the full original
history, excluding Mirrors-added context. Canonical field names stay camelCase,
including `conversationId`, `conversationRevision`, `entryPoint`, and tool `callId`.
See [trace capture](https://mirrors.dev/docs/guides/trace-capture.md) for message structure and conversation IDs.

After the model/tool loop and response stream end, capture the full final history
with `final=True` and `terminal="completed"`, `"failed"`, or `"canceled"`.
Terminal status describes the execution ending, not task success.

| Ticket access | Meaning |
| - | - |
| `ticket.receipt` | Current receipt; inspect `.status`, `.reason`, and `.ingestion`. |
| `ticket.wait(timeout=2)` | Wait up to **2 seconds** outside the model path; may still return `pending`. |
| `await ticket.wait_async(timeout=2)` | Equivalent bounded wait for async callers. |

Receipt statuses are `pending`, `accepted`, `dropped`, and `unconfirmed`. When
`receipt.ingestion` is present, `receipt.ingestion["status"] == "queued"` means
analysis was queued, not completed. `unconfirmed` may follow a committed write; it is not proof
of rejection. Ticket waits accept 0–30 seconds; other SDK timeout parameters use
milliseconds. Snapshots are limited to 1,000,000 encoded bytes and 10,000 messages.
Use builtin JSON data, without custom objects, cycles or silent truncation.

## Lifecycle

| Method | Purpose |
| - | - |
| `release_trace(trace_id)` | Clean up local execution state when no final evidence is available; does not claim server completion. |
| `flush(timeout_ms=2000)` | Wait for captures pending at call entry and return a `FlushResult`. |
| `await aflush(timeout_ms=2000)` | Async capture drain. |
| `close(timeout_ms=2000)` | Stop new work, flush, then dispose; return the flush result. |
| `dispose()` | Stop optional work immediately. |
| `set_enabled(False)` / `set_enabled(True)` | Disable or re-enable locally; use fresh execution IDs after re-enabling. |
| `diagnostics()` | Return a dictionary of local capture and execution counters. |

`FlushResult` has `accepted`, `dropped`, `unconfirmed`, and `pending` counts.
In Python, `.complete` is true only when all covered tickets were accepted
(or none were pending at entry). Already-settled tickets and later captures are
excluded. Keep individual receipts when their outcomes matter. Background threads
do not keep the process alive, and in-memory capture queues cannot survive a crash.
Use fresh execution IDs after finalization, release or process restart.

## Optional user and turn timing

Confirm service compatibility before adding these canonical fields:

* `user`: `{"id": "internal-user-id", "segment": "cohort"}`. Use an opaque internal
  ID, not an email; `segment` is optional. The final snapshot's value is recorded.
  Trimmed limits are 1–256 characters for ID and 1–64 for segment, without control characters.
* `turnLatencyMs`: whole-turn duration on **final captures only**. Start
  `turn = mirrors.start_turn()` when the application accepts the message, before retrieval;
  use `turn.finish()` after all model calls, tools, retries and streaming finish.
  The first finish freezes milliseconds. An existing application measurement can
  supply a finite nonnegative value; omit unknown duration.

Neither optional field changes model execution. Never send `turnLatencyMs` with
`get_context` or a non-final capture.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.