Skip to content

Python

The xmemory-ai package gives your Python code persistent, structured memory. Write free-form text, have it automatically extracted into typed objects, and query it back in natural language.

For MCP-based integration (no SDK needed), see the MCP guide.

API key: To use xmemory APIs or integrations, you need an API key. Get one from the xmemory console. Copy and securely store the key. Never share your API key publicly.

The full method, data-type, enum, and exception listing is in the Reference at the bottom of this page.


Terminal window
pip install xmemory-ai

Requires Python 3.10+ and pydantic>=2.0.


This is the full flow — from schema to stored knowledge to answers — in a single script:

import yaml
from xmemory import XmemoryClient, SchemaType
# Connect (reads XMEM_API_KEY from env if api_key is not passed)
client = XmemoryClient(api_key="your-api-key")
# List available clusters
clusters = client.admin.list_clusters()
cluster_id = clusters[0].id
# Describe what you want to remember
schema = client.admin.generate_schema(
cluster_id,
"Track contacts with name, email, company, and notes.",
)
# Create a memory instance from that schema
inst = client.admin.create_instance(
cluster_id=cluster_id,
name="contacts",
schema_text=yaml.dump(schema.data_schema),
schema_type=SchemaType.YML,
)
# inst is an InstanceAPI handle — all subsequent calls go through it.
# Write some information
inst.write("Alice Johnson works at Acme Corp. Her email is alice@acme.com.")
inst.write("Bob Lee is a designer at Globex. He joined last Monday.")
# Read it back
result = inst.read("What is Alice's email?")
print(result.reader_result) # {"answer": "alice@acme.com"}
result = inst.read("Who joined recently?")
print(result.reader_result) # {"answer": "Bob Lee joined last Monday."}
client.close()

Once you have an instance ID, skip the schema step on subsequent runs:

client = XmemoryClient(api_key="your-api-key")
inst = client.instance("your-saved-instance-id")

Parameter Env var Default Description
api_key XMEM_API_KEY None API key for authentication
url XMEM_API_URL https://api.xmemory.ai API base URL
timeout 60 Default request timeout in seconds
http_client None External httpx.Client (you manage its lifecycle)

All parameters are keyword-only. The API key and URL fall back to their environment variables when not passed.

with XmemoryClient(api_key="your-api-key") as client:
inst = client.instance("your-instance-id")
result = inst.read("What is Alice's email?")

AsyncXmemoryClient exposes the same methods with the same signatures as the sync client — three things differ, and that’s the whole story:

  • await every callawait inst.read(...), await client.admin.create_instance(...).
  • aclose() instead of close() — or just use the context manager.
  • async with instead of with.
from xmemory import AsyncXmemoryClient
async def main():
async with AsyncXmemoryClient(api_key="your-api-key") as client:
inst = client.instance("your-instance-id")
result = await inst.read("What is Alice's email?")
print(result.reader_result)
# exiting `async with` calls aclose() for you (or: await client.aclose())
import asyncio
asyncio.run(main())

Send free-form text — xmemory extracts structured objects according to your schema and merges them into the knowledge graph.

inst = client.instance("your-instance-id")
resp = inst.write("Carol is a senior engineer at Initech. Her email is carol@initech.com.")
print(resp.changes) # what the write created / updated / deleted
print(resp.trace_id) # request trace id when available
print(resp.console_url) # this operation's trace in the console

WriteResult, ReadResult, AsyncWriteResult, WriteStatusResult, and ExtractResult all carry console_url, a direct link to that operation’s trace in the console. All of them except WriteStatusResult also carry trace_id, which is useful when you want to correlate SDK calls with API logs.

console_url points at the call rather than at an individual record, and it is None when the deployment has no console configured, so check it before rendering a link. Requires xmemory-ai>=0.15.0.

Control the speed/accuracy tradeoff — pass extraction_logic=ExtractionLogic.DEEP to override the FAST default:

Value When to use
FAST High-volume, low-stakes writes (default)
DEEP Important or complex information

For latency-sensitive code, enqueue a write and return immediately:

resp = inst.write_async("Dave manages the London office.")
print(resp.write_id) # use this to poll for completion

Then check the status:

status = inst.write_status(resp.write_id)
print(status.write_status)
# → WriteQueueStatus.QUEUED | PROCESSING | EXTRACTING | EXTRACTED | APPLYING | COMPLETED | FAILED | NOT_FOUND

Do not call read immediately after write_async — the data may not be committed yet. Poll with write_status until COMPLETED, or use write (synchronous) when you need to read right after.

When you already know exactly what to store, skip extraction: pass structured_mutations instead of text (exactly one of the two — the client raises ValueError otherwise). These are deterministic, LLM-free object creates, updates, and deletes or relation creates and deletes, applied in list order — later mutations may reference objects created earlier in the same batch.

from xmemory import ObjectCreate, ObjectMutation, ObjectUpdate, RelationMutation
resp = inst.write(structured_mutations=[
ObjectMutation(
object_type="person",
create=ObjectCreate(key={"email": "carol@initech.com"}, values={"name": "Carol"}),
),
RelationMutation(
relation_type="works_at",
create={"endpoints": [
{"object_name": "person", "key": {"email": "carol@initech.com"}},
{"object_name": "company", "key": {"name": "Initech"}},
]},
),
])
print(resp.changes) # exactly what was created / updated / deleted

An ObjectMutation carries exactly one of create / update / delete. key identifies an object by its user-defined primary-key fields, or (for update/delete) by {"xuid": ...}. A None value in object values clears that field. A RelationMutation creates or deletes a relation through its endpoints; this caller-addressing format does not mean object primary keys are required for relations extracted from text. XMD v1 relations do not have scalar fields:

inst.write(structured_mutations=[
ObjectMutation(
object_type="person",
update=ObjectUpdate(key={"email": "carol@initech.com"}, values={"role": None}),
),
])

Plain dicts in the API wire form work anywhere a model does — handy for payloads produced by an LLM:

inst.write(structured_mutations=[
{"object_mutation": {"object_type": "person", "delete": {"key": {"email": "carol@initech.com"}}}},
])

write_async accepts structured_mutations identically — poll with write_status as usual. See the API reference for the full wire format, relation addressing (endpoints, allow_bulk_delete), and in-batch ordering rules.


A write normally reconciles whatever the extractor produced against every record in the instance — the right default for “here is something new”, the wrong one for “here is more about this record”. Pass a scope to anchor a text write to records you already know about:

from xmemory import ScopeObject, WriteScope
resp = inst.write(
"After her promotion she is a surgeon, and her desk phone is +1-555-0100.",
scope=WriteScope(
objects=[ScopeObject(type="Person", key={"name": "Alice Johnson"})],
),
)
print(resp.changes) # Alice updated in place, not duplicated

