Skip to main content

Marrow agent-memory client for ingest, jobs, query, and the native /v1 memory API.

Project description

marrowid - the Marrow Python client

Distribution marrowid, import package marrow, primary class Marrow.

The public client identities are marrowid==1.0.10, imported as marrow, @marrowid/sdk@1.0.10, and @marrowid/cli@1.0.10. The HTTP contract is their semantic authority. Direct HTTP, TypeScript, Python, and CLI examples run inside a trusted application process; using one of these clients does not provide prompt-injection isolation for untrusted content.

This proprietary typed client maps directly to Marrow's classic source/job/query routes and native /v1 memory routes. It uses one customer API key, preserves API error classes, and keeps the product contract explicit: peer.ask() returns cited evidence or an honest insufficient_evidence state. There is no chat method and no compatibility import alias.

Install

Install the exact stable version from PyPI:

python -m pip install marrowid==1.0.10
python -c "from marrow import Marrow, __version__; print(Marrow.__name__, __version__)"

The package metadata declares CPython 3.9 through 3.14 on macOS, Linux, and Windows. The wheel is pure Python (py3-none-any).

Marrow · Documentation

First use: connect context and retrieve it

Create a customer API key with ingest and query scopes, then initialize the client. Keep MARROW_API_KEY in server secret storage or the invoking process environment. Do not put it in source, arguments, notebooks, logs, screenshots, or generated documentation.

import os
from marrow import Marrow

client = Marrow(
    api_key=os.environ["MARROW_API_KEY"],
    host=os.environ.get("MARROW_API_BASE_URL", "https://api.marrow.id"),
)

Dry-runs may omit an idempotency key. Live URL and file ingest require a caller-owned idempotency_key between 8 and 160 characters so a retry can reuse the same submission identity without creating another job.

preview = client.ingest.url(
    "https://example.com/onboarding-note",
    dry_run=True,
    dated_at="2026-07-12",
)

queued = client.ingest.url(
    "https://example.com/onboarding-note",
    dry_run=False,
    dated_at="2026-07-12",
    idempotency_key="onboarding-note-2026-07-12",
)

job = client.ingest.jobs.wait(
    queued.job.id,
    max_attempts=60,
    poll_interval=1.0,
)
if job.job.status == "succeeded":
    result = client.query(
        "What should this workflow remember about onboarding?",
        limit=5,
        evidence_limit=3,
    )

Use client.ingest.file(...) for a local path; pass content_type when the media type is known. client.ingest.jobs.list() and .get(job_id) provide single reads when the application owns its own polling loop. Classic query returns only the current launch statuses partial or insufficient_evidence; the reserved answered status fails closed instead of being treated as a verified answer.

Native memory usage

The native memory methods are available and use memory.read and memory.write scopes. Use a dedicated customer API key with only the scopes the app or agent needs.

import time

peer = client.peer("riley")
session = client.session("project-update")

receipt = session.add_messages(
    [
        {
            "role": "user",
            "content": "Project update should cite retention notes and keep the pricing caveat visible.",
        }
    ],
    peer_id="riley",
    infer=True,
    source_ids=["550e8400-e29b-41d4-a716-446655440000"],
)

for attempt in range(60):
    event = client.events.get(receipt.event_id)
    if event.status == "succeeded":
        break
    if event.status in {"failed", "quarantined"}:
        raise RuntimeError(
            f"Memory event {receipt.event_id} ended with {event.status}"
        )
    if attempt == 59:
        raise TimeoutError(f"Memory event {receipt.event_id} is still processing")
    time.sleep(1)

context = peer.ask(
    "What context should the project-update assistant use?",
    session="project-update",
)

if context.status == "insufficient_evidence":
    raise RuntimeError("Add source material before using Marrow context")

infer and source_ids are optional write controls from the native MessagesCreateRequest schema. Omit infer to use the service default. Pass source_ids only when the application already has up to 100 succeeded ingest job IDs from the same account. The API rejects missing, foreign-account, and nonterminal jobs before creating a memory event. Use the same event-success gate before peer.context() or session.context() reads derived from the write.

