Memory

A memory is a list of lines that belongs to one subject of one tenant: facts, events your software reports, rules with conditions, decisions you choose to keep. It lives in RAM on Jepela's server and is flushed to disk in the background.

What a subject is

Anything you decide about more than once and know things about:

SubjectLines you would writeA decision that uses them
a customer accountcontract terms, who handles it, past incidentswhich team, how urgent, escalate or not
a user of your apppreferences, opt-outs, plansend this mail, show this offer
a player in a gamedeeds, reputation, what killed them last timehow a character treats them, which way to go
a device or machinenormal ranges, last service, known faultsalert, ticket, stop
a case or claimwhat is already known, who asked to be toldnext step, escalate

The id is yours: letters, digits, underscore and hyphen, up to 36 characters, scoped to your tenant.

Writing

POST /v1/memory/remember
{"subject": "acme", "text": "ACME Logistics holds an enterprise contract.\nRefunds for ACME go to the billing team within one business day."}

One fact per line; several lines per call. Write what is true or what to do. A line that forbids something names it, and naming an option pulls the engine toward it: in the Jepela Dungeon demo the line "Never walk along the corridor with the gold coins." raised the coins from 83% to 94% (measured 2026-09-23). When an option must never be chosen, use an exclusion rule, which removes it in code; a memory line cannot guarantee it (see Rules in memory). remember returns a memory_negative_rule warning for such lines, and memory_number_rule for lines that compare numbers in words; put those in a rule instead (see Rules in memory). Each subject holds up to 10,000 lines; a write that would pass that stores nothing (409). A line has at most 2,000 characters; a longer line refuses the whole write (422).

Using

Add "subject": "acme" to a decision. Jepela recalls the lines that share names and phrases with the state, places them before the state (see State), and asks the engine. In an array they are the first element, and an array longer than the engine reads is cut from its start, so for such an array (a long list of chat turns) Jepela leaves out its oldest elements until the lines and the rest fit, and reading.turns_dropped says how many: the oldest turns give way, so the memory is read. Recall takes up to memory.top_k lines (1 to 20, default 6) that reach memory.min_share of the best line's score (0.05 to 1, default 0.2). "memory": {"use": false} answers without the subject's memory and rules.

"memory": {"subject": "acme", "lines_used": 2, "lines_seen": 2, "lines_dropped": 0, "recall_ms": 0.2,
           "hits": [{"text": "ACME Logistics holds an enterprise contract.", "read": true, ...}],
           "rules_fired": [], "rules_broken": [], "without_memory": null, "changes": null}

lines_used is how many lines were sent to the engine, fired rules first, then recalled lines; lines_seen how many of them the engine read; each line in hits and rules_fired has read, true when the engine read it, as many as lines_seen counts; lines_dropped how many did not fit in its 64 context lines (computed facts, then fired rules, then recalled lines, each line at most 2,000 characters). lines_seen is lower than lines_used when the lines and a string or an object together are longer than the engine reads (the first lines are read), or when the lines and an array's newest element alone are (the last lines are read; Jepela has already left out every older element that helped); the memory_cut and memory_full warnings say so. A matched choice (more than 20 options, see Choice) is read by the matching model, which reads at most 512 tokens: before a string or an object the lines come first, so it reads the first ones; an array it reads as the engine does, so there it reads the last ones; with questions of both kinds, a line counts as read when every question read it.

