Geniffy for Python
Give your app a memory. Write what each of your users tells you, and put what is known about them in front of your model, with where every line came from. When nothing is known, it says so instead of guessing.
pip install geniffy
Make a key in the Geniffy app under API keys and set it as GENIFFY_API_KEY.
from geniffy import Geniffy
client = Geniffy() # reads GENIFFY_API_KEY
mem = client.space("customer_1042") # one of your users; nothing else can read it
source = mem.memories.add("Priya Nair signs the Lumen renewal, and it comes up in March.")
mem.sources.wait(source.id) # learning usually takes a few seconds
prompt = f"{mem.context('Who signs the Lumen renewal?')}\n\nUser: Who signs the Lumen renewal?"
context() returns the memories that bear on the question, one per line, each with where it came from:
- Priya Nair signs the Lumen renewal. [note, 2026-10-05]
- The Lumen renewal comes up in March 2027. [note, 2026-10-05]
Ask about something that was never stored and it returns one sentence, There is nothing stored about this yet. Say so rather than guessing., never an empty string: a model reads silence as permission to
invent.
Three ways to recall
mem.context("Who signs the renewal?") # a block for your own prompt; the one most apps want
mem.ask("Who signs the renewal?") # our answer in words, or answer=None and a message saying why
mem.search("renewal", limit=5) # the raw memories, ranked, to do with as you like
context() and ask() judge whether anything bears on the question. search() ranks and does not
judge: it returns its best matches for any question at all.
Spaces: one memory per user
A space is your own name for one of your users. Bind a client to it and every call stays inside it. A space exists from the first time you write to it; there is nothing to create.
mem = client.space(f"user_{user.id}") # per request
client.memories.add("...") # no space: your own memory, the one the Geniffy app shows
client.spaces() # which spaces hold anything, most recently written first
client.space("user_8841").export() # everything held for that user, as their own copy
client.forget_space("user_8841") # everything held for that user, gone, when they ask
To let a user's own app or device reach their memory, and nothing else, give it a key limited to them:
key = client.space(f"user_{user.id}").keys.create(name="Asha's phone") # key.key is shown once
client.space(f"user_{user.id}").keys.revoke(key.id)
Group your users' profiles your way with sections: client.sections.create("billing", keywords=["invoice"])
for every user, or on client.space(id) for one.
Add
mem.memories.add("A note to remember", title="Call with Priya")
mem.memories.add(url="https://example.com") # a web page, read once
mem.memories.add(messages=chat_history) # a conversation, as your framework holds it
mem.memories.add("We moved the launch to March.", said_at="2026-09-12") # said in the past: dated by when
mem.memories.add_file("Pricing.pdf") # PDF, .docx, .pptx, .xlsx or text: a path, bytes or a file opened "rb"
mem.memories.add_many([{"text": "..."}, {"url": "https://..."}])
A file or page that can't be read raises UnreadableError; error.source is the row it left, with the reason.
Syncing your own records? Give each its id. Sent again under the same external_id, the source is updated
rather than added twice: only what changed is learned, and what was removed is taken back.
mem.memories.add(ticket.body, title=ticket.subject, external_id=f"ticket-{ticket.id}")
mem.sources.get(external_id=f"ticket-{ticket.id}")
mem.sources.delete(external_id=f"ticket-{ticket.id}") # when the ticket is deleted in your app
Label what you add with your own name/value pairs, then keep any read to them. Every name must match, and a list of values is any one of them.
mem.memories.add(email.body, title=email.subject, labels={"channel": "email", "account": "lumen"})
mem.context("When does the renewal come up?", labels={"account": "lumen"})
mem.search("pricing", labels={"channel": ["email", "chat"]})
mem.memories.list(labels={"account": "lumen"})
mem.sources.delete_labelled({"channel": "email"}) # the user disconnected it: all it brought goes
mem.sources.delete_labelled({"channel": "email"}, keep=seen) # the end of a full sync: all but what is still there
To keep a whole data source in step, such as a user's Gmail, Drive or Notion, see Sync a data source.
Read and correct
page = mem.memories.list(kind="people") # all, people, plan, pref or detail; newest first
for m in mem.memories.iter(): # every memory, a page at a time
...
detail = mem.memories.get(42) # .memory.quote is the sentence it came from; .history its older values
mem.memories.correct(42, "Priya signs it, with Arjun co-signing.")
mem.memories.delete(42) # forget it for good
mem.profile("Priya Nair") # what is lastingly true about someone, and what is going on now
mem.brief("Priya Nair") # what to read before talking to them
mem.sources.list(); mem.sources.delete(source.id) # a source, and what only it taught
Async
from geniffy import AsyncGeniffy
async with AsyncGeniffy() as client:
mem = client.space("customer_1042")
await mem.memories.add("Priya wants a demo on Tuesday.")
print(await mem.context("When is Priya's demo?"))
Errors, retries and request ids
Every error carries the API's own sentence: AuthenticationError (a wrong or revoked key),
NotFoundError, BadRequestError, UnreadableError, RateLimitError, InternalServerError,
APIConnectionError. Reads are retried twice on network errors, 408, 429 and 5xx. Adding is retried
only when the request never reached Geniffy, or on 429, so a retry never saves a note twice. Set
max_retries= and timeout= on the client to change that.
Every response carries an X-Request-ID, and every error carries it as error.request_id. Paste it into
Requests in the Geniffy app to see that exact call: what was asked, what came back, and how long it took.
A key reaches its owner's memory and every space beneath it. Keep it on your server.
Docs: https://docs.geniffy.com
Metadata
Release files for geniffy 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| geniffy-0.2.0.tar.gz | 26.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| geniffy-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 45.5 kB
Release files / geniffy-0.2.0.tar.gz
| Download URL | geniffy-0.2.0.tar.gz |
|---|---|
| Size | 26.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
59f2438f4cc21aa292c89bf5671d6e071a83682db0909856ad02dc4555b916f5
|
|
BLAKE2b-256 checksum How to use checksums |
6631244b5223813b09d62b5663bfdb54c4d357bee4ac2f2eef0b7061497c27bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency logRelease files / geniffy-0.2.0-py3-none-any.whl
| Download URL | geniffy-0.2.0-py3-none-any.whl |
|---|---|
| Size | 18.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5dcb6a4cf653e81c4992715424f02dc3d61ff3273e6ab997b2439e127ce31227
|
|
BLAKE2b-256 checksum How to use checksums |
a4cad672cae1fb15fd66042e583e3cf0d4dae49568c931e5c1c767a4a8bd2e76
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 6, 2026.
Transparency log