The scoped objects’ current values are shown to the extractor, so the new information is folded into them instead of producing a near-duplicate; the write is then confined to the scope, free to modify or delete the scoped objects and to create new objects and relations anchored to them, but failing with an XmemoryAPIError if it would touch anything else. The confinement is checked against the resulting plan rather than asked of the extractor, so it holds regardless of what the extraction produced. See Scoped writes for the wire format and the full list of rejections.

WriteScope takes the same ScopeObjects as ReadScope, identified the same way. Scope everything the write should be able to change — a record left out may still be linked to, but not edited:

resp = inst.write(
"The Atlas migration finished on Tuesday and Bob Lee signed it off.",
scope=WriteScope(objects=[
ScopeObject(type="Project", key={"name": "Atlas"}),
ScopeObject(type="Person", key={"name": "Bob Lee"}),
]),
)

Unlike ReadScope there is no relations_scope: the relations among the scoped objects always accompany the extraction hint, and relation changes are governed by the confinement rule instead of a mode.

write_async accepts the same scope; a scope violation is reported by write_status as a failed write.

  • Scope applies to text writes only. Combining it with structured_mutations raises ValueError in the client, since those bypass extraction and leave a scope nothing to anchor to.
  • Only objects of a type with a user-defined primary key can be scoped: a scope names records by that key, so a type declared primary_key: [] has nothing to name its records by.
  • The server accepts a scope with fast extraction only, caps how many objects one scope may name, and can have scoped writes disabled entirely. Those are deployment rules, so they surface as an XmemoryAPIError rather than being second-guessed client-side.
  • A scoped write additionally requires read permission on the instance, on top of write permission: the scoped objects’ current field values ride the extraction prompt, which makes a scope-carrying write a read of those rows.

Ask questions in natural language. xmemory translates them into SQL against the knowledge graph and returns a formatted answer.

resp = inst.read("Who works at Acme Corp?")
print(resp.reader_result)
print(resp.trace_id)
from xmemory import ReadMode
# Plain-text answer (default)
resp = inst.read("What is Alice's email?", read_mode=ReadMode.SINGLE_ANSWER)
# → {"answer": "alice@acme.com"}
# Structured objects and relations
resp = inst.read("Show all contacts", read_mode=ReadMode.XRESPONSE)
# → {"objects": [...], "relations": [...]}
# Raw SQL result sets
resp = inst.read("List all contacts", read_mode=ReadMode.RAW_TABLES)
# → {"columns": [...], "rows": [...]}, or None when nothing matched

A question holding several independent parts is answered in one call. The server splits it into sub-questions, answers each on its own, and runs them against one shared snapshot. Nothing has to be enabled or passed.

resp = inst.read("Who owns billing, and when did the last incident close?")
resp.reader_result # the combined answer
for part in resp.reader_results:
print(part.sub_query, part.reader_result, part.error)

Each part’s reader_result is in the read_mode you asked for, so a RAW_TABLES composite question gives one table per part.

reader_results is always a list — the wire response omits it for a single-intent question and the client normalizes that to [], so an empty list means the question was not split. A part that could not be answered sets its own error and leaves the others intact, so check error per entry rather than assuming the whole call failed.

Ask with include_related_types=RelatedTypesMode.TYPES and related_types on the result says what else the memory could answer about: the object types the read touched, the fields it did not return, and the object types a declared relation links them to, with a catalog of every named type and relation. The shape is documented on the REST API.

from xmemory import RelatedTypesMode
resp = inst.read("Which courses require an English test?", include_related_types=RelatedTypesMode.TYPES)
for touched in resp.related_types.touched:
print(touched.object_type, touched.fields_not_returned, [link.object_type for link in touched.related])

related_types is None unless the read asked for it. The API key needs instance.get_own, the permission the schema endpoints need; without it the read raises XmemoryAPIError with .status 403 and a message naming the permission. Available from xmemory-ai 0.18.0.

Add related_types_depth=2 (up to 3) to follow the relations further: every entry under related_types.objects then carries its distance from the touched types and, below the last level, its own related edges; related_types.depth echoes the depth asked for and omitted_objects counts what the server’s budgets kept out of the catalog (shape). On a one-level read these fields are absent and read as their defaults (distance is None). Available from xmemory-ai 0.20.0.

deep = inst.read("Which courses require an English test?", include_related_types=RelatedTypesMode.TYPES, related_types_depth=2)
print({name: entry.distance for name, entry in deep.related_types.objects.items()})

Restrict a query to a specific set of objects. Useful for multi-tenant systems, privacy controls, and compliance requirements.

from xmemory import ReadScope, ScopeObject
scope = ReadScope(
objects=[
ScopeObject(type="Person", key={"name": "Alice Johnson"})
],
relations_scope="no_relations",
)
resp = inst.read("What is their email?", scope=scope)
print(resp.reader_result)

Each ScopeObject names one object by its type — the PascalCase class name or the snake_case table name — plus its user-defined primary key, with one entry per primary-key field. Only objects of a type that declares a primary key can be scoped.

scope = ReadScope(
objects=[
ScopeObject(type="Person", key={"email": "alice@example.com"}),
ScopeObject(type="Person", key={"email": "bob@example.com"}),
],
relations_scope="all_relations",
)
resp = inst.read("Which projects do these people work on together?", scope=scope)

relations_scope takes one of two string values:

  • "no_relations" (default): Only the specified objects.
  • "all_relations": Also expose the relations among the specified objects.

A ScopeObject naming an object the instance does not hold does not raise. It drops out of the scope, and the read answers from whatever is left — an empty result when nothing matched, or the objects that do exist when only some did. So you can scope to an object before you know whether it has been written yet, which is the opposite of a scoped write, where every named object must exist.

The trade-off: an aggregate is over the objects that were found, not the ones you listed. If it matters that every entry resolved, read the objects back rather than trusting a count over them. See Scoped reads for the full rules, including the malformed scopes that do still raise.

admin.get_setup_instructions(instance_id) and inst.setup_instructions() both return an AgentSetupResult: how to reach the same memory from another agent surface, ordered most-likely-first. On either handle, because the MCP instance connection serves the same tool.

setup = inst.setup_instructions()
for surface in setup.surfaces:
print(surface.label)
for step in surface.steps:
print(" ", step.description, step.command or "")

The default SetupFormat.AGENT answers what do I run right now, here. SetupFormat.PROJECT also returns the files a team commits once, so nobody sets the instance up by hand:

from xmemory import SetupFormat
setup = inst.setup_instructions(format=SetupFormat.PROJECT)
if setup.format == SetupFormat.PROJECT and setup.project:
for fragment in setup.project.fragments:
print(fragment.path, fragment.merge) # a merge, never a file to overwrite

Check setup.format rather than assuming. A server older than that parameter ignores it and still answers 200, so asking for PROJECT is not the same as receiving it.

Nothing returned carries a credential: the steps tell a reader to sign in themselves, out of band, so an instance id stays an identifier rather than a key.

Advisory values — step.kind, fragment.merge, format — arrive as enum members when this release knows them and as plain strings when it does not, so a value added to the server later cannot make the whole result unparseable. A step.kind you do not recognise is not something to execute.

from xmemory import XmemoryAPIError
try:
resp = inst.read("...", scope=scope)
except XmemoryAPIError as e:
if e.status == 400:
print(f"Invalid scope: {e.message}")
else:
raise

Preview what xmemory would extract from a piece of text, without storing anything:

resp = inst.extract("Dave manages the London office.")
print(resp.objects_extracted)
print(resp.trace_id)

Accepts the same extraction_logic parameter as write.


The describe() method returns the agent-facing tool descriptions for an instance, with its schema in schema_summary. Use it to tell an LLM what tools are available and how to call them.

desc = inst.describe()
# Plain text — inject into a system prompt
print(desc.as_text())
# Anthropic tool-use format
tools = desc.as_anthropic_tools()
# OpenAI function-calling format
tools = desc.as_openai_tools()

Results are cached locally for 5 minutes. To force a refresh (e.g. after updating the schema):

inst.clear_describe_cache()
desc = inst.describe()

as_text() shows tools as method signatures by default. Pass include_http=True to also show HTTP method and path for raw REST callers.

describe() also carries what the instance is for:

desc.purpose # what the memory is for (the instance description)
desc.owner_instructions # the standing preference set for it, verbatim
desc.usage_brief # generated from the schema; None until generated

as_text() includes the first two — the purpose under the instance line, the standing preference above the schema summary. usage_brief is left out of it, since it restates the schema summary already there; read the attribute if you want it.

Both are free text set by anyone holding edit permission on the instance, so as_text() labels each with where it came from rather than presenting it as the library’s own words. Those labels state provenance; they are not a security boundary. If you inject this into a system prompt you are still handling text you do not control.


These methods live on client.admin and cover the control plane: discovering clusters, generating schemas, creating instances, and evolving them safely.

clusters = client.admin.list_clusters()
for c in clusters:
print(f"{c.id}: {c.name}")

Describe what you want to track in plain language when generation is more convenient than writing XMD directly. Agents may use either path:

schema = client.admin.generate_schema(
cluster_id,
"Track user preferences, open tasks with priorities, and conversation history.",
)
print(schema.data_schema)

Object names use CamelCase (UserPreferences, OpenTask). The generation endpoint handles naming conventions automatically. The returned document is XMD, whose descriptions fine-tune extraction and permit grounded derivation rather than merely defining domain terminology. To evolve an existing schema (rename / remove / type change) rather than create one, retrieve the current XMD and use enhance_schema — it returns a structured migration plan; see Schema evolution.

Create an instance from a generated schema:

import yaml
from xmemory import SchemaType
inst = client.admin.create_instance(
cluster_id=cluster_id,
name="my-memory",
schema_text=yaml.dump(schema.data_schema),
schema_type=SchemaType.YML,
description="Optional description",
)
# inst is a bound InstanceAPI — use inst.write(), inst.read(), etc.

For a purely additive change (new objects/fields/relations), update the schema directly — existing data is preserved:

new_schema = client.admin.generate_schema(
cluster_id,
"Add an assignee field to tasks.",
current_yml_schema=current_schema,
)
client.admin.update_instance_schema(
instance_id, yaml.dump(new_schema.data_schema), SchemaType.YML
)

List and delete instances:

instances = client.admin.list_instances()
info = client.admin.get_instance(instance_id)
client.admin.delete_instance(instance_id)

An instance can carry metadata that shapes how agents connect to it and what they do with it. Use patch_instance_metadata to set the advisory hints: every argument is independent — omitting one leaves the stored value untouched, and passing None clears it.

from xmemory import AgentSurface, BindingTier
client.admin.patch_instance_metadata(
instance_id,
# Advisory hints — they seed what a connect flow proposes, and grant nothing.
agent_surfaces=[AgentSurface.CLAUDE_CODE, AgentSurface.CODEX],
agent_default_binding_tier=BindingTier.AUTOLOAD,
agent_engagement_hints=["a convention is learned or corrected"],
)

Concurrent edits to these three are last-writer-wins by design: they only seed what a connect flow proposes, so the loser of a race re-applies a suggestion. agent_owner_instructions is not like that.

Read it back from InstanceInfo. The hints come back as plain strings, so a value your server knows and your library version does not is returned rather than rejected:

info = client.admin.get_instance(instance_id)
info.agent_owner_instructions
info.agent_surfaces # e.g. ["claude_code", "codex"]
info.agent_default_binding_tier # e.g. "autoload"

Set agent_owner_instructions through update_instance_metadata, not patch_instance_metadata. It is rendered to agents verbatim and a second writer edits it from the same screen, so a silently lost edit is a rule that stops being enforced — and update_instance_metadata is the only call that carries expected_owner_instructions_epoch. Pass the epoch you read the value at and the losing save is refused instead of applied:

info = client.admin.get_instance(instance_id)
client.admin.update_instance_metadata(
instance_id, info.name, info.description,
agent_owner_instructions=(info.agent_owner_instructions or "") + "\nAlso: never paraphrase a rule.",
expected_owner_instructions_epoch=info.agent_owner_instructions_epoch,
)

Non-additive changes (rename / remove / type change) preserve data via a structured migration plan. Use enhance_schema to get the plan, dry_run_migration to preview the DDL, then update_instance_schema to apply it. confirm_destructive=True is required for ops that drop data.

current = client.admin.get_instance_schema(instance_id).data_schema
# 1. Enhance — new schema + an executor-ready migration plan.
enhanced = client.admin.enhance_schema(
cluster_id,
"Rename Person.mail to Person.email.",
yaml.dump(current),
)
print(enhanced.summary)
for op in enhanced.migration_plan.ops:
print(op)
new_yaml = yaml.dump(enhanced.data_schema)
# 2. Dry-run — preview the DDL, apply nothing.
preview = client.admin.dry_run_migration(
instance_id, new_yaml, SchemaType.YML, migration_plan=enhanced.migration_plan,
)
print(preview.statements)
# 3. Update — apply. A rename is non-destructive, so confirm_destructive stays False.
info = client.admin.update_instance_schema(
instance_id, new_yaml, SchemaType.YML,
migration_plan=enhanced.migration_plan, confirm_destructive=False,
)
print(info.migration_id, info.prior_version, "->", info.new_version)