When a subject holds lines about several similar situations, raise min_share so that only the lines about this one are read. In the Jepela Dungeon demo (three rooms, one lesson line each), lines about the other rooms could pull the Coin Hall answer back to the trap; with "min_share": 0.5 only the Coin Hall line was read and the hero took the stair (64%, checked by the demo's test).

Making sure the right lines are found

  • The subject's name is searched too. Recall looks for the words of the subject id (northwind-traders: northwind, traders) and its aliases along with the state, so a line about the subject is found when the state does not repeat the name. Measured on 2026-09-26: a ticket saying only "Our service is down and customers cannot log in." escalated 0.35 without this and 0.95 with the subject's Northwind lines. Set other names a subject goes by with POST /v1/memory/aliases ({"subject": "acct-1", "aliases": ["Globex Corporation"]}), and take one out with POST /v1/memory/aliases/delete ({"subject": "acct-1", "aliases": ["Globex Corporation"]}; it adds none and also works while your account is suspended). "memory": {"names": false} turns it off.
  • The question's words are searched too. "Which language should the reply be written in?" finds "Contoso's team wants every reply written in Swedish." even when the state is about exporting reports and ten other Contoso lines share its words. It was chosen on the cost cookbook's 60 constructed decisions about 20 customers with about 30 history lines each (cookbooks/cost_per_decision.py, 2026-09-26), where recall by the state alone often crowded the right line out; those same decisions were used to choose it, so they are not a measure of how often it helps on other data. "memory": {"question_words": false} turns it off.
  • Pinned lines are always sent. A rule with "pin": true goes first at every decision about the subject, whatever the state says: premium support, an opt-out, an allergy. Measured on 2026-09-26: a pinned premium-support line on subject acct-7781 moved the same ticket from 0.35 to 0.86.
  • Focus keeps the memory on topic. "memory": {"focus": true} keeps a recalled line only when it shares a word with the state beyond the subject's name. Measured on 2026-09-26: two Fabrikam lines about invoices and tickets no longer moved "send this launch mail?" (0.71 without memory, 0.66 with them, 0.71 with focus), and with the opt-out line added, focus kept only that one.

Checking what the memory did

  • "memory": {"compare": true} also asks without the memory: without_memory and changes show per question what the memory changed. Without the memory also means without its rules. An option that an exclusion rule removed keeps the probability the engine gives it in without_memory. That question's entry in changes lists the option under excluded, with the rule's text. When without is one of those options, a rule took that answer away. Your code can then say so ("I won't lock the screen: you asked me never to") instead of quietly doing the runner-up (see Rules in memory). A tenant temperature (Quality) is applied to both answers alike, so the comparison shows only what the memory did. When Jepela left out an array's oldest elements so that the lines could be read (see State), the answer without memory reads the same elements, and so do the passes of explain and placebo: each differs from the answer only by its lines. The second call is free.
  • "memory": {"explain": true} asks once more per memory line (up to 8) with that line left out, at the same moment so the engine answers them together: explain lists each line, its kind (pinned, rule or recalled), whether the engine read it (read) and how far the answer moved because of it. A tenant temperature (Quality) is applied to these passes as it is to the answer shown, so each move is on the scale of that answer. At balanced and best these passes stay with the engine, so a move is never a mix of two models. Where the second tier gave the answer shown, no pass of that model left a line out: for those questions each move is the engine's own answer's, first, as the answer shows it (the temperature is not applied to a second-tier answer or its first), and each entry's measured_on_first lists them. The extra passes are not billed.
  • "memory": {"placebo": true} also asks with neutral lines instead of yours: each line becomes "<the name it starts with> has a record in this system.", for example "North Traders has a record in this system.". Like the answer without memory, the placebo pass has none of your rules: exclusion rules remove no option there. placebo.content_effect is the change the memory's content caused (what its lines say and what its rules removed), memory_effect the change the whole memory caused, and presence_only lists questions that moved as much with neutral lines as with the real ones: there the memory worked by being present, not by what it says. A tenant temperature (Quality) is applied to the placebo's answers (placebo.answers) and to the answer without memory as it is to the answer shown, so each effect is on the scale of that answer. At balanced and best the placebo pass stays with the engine, so an effect is never a mix of two models. Where the second tier gave the answer shown, no pass of that model had neutral lines: for those questions each effect is measured from the engine's own answers, with neutral lines and without memory, to first, as the answer shows it (the temperature is not applied to a second-tier answer or its first), and placebo.measured_on_first lists them. The placebo pass is not billed.

Reading and forgetting

GET  /v1/memory?subject=acme&lines=true      every stored line and rule, word for word
POST /v1/memory/forget   {"subject": "hero", "concept": "Coin Hall plain stair"}
POST /v1/memory/forget   {"subject": "acme", "concept": "Ann Lindqvist", "preview": true}   what would go; nothing removed
POST /v1/memory/delete   {"subject": "hero"}
POST /v1/memory/aliases/delete   {"subject": "acme", "aliases": ["Annie"]}   these names out of the subject's aliases

forget removes every line that contains all the content words of the concept as whole words, and every rule whose text does (forgetting "Ann" leaves "annual" and "channel" alone); words are read in any script, so "Müller", "Åsa" and "José" are forgotten and recalled like "Ann". A word you write in lowercase matches with case ignored, as a whole word or as a part of a hyphenated word that holds a letter: forgetting "dairy" removes "The customer is dairy-free." and "Non-dairy milk only." (never a piece of a word: "ann" is not in "annual"; and a number is not taken out of a date or a phone number: forgetting "2026" leaves "Renewal on 2026-10-16."). A word you write with a capital letter is a name and matches only a word written with a capital: forgetting "Per" removes "Per prefers email." and leaves "Billing is per month." and the rule "Charge per seat."; write it in lowercase to match it in any case. A name that is also a short common word ("Can", "An") is a name too when you write it alone with a capital: forgetting "Can" leaves "We can ship on Monday." (and lists it in related). A name keeps the apostrophes inside it, however they are typed, so "O'Brien" is one word (it matches "O’Brien", "OʼBrien", "O‘Brien" and "OBrien" too, and "Brien Co is a supplier." stays); "İ" in a name is read as "I" ("İbrahim" matches "Ibrahim"). A name also matches its possessive and genitive forms ("Ann's", "Ann’s", "Lindqvists'", and the Nordic "Åsas" and "Lindqvists": forgetting "Åsa" removes "Åsas telefonnummer är 070-1234567.") and each part of a hyphenated name (forgetting "Smith" removes "Smith-Jones signed the contract."; forgetting "Smith-Jones" leaves "Smith prefers email."). A capital shows a name only where the text uses case, so where it does not, a line that has the name is removed too (privacy first) and listed in matched_unsure (lines, and rules with id and text) for you to check: the first word of a sentence, a list item ("1) Will ship Monday", "- Will ship") or a table cell, unless the concept's own name goes on there ("Will Smith signed the order." for "Will Smith"); a word written in capitals, unless the concept writes it so ("THE ORDER WILL SHIP MONDAY." for "Will", not "We moved to IBM Cloud." for "IBM"); a heading in title case ("Shipping Will Resume Monday"); and the whole of a name of several parts written in another case ("ann lindqvist's phone number is 070-7654321." or "ANN LINDQVIST" for "Ann Lindqvist"). So forgetting "Will" removes and lists "Will you call me tomorrow?", "Will Power is on my reading list." and "Will Smith signed the order." (the two cannot be told apart), and removes "Send it to Will." without listing it. A capital in the middle of a sentence is read as a name, so "Q2 report - May Update" goes with "May" unlisted. The first word of a concept that also has words in lowercase ("Enterprise contract") matches with case ignored, unless a name follows it ("Ann Lindqvist's phone number"). Accents are read in the same spirit: a word also matches the same word written with other accents or none, both ways (forgetting "Jose" removes "José prefers email.", forgetting "José" removes "Call Jose today.", and "Bjork" removes "Björk"), and because some languages hold such letters apart (in Swedish "Åsa" and "Asa" are two names), a line matched only that way is removed too (privacy first) and listed in matched_unsure; a line that writes the word as you do is removed as before. Letters that are not a base letter with a mark are compared as they are written without them: "ø" as "o", "æ" as "ae", "œ" as "oe", "ß" as "ss", "þ" as "th", "ð" and "đ" as "d", "ł" as "l" and "ı" as "i", so forgetting "Soren" removes "Please call Søren today." and lists it in matched_unsure, and forgetting "Bjørn" does the same with "Ask Bjorn first." ("Straße" and "Strasse" were always one word: case folding writes "ß" as "ss"). removed_lines and removed_rules say what went. related lists, in the same shape, the lines and rules left that may be about a name in the concept, without removing them, so you can decide (forget them on their own, by their text or with the name in lowercase, if they are about the same person): those that hold the concept with a name written in another case ("talked to ann today" for "Ann"; "Billing is per month." and "Charge per seat." for "Per"), and those that hold a part of a name of two or more parts ("Ann prefers email." for "Ann Lindqvist", "Smith prefers email." for "Smith-Jones"). A concept of several words removes only what holds all of them, so forgetting "Ann Lindqvist" leaves "Ann prefers email.". verified_forgotten is about what forget removes; what related lists does not change it. Then it checks recall: verified_forgotten is true only when recall, asked with the concept, with questions built from it and with the rest of each removed line, returns no line that still holds all the concept's content words, no line or rule still holds them, and nothing went that the concept does not name; a concept with no word in it (only symbols) is verified only when its own line went; leaks lists any line that still holds them. Send "preview": true to see first exactly what forget would remove: the answer has "preview": true and removed_lines, removed_rules, related and matched_unsure as forget would give them, and nothing is removed. A preview is not billed and writes no ledger line, so it has no usage, and no verified_forgotten either. preview is true or false (anything else is a 422); a field whose name is close to it in any case or spelling, dry_run, simulate, what_if and the like, an object that holds one ("options": {"preview": true}), and a query string (?preview=true) are refused with a 422 and nothing is removed. The routes that delete have no preview: delete, rules/delete, and the golden, decision and fine-tuned model deletes refuse a query string (?dry_run=true) and a field that asks them only to look in the same way, with nothing deleted. In the Jepela Dungeon, after "In the Coin Hall, the hero always goes down the narrow plain stair." was forgotten, the next decision took the gold coins again, as it did before it was taught. delete removes the whole subject, its lines and its rules, from RAM and disk. Both are immediate, and neither waits for money: forget, its preview, delete and aliases/delete work when your balance is zero or below and while your account is suspended (a deletion request suspends it until it is erased), as reading does (GET /v1/memory and the other GET routes), and are billed only while your credit covers their price (otherwise the ledger line says cost 0 and unbilled; see Pricing). delete also takes the subject's requests, and their results, out of your batches. What they do not remove: the tenant's ledger keeps the subject id on each priced request, golden cases keep the requests that named it until you delete them, and a fine-tuning job keeps the cases it trained on, with the lines the subject's memory recalled for them, until you delete its model (POST /v1/finetune/delete). After forget, batches run before it keep the lines their decisions recalled (each result's memory.hits) until the batch is deleted, and the subject's aliases stay as they are (POST /v1/memory/aliases/delete` takes one out).

Isolation

Inside the gateway every subject id is prefixed with your tenant, so acme at one tenant and acme at another are different memories, and no request can name another tenant's subject.

What recall is and is not

Recall matches words and short phrases, plus the subject's own names. A fact worded very differently from the state is missed, so use the names and terms your states use, or pin the facts that must always count. It returns lines to show the engine, and the answer says which ones. What the engine then does with a line is the subject of Rules in memory.