Repository navigation
Highlights
- Purge API: clear the whole memory root, one app or one project while the server keeps serving. No more stopping the server to delete files; other apps and projects keep reading and writing during a purge.
POST /api/v2/memory/purge: start a purge.POST /api/v2/memory/purge/confirm: confirm it before anything is deleted, so nothing goes by mistake.GET /api/v2/memory/purge/{job_id}: check its progress.
- User profile built from episodes: every profile item cites the conversation it comes from, so each fact can be traced to its source.
user.mdgains ado_not_recordlist: what the user asked not to be remembered stays out of the profile, even when it comes up again.user.mdgains apendinglist: information seen only once waits here until a later conversation confirms it, which cuts down on wrong entries.
- Scenario-based LLM settings (
[llm.<task>]): give each scenario, such as boundary detection, profile extraction or the retrieval decider, its own model and parameters; anything left out follows[llm]. Use a small, cheap model for simple steps and a larger one where quality matters. [boundary_detection] strategy: choose how conversations are cut into episodes.batch(default): one call per batch of messages, about a tenth ofstep's calls and about 5x faster ingest.step: one decision per message, about 0.3 points higher on LoCoMo; for chat-only workloads where ingest speed matters less.
[tokenizer] name: keyword search can usejieba(default, suited to Chinese) ornltk(suited to English), for more accurate keyword search over English content.- Episode summaries: each episode gets its own summary, so a search result shows at a glance what it is about. This costs one more model call per episode.
- everalgo on final releases: all eight everalgo packages are final releases, so dependencies are more stable and upgrading needs no extra flag.
- LoCoMo: up to 94.61% (re-judged: the 99 questions whose gold answer the LoCoMo audit disputes are graded against its corrected answers).
Upgrade
- One command:
pip install --upgrade everos, or with uv,uv pip install --upgrade everos. - Stop the old server normally, then start the new version. No data migration, no index rebuild.
- Check before upgrading:
- Unknown keys under
[llm]now stop startup, which lists the valid keys; fix them as it says. - Move the retrieval decider's model settings from
[decider]to[llm.decider]. The old section still works for now, with a startup warning.
- Unknown keys under
Full changelog
EverOS 1.7.0 moves onto the everalgo 2026-11 line, all final releases. The
user profile is now built from episode narratives, every LLM task can have its
own model and settings under [llm.<task>], boundary detection can run batch
or step, and a memory root, app or project can be purged while the server keeps
serving. LoCoMo is published on this release at 94.61% (re-judged), with every
retrieval method in benchmarks/README.md.
Two changes can stop an existing setup: unknown keys under [llm] (or an
EVEROS_LLM__* variable naming none) now fail startup, and the Python API for
building providers changed. HTTP and CLI are unchanged apart from additions.
See Upgrade below.
Added
POST /api/v2/memory/purge,/purge/confirmand
GET /api/v2/memory/purge/{job_id}: clear the whole memory root, one
app or one project while the server keeps serving, aligned with the Cloud
API. Reads, other apps and projects and background work all proceed while
a purge runs; only writes into the purged range answer409
PURGE_IN_PROGRESSuntil it ends; a browser page of another site cannot
purge (403CROSS_SITE_REFUSED). Background work on input taken in
before the purge is dropped, and the purge waits for work in flight before
it deletes a second time. New[purge]settings bound a job's running
time. Seedocs/api.md.[boundary_detection] strategy:batch(the default) cuts the whole
buffer in one model call per add;stepasks once per message whether it
starts a new episode, as the EverAlgo LoCoMo reference run does (chat
messages only, so use it with[memorize] mode = "chat").[tokenizer] name: choose the keyword (BM25) tokenizer.jiebastays
the default;nltkreproduces the EverAlgo LoCoMo harness pipeline token for
token (English only; install withpip install "everos[nltk]"plus
python -m nltk.downloader punkt_tab stopwords). The LoCoMo benchmark config
selectsnltk, and the benchmark's ingest identity now records the tokenizer,
so a store indexed with another one is not resumed. Changingnameon an
existing store requireseveros cascade rebuild.- Per-task LLM sections,
[llm.<task>].[llm]alone still runs
everything; any of twelve tasks --boundary,episode,summary,
atomic_fact,foresight,profile,reflection,agent_case,
agent_skill,knowledge,search,decider-- can override any[llm]
field for itself (EVEROS_LLM__<TASK>__<FIELD>in the environment). Each
field is inherited from[llm]on its own;extrais replaced whole, never
merged. Tasks that resolve to the same settings share one client; the decider
always has its own.everos config showprints what each task resolves to and
where each field came from, and the server logs the same at startup
(llm.task.resolved). Seedocs/configuration.md. [llm] temperature,max_tokensandsdk_max_retries(also per task).
temperatureandmax_tokensare sent only when a call names none -- agentic
search keeps its own temperatures -- and, unset, the request carries neither,
as before.- A sender keeps the name it was last given. Ingestion records the latest
non-blanksender_nameof every(app_id, project_id, sender_id)in a new
sender_nametable ofsystem.db, and gives it to that sender's later
messages that arrive without one (blank names count as absent; names are
stripped). One person is then labelled the same way in every memcell,
episode narrative and profile. Deleting a whole user or agent forgets its
name; deleting a session does not. [profile] max_episodes_per_call(default 10): how many episodes one
profile extraction call folds; see the profile entry under Changed. The
profile's LLM is[llm.profile]; a reasoning model with thinking on, at its
default effort, and a longertimeout_seconds(240) are recommended there;
seedocs/configuration.md.- Benchmark reports give a
Re-judgedscore besideAccuracy. With a gold
audit, JUDGE grades the answers to the questions it disputes once more, against
the audit's corrected answers, with the same judge model, number of runs and
majority vote; nothing is searched or answered again, and the rows go to
conv<N>/rejudge_<method>.jsonl(resumable, so--stages judgefills them in
for a run judged earlier).report.txt,report.jsonand the terminal summary
show both scores, overall and per category; until every disputed question is
re-judged the report gives noRe-judgednumber and says how many are missing.
LoCoMo reads dial481/locomo-audit
errors.json(99 of 1,540 questions;WRONG_CITATIONentries keep their gold)
through the newgold_audit_pathsetting; a missing file leaves the run going
and the report saying it was not re-judged, and the benchmarks without an audit
say so on theRe-judgedline. bash benchmarks/download_data.sh <benchmark>fetches exactly the files each
adapter reads to the path its config expects -- including LoCoMo's gold audit,
pinned to a commit, LongMemEval's extension-less upstream file and SubtleMemory's
nested personas -- and runs the EverMemBench conversion. Running it again
finishes an interrupted download.- A preflight in
benchmarks/run.pylists every missing dataset and key before
any server starts, and refuses to start ADD when a started server's/health
lacks a provider the retrieval method needs. Requirements follow the method, as
EverOS's own search does:keywordruns on the LLM key alone, the vector-based
methods need embedding, andagentic(LoCoMo's) needs rerank too. A missing
provider is reported before ADD pays for extraction. The decider keeps its own
startup probe. - Benchmark
flush_scope:"session"(the default, unchanged in every shipped
config) flushes each dataset session apart;"conversation"sends a whole
conversation as one EverOS session flushed once at its end, as the EverAlgo
reference harness did. Refused for datasets whose sessions carry their own ids. benchmarks/configs/locomo-step.toml(DATASET=locomo-step): LoCoMo with
step boundary detection and prompt-only replies on every EverOS call, the
configuration behind LoCoMo's published number. It islocomo.tomlwith
those two server settings changed. The reply
format (EVEROS_LLM__RESPONSE_FORMAT_MODE) is now part of the ingest
identity, so a store built under one format is not reused under another.
Changed
-
everalgo moves to the 2026-11 line, all final releases:
everalgo-core 0.7.0,everalgo-boundary 0.5.0,everalgo-user-memory 0.9.0,
everalgo-agent-memory 0.6.0,everalgo-rank 0.5.0, and0.3.0of
everalgo-knowledge,everalgo-clusteringandeveralgo-parser. The pins
stay exact (==). No everalgo release candidate is pinned any more, so pip
and uv both upgrade without a pre-release flag.
everalgo-user-memoryis the 2026-11-120.9.0rc8, which checks every
profile item against the episode narratives and keeps a do-not-record list
(the next two entries).0.9.0rc6and0.9.0rc7are skipped: under the
default strict reply format, rc6's profile reply schemas were refused by
OpenAI, and rc7's profile calls now and then stalled untilmax_tokens. -
Boundary detection replies are short. Each boundary call's
reasoning
is one sentence of at most 25 words, on both strategies, so a call returns
much sooner: on qwen3-4b-instruct-2507 the median batch reply fell from 299
to 38–78 output tokens, and on gpt-4.1-mini from 108 to 31–39. The prompts
now ask whether a message starts a new episode, and the batch reply names
the start labels, which keeps where a small model cuts. On LoCoMo
(gpt-4.1-mini, three runs each) the batch score did not change measurably
(−0.15 points re-judged, 95% CI [−0.87, +0.52]), with about a quarter fewer,
longer memcells;strategy = "step"with prompt-only boundary replies
scored 94.03% re-judged. -
Profile items must be grounded in what was said, and new profiles add no
implicit_traits. Every item the model writes carries a quote that has to
be found in that batch's episode narratives, or the item is dropped. An
item claiming a habit, preference or other standing pattern is written only
when its quote shows it is regular; otherwise it waits until a later
conversation shows it again. No newimplicit_traitsitem is created --
what a person says about their own values and habits becomes an
explicit_infoitem -- while traits already stored are kept and can still
be updated or removed. A matter the user asks not to be recorded stays out
of the profile from then on, and an item carrying an identification or
account number (12 or more digits) is dropped. A do-not-record request is
kept only with a quote found in the narratives, and the user can withdraw
it, whole or in part. -
user.mdkeeps the extractor'sdo_not_recordandpendinglists in
its frontmatter: the matters the user asked not to be recorded, and the
patterns waiting for a second occurrence. They are the extractor's own
state: each profile update passes them back to everalgo and stores the
lists it returns. They are written only when non-empty, so existing files
read and write unchanged; they are not indexed, are never returned by
/getor/search, and go withuser.mdwhen the user is deleted. -
Episode summaries are written by the model. Each cell's summary comes
from a second call made at the same time as the episode call
(EpisodeSummarizer.asummarize_memcell, reading the same conversation),
instead of the episode's first 200 characters. The summary is display-only:
when its call fails, the episode keeps the first 200 characters and is
written as before. A cell now costs two extraction calls instead of one. -
[memory] timezonenow also sets the zone the model sees message times
in. Boundary detection, episode and summary extraction, atomic facts,
foresight, the profile and agentic search show every time in that zone with
its offset (2024-03-11 00:00 UTC+08:00 (Monday)), so relative dates resolve
against the user's own calendar. With the defaultUTCthe requests are
unchanged. -
Boundary detection runs on everalgo's
BoundaryDetector, whose two paths
returnCuts.hard_token_limit/hard_msg_limitkeep their defaults and
apply tobatchonly;stepuses everalgo's own step limits. A tail the
model judged complete is no longer closed early: the uncut tail waits for
the next batch or the flush. -
The decider's LLM is
[llm.decider]; the LLM keys of[decider]are
deprecated.[decider]keeps the multi-round loop tuning. Itsmodel,
api_key,base_url,timeout_seconds,extra,max_tokens,
sdk_max_retries,response_format_modeandinvalid_reply_retries(and
EVEROS_DECIDER__*) still apply, with a startup warning;[llm.decider]wins
when both set a field. Unset,timeout_seconds(60),sdk_max_retries(0)
andinvalid_reply_retries(1) keep the decider's own defaults rather than
[llm]'s. -
The decider has no output cap of its own. Its replies used to stop at 512
tokens; it now inherits[llm].max_tokenslike every task, so a decider that
thinks before it answers has room to. Set[llm.decider] max_tokensto cap it. -
The decider inherits
extraandresponse_format_modefrom[llm]field by
field. They used to follow whichever section named the model, so pointing
the decider at another model dropped[llm].extra-- re-enabling thinking on a
Qwen gateway configured to turn it off. A decider on a gateway that rejects
[llm].extranow sets its ownextra(even{}), which replaces it. A
[decider].extrathat only added keys to[llm].extranow replaces it too. -
[llm]rejects unknown keys. A mistyped field or task name under[llm]
or[llm.<task>]-- including anEVEROS_LLM__*variable -- fails startup
with the valid names, where it used to be ignored. Other sections are
unchanged. Settings validation errors no longer echo the offending input, so a
mistypedapi_keyline is not printed with its key. -
MemoryDeps.llmis nowMemoryDeps.llms(one client per task, an
LLMClients),UserMemoryPipelinetakesepisode_llmandsummary_llm,
andProviders.llm/Providers.deciderbecameProviders.llms. In
everos.component.llm,resolve_llm_config,resolve_decider_configand
DeciderClientConfiggave way toresolve_llm_task(s),llm_client_config
andbuild_llm_task_providers. -
The user profile is built from episodes. Every pipeline
EpisodeExtractednow folds its episode's narrative into its owner's
user.mdthrough everalgo'sProfileExtractor.aextract_from_episodes,
instead of re-reading raw memcells selected through the owner's clusters
(with an embedding provider) or through the episode index (without one).
The same call folds that owner's newer episodes still waiting in the
episode index, up to[profile] max_episodes_per_call, and their own runs
then end without a call, so a burst for one owner (a bulk import) drains in
a fraction of the calls instead of queueing one slow call per episode past
the OME run timeout. The profile no longer depends on clustering, and an
episode is no longer sent to the profile again on a later run (a crash can
still repeat one). The event now carries
owner_name— the label the owner's chat lines had (sender_name, else
sender_id) — and the profile finds the owner in the narrative by it; an
episode whose narrative does not name them is skipped with a
profile_episode_skippedwarning.ProfileClusterUpdatedis removed:
trigger_profile_clusteringstill clusters episodes for agentic search and
Reflection, and no longer emits an event. -
The benchmark's decider probe and run record follow
[llm.decider].
benchmarks/run.pystarts its servers withEVEROS_LLM__DECIDER__MODEL/
__BASE_URL/__API_KEYinstead of the deprecatedEVEROS_DECIDER__*, and
evermembench.tomlsetsEVEROS_LLM__DECIDER__TIMEOUT_SECONDS. Before the
first question of anllm_multiroundrun, the decider is resolved by EverOS's
ownresolve_llm_task, in a child process under the servers' environment,
and asked once through the provider built from it -- the decider's own model,
endpoint,extra, output cap, temperature, timeout and SDK retries, with a
strictjson_schemareply format -- where the harness used to rebuild the
precedence itself and send a request of its own.run_spec.jsonrecords the
decider that resolution names, with every effective field (output cap,
timeout,extraand the rest;extra's header and query values masked) and
the section each came from, and the backbone entry no longer picks up
EVEROS_LLM__<TASK>__*variables. -
The LoCoMo benchmark runs the EverAlgo reference setup, and its docs work
as written.benchmarks/configs/locomo.tomlsearches withagenticat
top_k = 10, judges every answer 3 times (majority vote), ingests inchat
mode, extracts at temperature 0.3 /max_tokens16384 with 1024-dimensional
embeddings and a 0.7 clustering threshold, and files every message under the
queried speaker (message_owner = "eval_owner", new, part of the ingest
identity). It also turns off the user-profile strategy its answers never read:
the new[ome_strategies]table is written as each started server's
ome.toml, the one place EverOS reads strategy switches from (also part of
the ingest identity). A LoCoMo turn that shares an image now reaches the
extractor with the image's caption in front of its text, worded as the
EverAlgo loader writes it (910 of 5,882 turns; the text alone was sent
before), and the ingest identity carries amessage_formatversion so a
store built from the old text is not reused.benchmarks/.env.exampleno longer points embedding at a local
server and asks for the reranker key agentic search cannot run without. -
The benchmark ingest identity covers more of what shapes a store. It now
records the messages ingested per conversation (a--smokeor--max-messages
cap),flush_scope, and the server settings that change what is stored --
EVEROS_BOUNDARY_DETECTION__STRATEGY,EVEROS_CLUSTERING__THRESHOLD,
EVEROS_CLUSTERING__TIME_WINDOW_DAYS,EVEROS_EMBEDDING__DIM,
EVEROS_EMBEDDING__DIMENSIONS,EVEROS_MEMORIZE__MODE,
EVEROS_LLM__TEMPERATUREandEVEROS_LLM__MAX_TOKENS, read like the keyword
tokenizer (the config's[retrieval_env]first, then the environment). A
results directory built under other values refuses to resume, so a 50-message
smoke store is no longer scored as a full one.run_spec.jsonrecordssmoke
andmax_messages. -
--smokemeans conversation 0, both throughrun.py(it took 0 and 1) and
throughreproduce.sh(it passed--conv all, so--smokealone trimmed and
ingested every conversation);reproduce.shnow leaves the default torun.py,
names smoke runs<dataset>_smoke, and says so when.venvis missing. -
report.txtflags what it measured: aScope:line marks a smoke or trimmed
run as not comparable to published results, andDecider model:names the
deciderrun_spec.jsonrecords (model, endpoint and the section the model came
from), or that the run's methods use none. -
LoCoMo's published results are measured on this release and re-judged.
benchmarks/README.mdgives every LoCoMo figure graded against the gold
audit's corrected answers for the 99 questions it disputes (marked *), best
of three full runs per config: 94.61% withlocomo-step(step, prompt-only
replies) and 94.35% with the defaultlocomo(batch, strict), bothagentic.
llm_multiround,hybrid,vectorandkeywordare scored on those runs'
stores; both LoCoMo configs run thellm_multirounddecider with thinking off. A "Batch or step" section, and its counterpart under
[boundary_detection]indocs/configuration.md, set out the choice: the
defaults (batch, strict) ingest about five times faster with every reply held
to its schema; step with prompt-only replies scores higher, within LoCoMo's
run-to-run spread, and handles chat messages only. The guide also shows how to
score another retrieval method on stores a run already built. These figures
replace 94.42%. -
Benchmark docs:
benchmarks/README.md(and its Chinese mirror) is a
step-by-step guide with a quick start, a check per step, which keys each
retrieval method needs, a troubleshooting table and the two scores;
benchmarks/.env.exampleopens with aREQUIREDblock. The root READMEs gain a
"Recommended models" table ([llm.decider]for the decider) and link the guide,
as dodocs/index.mdandCLAUDE.md.
Removed
EpisodeIndex.countand the repositories'count_by_owner(LanceDB
and Milvus): only the old profile throttle used it.
Fixed
-
Benchmark runs started together no longer share servers.
GET /health
now reportspid, andrun.pyuses a server only when the answer comes from
the process it started; a start that loses a port race moves to another port
block and starts clean. -
llm_multiroundruns stop at a decider fallback. A conversation with a
round where every decider attempt failed is stopped right after SEARCH, before
its answers are paid for, since such a round measures a different method. The configs'
decider comments say which method uses the decider and where to serve it, and
reproduce.shno longer names a decider that has no endpoint. -
Deleting a session also removes its episodes from their clusters' members,
and a retry after a partial deletion finishes that cleanup (#591). The
profile-source guards of #591 are superseded here: the profile is built
from episodes read back from markdown, not from cluster members. -
A benchmark ADD no longer dies waiting for extraction. OME empties a
succeeded run'sevent_payload(since #555), andrun.py's readiness check
raisedmalformed JSONon it, failing every conversation once one
extraction had finished. Emptied rows now skip the payload filter and still
count as finished. The "no decider endpoint" notice, which called an agentic
run "not comparable", is printed only for runs that usellm_multiround. -
benchmarks/run.pyrun directly (withoutreproduce.sh) loaded
benchmarks/.envonly after expanding the config, soBENCH_DECIDER_*,
BENCH_DATA_*andBENCH_EVAL_ROOTset there were ignored; a--methodsvalue
was applied after the method check, so a misspelt one was never refused. -
A stage-only rerun (no ADD) after a multi-server ADD failed with
"--everos-root is required": it looked forstore/, while a fleet builds
store_s0..store_s<N-1>. The run now prints the stores it actually uses. -
A SubtleMemory data path one level above the personas failed every conversation
withconv_index 0 out of range (have 0); it now names the expected layout and
the download command. -
python -m benchmarks.adapters.evermembenchran its conversion twice and wrote
its output file twice. -
The language an episode is detected in, which its atomic facts and a new
user's profile are then written in, is Chinese, Japanese or English. Every
other script and every Latin text read as English: an English episode that
used words such as "die", "Los Angeles" or "café" twice was read as German
or Spanish, and its atomic facts were written in that language.
Upgrade
pip install --upgrade everos # or: uv syncpip install --upgrade everos
# or, with uv:
uv pip install --upgrade everos('everos[multimodal]' or 'everos[milvus]' if you installed those extras.)
Check the version afterwards with pip show everos or the version field of
GET /health.
Stop the old server normally (not kill -9) so it finishes its queued
background runs, and check that its log has no ome_stop_drain_timeout. Then
start the new version on the same memory root. Nothing has to be migrated or
rebuilt: the first start adds the sender_name table to system.db, and
Markdown (including user.md), the index, the OME jobstore, the memorize queue
and conversations left open are used as they are. The keyword tokenizer stays
jieba; everos cascade rebuild is needed only if you later change
[tokenizer] name.
| If you | Then |
|---|---|
have a key under [llm] that EverOS does not define, or an EVEROS_LLM__* variable that names none (such as EVEROS_LLM__SEARCH_TIMEOUT_SECONDS) |
remove or correct it: startup now fails and lists the valid fields and tasks |
set the decider's LLM under [decider] (model, api_key, base_url, timeout_seconds, extra, max_tokens, sdk_max_retries, response_format_mode, invalid_reply_retries) or through EVEROS_DECIDER__* |
move those keys to [llm.decider] (EVEROS_LLM__DECIDER__*); until then they still apply, with the startup warning llm.decider.deprecated_section |
| run the decider on another model or gateway | it now takes extra and response_format_mode from [llm] unless [llm.decider] sets them; if that gateway rejects [llm].extra, set extra = {} under [llm.decider] |
| want the decider's replies capped as before | set [llm.decider] max_tokens = 512; the decider has no cap of its own any more |
turned the user profile off by disabling trigger_profile_clustering |
disable extract_user_profile in ome.toml instead; the profile no longer depends on clustering |
build Providers or MemoryDeps in Python, or import resolve_llm_config, resolve_decider_config, DeciderClientConfig or ProfileClusterUpdated |
pass llms (an LLMClients) and use resolve_llm_task, llm_client_config and build_llm_task_providers |
Profile updates now start from EpisodeExtracted. A profile run still queued
under ProfileClusterUpdated when the old server stopped is not resumed; after
a forced stop the new server logs one TypeError (did not resolve to an Event subclass) for each such run and carries on. An owner's profile catches up on
their next episode: that run also folds the episodes newer than the stored
profile, oldest first, up to [profile] max_episodes_per_call episodes in all
(default 10, the new one included). Older episodes beyond that are not folded
later, so if profile extraction was off or behind before the upgrade, raise
max_episodes_per_call before those owners' next conversations arrive.
Extraction output changes with this release, with no action needed: each
memcell costs two extraction calls (the episode and its summary); a new profile
item must quote the conversation and no new implicit_traits are added
(stored ones are kept); user.md carries do_not_record and pending only
when they are non-empty; text in a language other than Chinese, Japanese or
English is read as English, so its atomic facts and new profiles are written in
English (a stored profile keeps its language); and with a non-UTC
[memory] timezone the model sees message times in that zone.
Upgrading from 1.5.x: apply the 1.6.0 upgrade notes as well; the steps above
come after them unchanged.
Full changelog: v1.6.0...v1.7.0