Review applied migrations:

page = client.admin.list_migrations(instance_id, limit=20)
for r in page.items:
print(r.id, r.source, r.prior_version, "->", r.new_version)
detail = client.admin.get_migration(instance_id, page.items[0].id, include_yaml=True)
print(detail.yaml_before, detail.yaml_after)

Migration ops are exported as typed models (MigrationPlan, AddField, RenameField, RemoveObject, …). ProposalItem.op and MigrationRecord.ops stay as raw dicts for forward compatibility — use parse_migration_op(...) to validate them.

Suggestion engine (review → decide → apply)

Section titled “Suggestion engine (review → decide → apply)”

xmemory watches read traffic and, on demand, surfaces a single rolling proposal of schema improvements. The flow is three calls — never wrap them; the checkpoints are the point.

from xmemory import DecisionInput
inst = client.instance(instance_id)
# 1. Review — proposal + optimistic-concurrency token.
review = inst.review_suggestions()
if review.status == "evolution_in_progress":
print(f"Migration in flight; retry in {review.retry_after_seconds}s")
else:
proposal = review.proposal
for item in proposal.items:
print(item.item_fingerprint, item.rationale, item.op)
# 2. Decide — accept / reject / defer per item, in bulk.
decided = inst.decide_suggestions(
proposal.proposal_version,
[DecisionInput(item_fingerprint=i.item_fingerprint, decision="accept")
for i in proposal.items],
)
# 3. Apply — commit accepted decisions as one migration.
applied = inst.apply_pending_decisions(decided.next_proposal_version)
print(applied.status, applied.summary)

All errors raise XmemoryAPIError. The exception carries an optional .status (HTTP status code), .code (structured error code, when the server returned one), .details, and .retry_after (int seconds parsed from the Retry-After header, >= 0, or None).

from xmemory import XmemoryAPIError, XmemoryHealthCheckError
# Check connectivity
try:
client.check_health()
except XmemoryHealthCheckError as e:
print(f"API unreachable: {e}")
# Handle operation errors
try:
inst.write("...")
except XmemoryAPIError as e:
print(f"Error (HTTP {e.status}): {e}")

XmemoryHealthCheckError is a subclass of XmemoryAPIError, so catching XmemoryAPIError covers both.

The schema-evolution methods set .code to a structured error type you can match on — e.g. stale_proposal_version, destructive_confirmation_required, non_additive_change_requires_plan:

try:
inst.apply_pending_decisions(token)
except XmemoryAPIError as e:
if e.code == "stale_proposal_version":
review = inst.review_suggestions() # re-review and retry

The same .code branching separates a spent allowance from a genuine rate limit — match on .code, not on the status:

try:
inst.write("...")
except XmemoryAPIError as e:
if e.code == "QUOTA_EXCEEDED":
# Plan/usage allowance exhausted; e.details["kind"] is
# "daily_quota_exceeded" or "monthly_quota_exceeded".
# Not retryable now — surface "limit reached / upgrade your plan".
# You may retry after e.details["retry_after_seconds"] (if not None).
...
elif e.code == "RATE_LIMITED":
# Genuine rate limit — back off and retry, honoring Retry-After.
...
else:
raise

For both RATE_LIMITED (429) and a resettable QUOTA_EXCEEDED (402), use e.retry_after — the seconds parsed from the Retry-After header (an int >= 0, or None when the header was absent or in HTTP-date form) — as your backoff delay.


The complete method, data-type, enum, and exception listing. Methods are grouped by where they live; expand a row for its parameters, returns, and details.

Constructors and client-level handles.

XmemoryClientCreate a sync client
XmemoryClient(url=None, *, timeout=60, api_key=None, http_client=None)
urlstr
API base URL; falls back to XMEM_API_URL. The only positional arg. Defaults to None.
timeoutfloat
Default request timeout in seconds. Defaults to 60.
api_keystr
API key for authentication; falls back to XMEM_API_KEY. Defaults to None.
http_clienthttpx.Client
External HTTP client (you manage its lifecycle). Defaults to None.
AsyncXmemoryClientCreate an async client (same signature; methods are awaitable)
AsyncXmemoryClient(url=None, *, timeout=60, api_key=None, http_client=None)
urlstr
API base URL; falls back to XMEM_API_URL. The only positional arg. Defaults to None.
timeoutfloat
Default request timeout in seconds. Defaults to 60.
api_keystr
API key for authentication; falls back to XMEM_API_KEY. Defaults to None.
http_clienthttpx.AsyncClient
External HTTP client (you manage its lifecycle). Defaults to None.
adminControl-plane accessor — clusters, schema generation, instances, schema evolution
client.adminAdminAPI

client.admin (AsyncAdminAPI on the async client) — the receiver for the admin / control-plane methods (clusters, schema generation, instances, schema evolution).

instanceGet a handle for data operations on an instance
client.instance(instance_id)InstanceAPI
instance_idstrrequired
The instance ID to operate on.

A bound InstanceAPI handle (AsyncInstanceAPI on the async client) — call write / read / extract / … on it.

check_healthRaise XmemoryHealthCheckError if the API is unreachable
client.check_health()

None on success; raises XmemoryHealthCheckError otherwise.

closeClose the underlying HTTP client (no-op if you supplied your own)
client.close()

Async client: use await client.aclose() instead — or prefer the context manager (with / async with).

acloseAsync counterpart of close() (AsyncXmemoryClient)
await client.aclose()

Control-plane methods on client.admin — clusters, schema generation, instances, and schema evolution.

list_clustersList clusters
list_clusters(*, ids=None, timeout=None)list[ClusterInfo]
idslist[str]
Filter to specific cluster IDs.
timeoutfloat
Per-call timeout in seconds.
get_clusterGet a cluster by ID
get_cluster(cluster_id, *, timeout=None)ClusterInfo
cluster_idstrrequired
The cluster ID to fetch.
timeoutfloat
Per-call timeout in seconds.
generate_schemaGenerate a schema from a plain-language description
generate_schema(cluster_id, schema_description, *, current_yml_schema=None, timeout=None)GenerateSchemaResult
cluster_idstrrequired
The cluster to generate the schema for.
schema_descriptionstrrequired
Plain-language description of what to track.
current_yml_schemastr
Existing schema to extend additively.
timeoutfloat
Per-call timeout in seconds.
enhance_schemaEvolve a schema → new schema + migration plan
enhance_schema(cluster_id, schema_description, current_yml_schema, *, timeout=None)EnhanceSchemaResult
cluster_idstrrequired
The cluster the schema belongs to.
schema_descriptionstrrequired
Plain-language description of the change.
current_yml_schemastrrequired
The current schema to evolve.
timeoutfloat
Per-call timeout in seconds.

