Memory

Memories: named notes any agent of their scope can attach.

company = noclick.Memory.from_name("company", create_if_missing=True)
company.save("refunds", "Within 30 days, to the original card.", description="Our refund rules")
support = noclick.Agent.from_name("support", create_if_missing=True, instructions="...",
                                  memories=[company])              # it reads company
closer = noclick.Agent.from_name("closer", create_if_missing=True, instructions="...",
                                 memories=[company.writable()])     # it reads and writes it

Every agent also has a memory of its own, agent.memory, which it always reads and writes. A memory attached to it is read-only unless attached with writable(); every turn starts with the names and descriptions of every entry it can read, its own memory's first.

class Memory

Named notes any agent of its scope can attach: the account's (every user's agents may read them) or one user's. Its entries are what agent.memory holds: a name, a one-line description, Markdown content and a version.

A memory of the account keeps two layers, as an account agent's own memory does: its own entries, and each user's, which only that user's conversations see. A user's memory keeps one, its user's.

Attributes
idstr
namestr | None

Its name, unique in its scope (None for an agent's own memory).

descriptionstr | None

One line saying what it holds.

agent_idstr | None

The agent whose own memory this is (None for a memory with a name).

attached_tolist[dict[str, Any]]

The agents that attach it besides: each one's id, name and whether it writes.

entry_countint | None

How many entries it holds, in all its layers.

user_idstr | None
created_atAny
updated_atAny

createclassmethodasync

noclick.Memory.create(name, *, description=None, user=..., client=None) -> Memory

Create an empty memory called name (1-64 letters, digits and . _ -), unique to its user, or to the account outside as_user (a taken name raises noclick.Conflict with existing_id).

Parameters
namestrrequired
descriptionstr | Nonedefault None
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Memory

The new, empty memory.

Usage
company = noclick.Memory.create("company", description="What every agent should know")
company.save("tone", "Warm and brief.", description="How we write")

from_nameclassmethod

noclick.Memory.from_name(
    name, *, create_if_missing=False, description=None, user=..., client=None
) -> Memory

A lazy reference to the memory called name. Acting for a user, that user's own memory is found first, then the account's. With create_if_missing=True the current (or given) user's is created on first use.

Parameters
namestrrequired
create_if_missingbooldefault False
descriptionstr | Nonedefault None
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Memory

A memory that loads (or is created) the first time it's used. Nothing is sent yet.

Usage
company = noclick.Memory.from_name("company", create_if_missing=True)
support = noclick.Agent.from_name("support", create_if_missing=True,
                                  instructions="Answer billing questions.", memories=[company])

from_idclassmethod

noclick.Memory.from_id(memory_id, *, client=None, user=...) -> Memory

A lazy reference to a memory by id, such as an agent's own (agent.memory_id).

Parameters
memory_idstrrequired
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns
Memory

A memory that loads the first time you read a field. Nothing is sent yet.

Usage
support = noclick.Agent.from_name("support")
own = noclick.Memory.from_id(support.memory_id)
print(own.entry_count)

getclassmethodasync

noclick.Memory.get(memory_id, *, user=..., client=None) -> Memory

Load a memory by id now; an unknown id raises noclick.NotFound.

Parameters
memory_idstrrequired
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Memory

The memory, loaded: who attaches it and how many entries it holds.

Usage
company = noclick.Memory.get("5d1c9a3e-7b2f-4e60-8a19-3c4d6e8f0b21")
print(company.name, company.entry_count, company.attached_to)

listclassmethodasync

noclick.Memory.list(
    *, limit=None, user_filter=None, user=..., client=None
) -> list[Memory]

The current user's named memories and the account's (or those user_filter covers). An agent's own memory isn't listed: it's agent.memory.

Parameters
limitint | None

Return at most this many.

(default None)
user_filterAny

Which users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
list[Memory]

Every matching memory, newest first, fetched page by page.

Usage
for memory in noclick.Memory.list():
    print(memory.name, memory.entry_count, memory.user_id)   # user_id is None for the account's

list_pageclassmethodasync

noclick.Memory.list_page(
    *, cursor=None, limit=None, user_filter=None, user=..., client=None
) -> Page

One page of named memories; pass next_cursor back as cursor.

Parameters
cursorstr | None

The next_cursor of the page before; omit it for the first page.

(default None)
limitint | None

Return at most this many.

(default None)
user_filterAny

Which users a read covers: noclick.ALL_USERS, noclick.NO_USER, a User, or your id for one. It filters without acting as that user.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
clientClient | None

The client to send with; defaults to the module client built from NOCLICK_API_KEY.

(default None)
Returns
Page

A page whose data holds the memories and whose next_cursor is None on the last page.

Usage
page = noclick.Memory.list_page(limit=50)
for memory in page.data:
    print(memory.name)

writable

memory.writable() -> MemoryLink

This memory, for an agent's memories=, with writes: the agent may save to it and forget from it, not only read it. Only a call acting for the memory's own scope can attach it so.

Returns
MemoryLink

What memories= takes for a memory the agent writes.

Usage
team = noclick.Memory.from_name("team-notes", create_if_missing=True)
noclick.Agent.from_name("scribe", create_if_missing=True, instructions="Keep the team's notes.",
                        memories=[team.writable()])

updateasync

memory.update(*, name=None, description=None) -> Memory

Rename it, or change its description. An agent's own memory takes no name.

Parameters
namestr | Nonedefault None
descriptionstr | Nonedefault None
Returns
Memory

This memory, as it is now.

Usage
noclick.Memory.from_name("company").update(name="handbook")

deleteasync

memory.delete() -> TrashItem

Delete the memory and every entry in it: it goes to the trash, restorable for 30 days. While an agent attaches it (attached_to), this raises noclick.Conflict: take it out of the agents' memories first. An agent's own memory goes only with its agent (noclick.Conflict).

Returns
TrashItem

Its TrashItem: restore() puts it back.

Usage
noclick.Memory.from_name("old-notes").delete()

entriesasync

memory.entries(*, user=...) -> list[MemoryEntry]

The entries of one layer: the account's outside as_user, else the user's.

Parameters
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns
list[MemoryEntry]

Each entry's name, description, version and when it last changed, by name; content is None here, so read an entry for its body.

Usage
for entry in noclick.Memory.from_name("company").entries():
    print(entry.name, entry.description)

searchasync

memory.search(query, *, limit=None, user=...) -> list[MemoryEntry]

Search the memory the way an agent does: a user's entries and the account's. An entry matches when its name, description or content holds any of the query's words (refund finds refunds), or its name or description holds the query.

Parameters
querystrrequired
limitint | None

Return at most this many.

(default None)
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns
list[MemoryEntry]

The best matches first (10 unless limit says otherwise, at most 100), with their content.

Usage
for entry in noclick.Memory.from_name("company").search("refund policy"):
    print(entry.name, entry.content)

readasync

memory.read(name, *, user=...) -> MemoryEntry

One entry in full; a name with no entry raises noclick.NotFound.

Parameters
namestrrequired
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns
MemoryEntry

The entry with its content and the version to pass back when you change it.

Usage
tone = noclick.Memory.from_name("company").read("tone")
print(tone.version, tone.content)

saveasync

memory.save(name, content, *, description=None, version=None, user=...) -> MemoryEntry

Write an entry, creating it if the name is new. description is the line every agent attaching the memory sees each turn (required for a new entry, kept when you leave it out). With version, the save only lands if the entry is still at that version, else noclick.Conflict.

Parameters
namestrrequired
contentstrrequired
descriptionstr | Nonedefault None
versionint | Nonedefault None
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns
MemoryEntry

The entry as saved, with its new version.

Usage
company = noclick.Memory.from_name("company", create_if_missing=True)
company.save("tone", "Warm and brief, no jargon.", description="How we write")

forgetasync

memory.forget(name, *, version=None, user=...) -> None

Forget an entry. With version, only if it is still at that version (else noclick.Conflict).

Parameters
namestrrequired
versionint | Nonedefault None
userAny

Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).

(defaults to the current user)
Returns

None once it's gone; a name with no entry raises noclick.NotFound.

Usage
noclick.Memory.from_name("company").forget("old-pricing")