Claim mutations use optimistic concurrency. Read the claim first and pass its head_revision back to the change:

claim = client.claims.get("mem_0123456789abcdef01234567")
corrected = client.claims.update(
    claim.id,
    "Project updates should lead with the decision.",
    head_revision=claim.head_revision,
    reason="User clarified the preference.",
)
client.claims.delete(
    claim.id,
    head_revision=corrected.head_revision,
    reason="User withdrew the preference.",
)

Corrections return eligibility: "eligible"; a stale revision fails with 409 revision_conflict instead of overwriting a newer head.

Every API method returns a named response object. Attributes are the primary API; mapping access remains available for existing code. Context.blocks contains typed ContextBlock objects, and repr() identifies each response type.

Delete sources, peers, and sessions through the same client, then poll the returned job or event receipt:

source_deletion = client.sources.delete(source_id)
client.ingest.jobs.wait(source_deletion.job.id)

peer_deletion = client.peer("riley").delete()
client.events.get(peer_deletion.event_id)

Errors

Import errors directly from marrow. MarrowConfigError is raised locally for invalid client configuration. Hosted failures raise MarrowApiError or the more specific ValidationError, WrongCredentialClassError, ForbiddenScopeError, NotFoundError, or ConflictError. API errors expose status_code, code, request_id, and validation issues.

Public surface

  • Marrow(api_key=..., workspace="default")
  • client.access()
  • client.ingest.url() / client.ingest.file()
  • client.ingest.jobs.list() / .get() / .wait()
  • client.sources.delete(source_id)
  • client.query()
  • client.workspaces.upsert() / client.workspaces.list()
  • client.peer(id).create() / .get() / .ask() / .context() / .representation() / .delete()
  • client.session(id).create() / .get() / .add_messages() / .messages() / .context(peer=...) / .delete()
  • client.claims.query() / .list() / .get() / .history() / .update() / .delete()
  • client.events.get(event_id)
  • client.queue.status()

Source verification

For repository development:

cd clients/python
python -m pip install -e ".[dev]"
python -m pytest

The offline parity suite drives every native route and the classic ingest/job/query path through a recording transport and verifies that each request carries the customer-key bearer. The release gate builds the wheel and sdist twice, compares their bytes and exact inventories, runs metadata and Twine checks, verifies the registry version is unused, and clean-installs both artifacts before executing the classic path, native route, strict-response, and structured-error probes.

Proprietary. UNLICENSED means the distribution is a delivery channel, not an open-source grant.

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

marrowid-1.0.10.tar.gz (15.5 kB view details)

Uploaded Source

Built Distribution

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

marrowid-1.0.10-py3-none-any.whl (17.2 kB view details)

Uploaded Python 3

File details

Details for the file marrowid-1.0.10.tar.gz.

File metadata

  • Download URL: marrowid-1.0.10.tar.gz
  • Upload date:
  • Size: 15.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for marrowid-1.0.10.tar.gz
Algorithm Hash digest
SHA256 355afa37a7e087b289bfffd3f93a7030b55c100e21753f6e5dfc6635e34ebbbd
MD5 60cd00f64eab217f59467b4901fd86ba
BLAKE2b-256 af4ea70afab3d0a7899a2a71caa59d4107c08398c7ce3e3adc378fcbabc15a7c

See more details on using hashes here.

File details

Details for the file marrowid-1.0.10-py3-none-any.whl.

File metadata

  • Download URL: marrowid-1.0.10-py3-none-any.whl
  • Upload date:
  • Size: 17.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.11 {"installer":{"name":"uv","version":"0.11.11","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for marrowid-1.0.10-py3-none-any.whl
Algorithm Hash digest
SHA256 5bc3b73cd4d000a09315a2f60fef8e28594e76ffce6f57cd128751ed1904f679
MD5 a1b97fcc01ebc0dd40f9823225bcff09
BLAKE2b-256 62f9e0c8ba33d5a3a2e354472e456662579dc65e9e95b5cae085b38553ccc3ed

See more details on using hashes here.

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