Pass the returned migration_plan to dry_run_migration / update_instance_schema.

create_instanceCreate a new instance; returns a bound handle
create_instance(cluster_id, name, schema_text, schema_type, *, description=None, schema_description=None, timeout=None)InstanceAPI
cluster_idstrrequired
The cluster to create the instance in.
namestrrequired
Name for the instance.
schema_textstrrequired
The schema document text.
schema_typeSchemaTyperequired
Format of the schema (YML or JSON).
descriptionstr
Description.
schema_descriptionstr
Plain-language schema description.
timeoutfloat
Per-call timeout in seconds.

A bound InstanceAPI handle for the new instance — call inst.id to get its ID.

list_instancesList instances
list_instances(*, ids=None, timeout=None)list[InstanceInfo]
idslist[str]
Filter to specific instance IDs.
timeoutfloat
Per-call timeout in seconds.
get_instanceGet an instance by ID
get_instance(instance_id, *, timeout=None)InstanceInfo
instance_idstrrequired
The instance ID to fetch.
timeoutfloat
Per-call timeout in seconds.
get_instance_schemaGet an instance's schema
get_instance_schema(instance_id, *, timeout=None)InstanceSchemaInfo
instance_idstrrequired
The instance whose schema to fetch.
timeoutfloat
Per-call timeout in seconds.
update_instance_schemaUpdate a schema; pass migration_plan for non-additive changes
update_instance_schema(instance_id, schema_text, schema_type, *, migration_plan=None, confirm_destructive=False, timeout=None)InstanceInfo
instance_idstrrequired
The instance to update.
schema_textstrrequired
The new schema document text.
schema_typeSchemaTyperequired
Format of the schema (YML or JSON).
migration_planMigrationPlan | dict
Plan for non-additive changes (from enhance_schema).
confirm_destructivebool
Required for ops that drop data. Defaults to False.
timeoutfloat
Per-call timeout in seconds.

The migration_id / prior_version / new_version / migration_warnings fields are populated when a migration ran.

Raises XmemoryAPIError with .code non_additive_change_requires_plan (no plan) or destructive_confirmation_required (drops data without confirm_destructive=True).

update_instance_metadataUpdate instance name and description
update_instance_metadata(instance_id, name, description, *, agent_owner_instructions=UNSET, expected_owner_instructions_epoch=UNSET, timeout=None)InstanceInfo
instance_idstrrequired
The instance to update.
namestrrequired
New name for the instance.
descriptionstr | Nonerequired
New description (pass None to clear).
agent_owner_instructionsstr | None
Standing preference for how agents should use this instance. Omit to leave it as it is; pass None to clear it. Max 2000 characters. Defaults to UNSET.
expected_owner_instructions_epochint
The agent_owner_instructions_epoch your edit was composed from. Defaults to UNSET.
timeoutfloat
Per-call timeout in seconds.

Omitting an argument sends no such key at all, so a rename never disturbs the standing preference.

When expected_owner_instructions_epoch is sent and the stored instructions changed after you read them, the save is refused rather than applied: XmemoryAPIError with .status 409 and .code INVALID_INPUT. Re-read the instance, reapply your change, and save again.

patch_instance_metadataChange some metadata fields, leaving the rest alone
patch_instance_metadata(instance_id, *, name=UNSET, description=UNSET, agent_surfaces=UNSET, agent_default_binding_tier=UNSET, agent_engagement_hints=UNSET, agent_owner_instructions=UNSET, timeout=None)InstanceInfo
instance_idstrrequired
The instance to update.
namestr
New name for the instance. Defaults to UNSET.
descriptionstr | None
New description (pass None to clear). Defaults to UNSET.
agent_surfacesSequence[str] | None
Agent surfaces this instance is expected to be used from — see AgentSurface. Advisory: it orders the connect steps and grants nothing. Defaults to UNSET.
agent_default_binding_tierstr | None
Default prominence for a binding — see BindingTier. Advisory. Defaults to UNSET.
agent_engagement_hintsSequence[str] | None
Short routing phrases, e.g. "a convention is learned or corrected". At most 16, each at most 200 characters. Defaults to UNSET.
agent_owner_instructionsstr | None
Standing preference for how agents should use this instance. Max 2000 characters. Prefer update_instance_metadata for this field — it is the only call that accepts expected_owner_instructions_epoch, so an edit sent here cannot be guarded against a concurrent one. Defaults to UNSET.
timeoutfloat
Per-call timeout in seconds.

Every argument is independent: omit one and the stored value is untouched, pass None to clear it. This is the only call that accepts the three agent_* hints; update_instance_metadata is the only one that accepts the epoch guard.

get_setup_instructionsHow to connect this instance on an agent surface, most likely first
get_setup_instructions(instance_id, *, format=SetupFormat.AGENT, timeout=None)AgentSetupResult
instance_idstrrequired
The instance to describe connect steps for.
formatSetupFormat | str
AGENT returns the steps to run now. PROJECT additionally returns the files a team commits once, so nobody sets the instance up by hand. Defaults to SetupFormat.AGENT.

Answers "how do I also reach this memory somewhere else" — a desktop client, another editor, a teammate's machine — rather than how to reach it from here. The same payload the get_setup_instructions MCP tool serves and xmemcli instance setup prints.

Also on an instance handle as instance.setup_instructions(), without the id.

Computed from the instance's current metadata, so editing its surface hints changes the next response.

Carries no credential. The steps tell a reader to sign in themselves, out of band, so nothing returned is a secret and an instance id stays an identifier rather than a key.

Read result.format rather than assuming: a server older than the format parameter ignores it and still answers 200, so asking for PROJECT is not the same as receiving it.

Advisory values tolerate a newer server. step.kind, fragment.merge and format come back as enum members when this release knows them and as plain strings when it does not, so a value added later cannot make the whole result unparseable. A step.kind you do not recognise is not something to execute.

delete_instanceDelete an instance
delete_instance(instance_id, *, timeout=None)list[str]
instance_idstrrequired
The instance ID to delete.
timeoutfloat
Per-call timeout in seconds.

The deleted instance IDs.

dry_run_migrationPreview a migration's DDL
dry_run_migration(instance_id, schema_text, schema_type, *, migration_plan=None, confirm_destructive=False, timeout=None)DryRunResult
instance_idstrrequired
The instance to preview against.
schema_textstrrequired
The new schema document text.
schema_typeSchemaTyperequired
Format of the schema (YML or JSON).
migration_planMigrationPlan | dict
Plan for non-additive changes.
confirm_destructivebool
Required for ops that drop data. Defaults to False.
timeoutfloat
Per-call timeout in seconds.

statements are the planned DDL; nothing is applied.

list_migrationsList applied migrations (newest first)
list_migrations(instance_id, *, limit=50, before_id=None, include_yaml=False, timeout=None)ListMigrationsResult
instance_idstrrequired
The instance to list migrations for.
limitint
Maximum number of records to return. Defaults to 50.
before_idstr
Page cursor: pass the previous page's next_before_id.
include_yamlbool
Include the before/after YAML in each record. Defaults to False.
timeoutfloat
Per-call timeout in seconds.

Page with next_before_id — pass it as before_id to fetch the next page.

get_migrationGet a single migration record
get_migration(instance_id, migration_id, *, include_yaml=False, timeout=None)MigrationRecord
instance_idstrrequired
The instance the migration belongs to.
migration_idstrrequired
The migration record ID.
include_yamlbool
Include the before/after YAML snapshots. Defaults to False.
timeoutfloat
Per-call timeout in seconds.

yaml_before / yaml_after are populated only when include_yaml=True.

Data-plane methods on a bound instance (client.instance(id)) — writing, reading, extracting, describing, and the suggestion engine.

writePersist data — extract objects from text, or apply structured mutations
write(text="", *, structured_mutations=None, extraction_logic=ExtractionLogic.FAST, diff_engine=None, scope=None, timeout=None)WriteResult
textstr
Free-form text to extract from. Exactly one of text / structured_mutations must be provided (enforced with ValueError).
structured_mutationslist
Ordered list of ObjectMutation / RelationMutation models (or equivalent wire-form dicts): deterministic object create/update/delete and relation create/delete mutations — see Structured writes. Mutually exclusive with text.
extraction_logicExtractionLogic
Speed/accuracy tradeoff (FAST default, or DEEP). Text writes only. Defaults to ExtractionLogic.FAST.
diff_enginebool
Override the server's diff-engine setting for this write. Text writes only.
scopeWriteScope | None
Anchor a text write to concrete existing objects. Their current values are shown to the extractor so the write updates them instead of duplicating them, and the write is then confined to them — touching any other existing object fails. Text writes only (combining it with structured_mutations raises ValueError); the server accepts it with fast extraction only, and it additionally requires read permission on the instance. See Scoped writes.
timeoutfloat
Per-call timeout in seconds.
write_asyncEnqueue a write; returns write_id immediately
write_async(text="", *, structured_mutations=None, extraction_logic=ExtractionLogic.FAST, diff_engine=None, scope=None, timeout=None)AsyncWriteResult
textstr
Free-form text to extract from. Exactly one of text / structured_mutations must be provided.
structured_mutationslist
Same structured-mutation input as write. Mutually exclusive with text.
extraction_logicExtractionLogic
Speed/accuracy tradeoff (FAST default, or DEEP). Text writes only. Defaults to ExtractionLogic.FAST.
diff_enginebool
Override the server's diff-engine setting for this write. Text writes only.
scopeWriteScope | None
Anchor a text write to concrete existing objects. Their current values are shown to the extractor so the write updates them instead of duplicating them, and the write is then confined to them — touching any other existing object fails. Text writes only (combining it with structured_mutations raises ValueError); the server accepts it with fast extraction only, and it additionally requires read permission on the instance. See Scoped writes.
timeoutfloat
Per-call timeout in seconds.

Poll for completion with write_status using the returned write_id.

write_statusPoll the status of an async write
write_status(write_id, *, timeout=None)WriteStatusResult
write_idstrrequired
The write ID returned by write_async.
timeoutfloat
Per-call timeout in seconds.
readQuery the instance in natural language
read(query, *, read_mode=ReadMode.SINGLE_ANSWER, scope=None, include_related_types=None, related_types_depth=None, read_id=None, timeout=None)ReadResult
querystrrequired
Natural-language question.
read_modeReadMode
Result shape (SINGLE_ANSWER default, XRESPONSE, or RAW_TABLES). Defaults to ReadMode.SINGLE_ANSWER.
scopeReadScope | None
Restrict the read to concrete objects, and optionally to the relations among them. See Scoped reads.
include_related_typesRelatedTypesMode | None
RelatedTypesMode.TYPES asks for related_types on the result. Sent only when set. Needs instance.get_own on the API key. See Related types.
related_types_depthint | None
With RelatedTypesMode.TYPES, how many relation levels related_types.objects follows out from the touched types, 1 to 3. Sent only when set. See Related types.
read_idstr
Correlation ID for the read.
timeoutfloat
Per-call timeout in seconds.

reader_result is shaped by read_mode (e.g. {"answer": ...}).

A question holding several independent parts is split server-side into sub-questions, and reader_results then holds one entry per sub-question — each { sub_query, reader_result, error }, with every reader_result in the requested mode. reader_result stays the combined value. Unlike the REST response, which omits the key entirely for a single-intent question, the client always gives you a list here: it is empty unless the question was split. A sub-question that could not be answered sets its own error and leaves the others intact.

related_types is a RelatedTypes when the read asked for it with include_related_types, and None otherwise. See Related types.

extractExtract objects without writing them
extract(text, *, extraction_logic=ExtractionLogic.FAST, timeout=None)ExtractResult
textstrrequired
Free-form text to extract from.
extraction_logicExtractionLogic
Speed/accuracy tradeoff (FAST default, or DEEP). Defaults to ExtractionLogic.FAST.
timeoutfloat
Per-call timeout in seconds.
describeGet agent-facing tool descriptions (cached 5 min)
describe(*, timeout=None)DescribeResult
timeoutfloat
Per-call timeout in seconds.
clear_describe_cacheForce next describe() to fetch fresh data
clear_describe_cache()None
get_schemaGet this instance's schema
get_schema(*, timeout=None)InstanceSchemaInfo
timeoutfloat
Per-call timeout in seconds.
setup_instructionsHow to connect *this* instance somewhere else, most likely surface first
setup_instructions(*, format=SetupFormat.AGENT, timeout=None)AgentSetupResult
formatSetupFormat | str
AGENT returns the steps to run now; PROJECT also returns the files a team commits once. Defaults to SetupFormat.AGENT.

The instance-scoped counterpart of admin.get_setup_instructions(), which takes an id. On both because the MCP instance connection serves the same tool, so a caller holding an instance handle need not reach through admin for it.

Not cached, unlike describe(): computed from the instance's current metadata, so an owner who edits a surface hint expects the next call to show it.

review_suggestionsGet the rolling schema-improvement proposal
review_suggestions(*, session_id=None, timeout=None)ReviewSuggestionsResult
session_idstr
Session correlation ID.
timeoutfloat
Per-call timeout in seconds.

On status evolution_in_progress, back off for retry_after_seconds and retry — it's a status, not an error.

decide_suggestionsRecord accept/reject/defer decisions in bulk
decide_suggestions(proposal_version, decisions, *, session_id=None, timeout=None)DecideSuggestionsResult
proposal_versionstrrequired
Optimistic-concurrency token from the review.
decisionslist[DecisionInput]required
Per-item decisions — DecisionInput(item_fingerprint, decision, edits=None).
session_idstr
Session correlation ID.
timeoutfloat
Per-call timeout in seconds.

Pass next_proposal_version straight to apply_pending_decisions without re-reviewing.

Raises XmemoryAPIError with .code stale_proposal_version (token out of date) or invalid_decision_input.

apply_pending_decisionsApply accepted decisions as one migration
apply_pending_decisions(proposal_version, *, session_id=None, timeout=None)ApplyPendingDecisionsResult
proposal_versionstrrequired
The next_proposal_version from the decision step.
session_idstr
Session correlation ID.
timeoutfloat
Per-call timeout in seconds.

status nothing_to_apply means no accepted items were left to apply.

Raises XmemoryAPIError with .code stale_proposal_version, dependency_closure_failed, or apply_failed.

Data types
ClusterInfoA cluster.
idstr
Cluster ID.
org_idstr
Owning organization ID.
namestr
Cluster name.
descriptionstr | None
Cluster description.
InstanceInfoAn instance's metadata.
idstr
Instance ID.
cluster_idstr
The cluster this instance belongs to.
namestr
Instance name.
descriptionstr | None
Instance description.
data_schemadict | None
The instance's data schema.
agent_surfaceslist[str] | None
Agent surfaces this instance is expected to be used from. Advisory; None means no opinion. Read as plain strings, so a value newer than your library version is returned rather than rejected.
agent_default_binding_tierstr | None
Default prominence for a binding (autoload / available). Advisory.
agent_engagement_hintslist[str] | None
Short routing phrases seeding when an agent should engage this instance.
agent_owner_instructionsstr | None
The standing preference set for how agents should use this instance, rendered verbatim wherever it is shown.
agent_owner_instructions_epochint
Which edit of the instructions above this response describes. Pass it back as expected_owner_instructions_epoch to make an edit conflict-safe.
migration_idstr | None
Set only by update_instance_schema when a migration ran.
prior_versionint | None
Schema version before the migration (migration responses only).
new_versionint | None
Schema version after the migration (migration responses only).
migration_warningslist[str] | None
Non-fatal migration warnings (migration responses only).
InstanceSchemaInfoAn instance's schema.
data_schemadict
The instance's data schema.
WriteResultResult of a synchronous write.
write_idstr
ID of the committed write.
changesAny
What the write did, grouped into created / updated / deleted (null on responses from an older server).
trace_idstr | None
Correlate the call with API logs.
AsyncWriteResultAcknowledgement of an enqueued write.
write_idstr
Poll completion with write_status.
WriteStatusResultStatus of an async write.
write_idstr
The write being polled.
write_statusWriteQueueStatus
Current queue status.
error_detailstr | None
Failure detail when write_status is FAILED.
completed_atdatetime | None
When the write completed.
ScopeObjectOne concrete object a read or write scope may touch.
typestr
Object type — PascalCase class name or snake_case table name.
keydictrequired
The object's user-defined primary key, one entry per primary-key field. Only objects of a type that declares a primary key can be scoped.
ReadScopeA read's scope.
objectslist[ScopeObject]
The objects the read may see.
relations_scopestr
"no_relations" (default) or "all_relations", which also exposes the relations among the scoped objects.
WriteScopeA write's scope.
objectslist[ScopeObject]
The existing objects the write is anchored to and confined to. There is no relation policy — the relations among them always accompany the extraction hint.
ReadResultResult of a read.
reader_resultAny
The formatted answer, shaped by read_mode (e.g. {"answer": ...}).
trace_idstr | None
Correlate the call with API logs.
related_typesRelatedTypes | None
The schema around the answer, when the read asked for it with include_related_types; None otherwise.
RelatedTypesWhat else the memory could answer about, for a read that asked with `include_related_types`. The shape is documented on the [REST API](/api/#related-types).
touchedlist[RelatedTypesTouched]
Object types the read touched, each { object_type, fields_not_returned, related, omitted_related } with related a list of RelatedTypesLink (object_type, relation, touched_role, related_role, cardinality). Empty when the read executed nothing.
objectsdict[str, RelatedTypesObjectType]
Every object type named in touched or reached within depth relation levels, once: description, primary_key, fields, and — on a read that asked for a depth above 1 — distance (listed edges from the nearest touched type; None otherwise), its own related edges below the last level, and omitted_related.
relationsdict[str, RelatedTypesRelation]
Every relation named by an edge, once: description.
depthint
Relation levels asked for (related_types_depth; 1 when unset). Never lowered by the server: what its budgets kept the walk from listing is counted on omitted_objects.
omitted_touchedint
Touched types the server dropped to stay within its payload budget.
omitted_objectsint
Object types within depth relation levels in the schema that the catalog does not list: the route to them ran through an edge the server's budget cut, or their level was dropped for its byte budget.
truncatedbool
True when any type, edge or level was dropped for the budget.
ExtractResultResult of an extract (nothing persisted).
objects_extractedAny
The objects that would be stored (objects / relations).
trace_idstr | None
Correlate the call with API logs.
DescribeResultAgent-facing tool descriptions, with `as_text(include_http=False)`, `as_anthropic_tools()`, and `as_openai_tools()` format helpers.
instance_idstr
The described instance.
instance_namestr
Instance name.
schema_summarystr
Human-readable schema summary.
toolslist[ToolDescription]
One entry per agent-callable tool.
purposestr | None
What this memory is for — the instance description, under the name the agent-facing surfaces give it. Included in as_text().
owner_instructionsstr | None
The standing preference set for this instance, rendered verbatim. Included in as_text(), labelled with where it came from rather than as the library's own words.
usage_briefstr | None
Generated from the schema; None until generated, and cleared again by a schema change. Left out of as_text(), since it restates schema_summary.
GenerateSchemaResultA generated schema.
data_schemadict
The produced schema as a dict.
EnhanceSchemaResultAn evolved schema plus an executor-ready migration plan.
data_schemadict
The produced schema as a dict.
migration_planMigrationPlan | None
Reconciled plan; pass to update_instance_schema / dry_run_migration.
summarystr | None
Human-readable summary of the change.
warningslist[dict]
Non-fatal reconciler observations.
repair_loglist[dict]
Auto-repairs applied to the LLM output.
DryRunResultPlanned migration DDL, with nothing applied.
status"ok"
Always "ok" for a successful dry run.
instance_idstr
The instance previewed against.
current_versionint
Current schema version.
statementslist[str]
The planned DDL statements.
warningslist[str]
Non-fatal warnings.
plan_summaryPlanSummary
Per-op-type breakdown.
requires_metadata_syncbool
Whether a metadata sync is needed after applying.
MigrationRecordOne applied-migration history row.
idstr
Migration record ID.
applied_atdatetime
When the migration was applied.
sourcestr
direct or suggestion_engine.
decided_bystr | None
Who decided the migration, if recorded.
prior_versionint
Schema version before.
new_versionint
Schema version after.
opslist[dict]
The raw ops applied (kept as dicts for forward-compat).
ops_summaryPlanSummary
Per-op-type breakdown.
notesstr | None
Free-text notes.
yaml_beforestr | None
Schema YAML before (only when include_yaml=True).
yaml_afterstr | None
Schema YAML after (only when include_yaml=True).
ListMigrationsResultA page of migration records, newest first.
status"ok"
Always "ok".
instance_idstr
The instance listed.
itemslist[MigrationRecord]
The records on this page.
next_before_idstr | None
Pagination cursor; None at the tail.
has_morebool
Whether more pages follow.
ReviewSuggestionsResultThe rolling schema-improvement proposal (or an in-progress status).
status"ok" | "evolution_in_progress"
evolution_in_progress means a migration is in flight.
instance_idstr
The instance reviewed.
proposalConsolidatedProposal | None
The proposal when status is "ok".
retry_after_secondsint | None
Back-off hint when evolution_in_progress.
DecideSuggestionsResultResult of recording a batch of decisions.
status"ok"
Always "ok".
instance_idstr
The instance decided on.
decisions_recordedlist[RecordedDecision]
One per recorded decision.
warningslist[DependencyWarning]
Advisory dependency warnings.
next_proposal_versionstr
Token to pass to apply_pending_decisions.
ApplyPendingDecisionsResultResult of applying accepted decisions as one migration.
status"ok" | "nothing_to_apply"
nothing_to_apply when no accepted items remained.
instance_idstr
The instance applied to.
migration_idstr | None
The committed migration's ID (when one ran).
prior_versionint
Schema version before.
new_versionint
Schema version after.
applied_itemslist[str]
Item fingerprints that were applied.
summarystr
Human-readable summary.
warningslist[str]
Non-fatal warnings.
noteslist[str]
Additional notes.
MigrationPlanAn ordered sequence of migration ops — the wire format emitted by `enhance_schema` and consumed by `update_instance_schema` / `dry_run_migration`.
opslist[MigrationOp]
The ordered ops.
MigrationOpOne migration operation — a discriminated union keyed on `op_type`.
op_typestr
Discriminator: one of add_object, remove_object, rename_object, change_object, add_field, remove_field, rename_field, change_field, add_relation, remove_relation, rename_relation, change_relation — each variant carries operation-specific parameters.
PlanSummaryPer-op-type breakdown of a migration plan.
count_by_op_typedict[str, int]
Op count keyed by op_type.
totalint
Total number of ops.
ConsolidatedProposalThe single rolling improvement proposal for an instance.
instance_idstr
The instance the proposal is for.
proposal_versionstr
Optimistic-concurrency token for decide_suggestions / apply_pending_decisions.
schema_versionint
Schema version the proposal targets.
itemslist[ProposalItem]
The accept/reject/defer-able items.
generated_atdatetime
When the proposal was generated.
noteslist[str]
Proposal-level notes.
ProposalItemOne decide-able item inside a proposal.
item_fingerprintstr
Stable ID; pass in a DecisionInput.
opdict
The proposed op (raw dict; parse with parse_migration_op).
evidence_feedback_idslist[str]
Feedback IDs that motivated the item.
evidence_query_sampleslist[str]
Sample queries that motivated the item.
frequencyint
How often the supporting signal occurred.
depends_onlist[str]
Fingerprints this item depends on.
current_decisionstr | None
Existing decision, if any.
rationalestr
Why the item was proposed.
DecisionInputOne decision in a `decide_suggestions` batch.
item_fingerprintstr
The ProposalItem to decide on.
decisionstr
One of accept, reject, defer.
editsdict | None
Optional op override (must keep the same op_type).
RecordedDecisionA persisted decision.
item_fingerprintstr
The decided item.
decision_idstr
ID of the recorded decision.
DependencyWarningAn advisory (non-blocking) dependency warning from `decide_suggestions`.
kindstr
Warning kind.
item_fingerprintstr
The item the warning concerns.
related_fingerprintslist[str]
Related item fingerprints.
related_summarieslist[str]
Human-readable summaries of the related items.
guidancestr
What to do about it.
ToolDescriptionOne agent-callable tool in a DescribeResult.
namestr
Tool name.
descriptionstr
What the tool does.
when_to_usestr
Guidance on when to call it.
parameterslist[ToolParameterDescription]
The tool's parameters.
http_methodstr
Underlying HTTP method.
http_pathstr
Underlying HTTP path.
ToolParameterDescriptionOne parameter of a ToolDescription.
namestr
Parameter name.
typestr
Parameter type.
descriptionstr
What the parameter is.
requiredbool
Whether it must be supplied.
enumlist[str] | None
Allowed values, if constrained.
defaultstr | None
Default value, if any.
Enums
EnumValues
SchemaTypeYML, JSON
ExtractionLogicFAST, DEEP
ReadModeSINGLE_ANSWER, RAW_TABLES, XRESPONSE
RelatedTypesModeNONE, TYPES
RelationCardinalityONE_TO_ONE, ONE_TO_MANY, MANY_TO_ONE, MANY_TO_MANY, UNCONSTRAINED
WriteQueueStatusQUEUED, PROCESSING, EXTRACTING, EXTRACTED, APPLYING, COMPLETED, FAILED, NOT_FOUND
Exceptions
ExceptionParentAttributes
XmemoryAPIErrorException.status, .code, .details, .retry_after (each None when absent)
XmemoryHealthCheckErrorXmemoryAPIError.status (HTTP status code or None)