Compare commits

...

10 Commits

Author SHA1 Message Date
0910affc78 Save local modifications for syncing
Some checks failed
CI / lint (push) Has been cancelled
CI / unit tests (push) Has been cancelled
CI / integration tests (push) Has been cancelled
CI / package build (push) Has been cancelled
Commit lint / pull request title (push) Has been cancelled
Commit lint / commit messages (push) Has been cancelled
2026-06-10 10:05:52 +08:00
9fc6ad20d2 fix(docs): repair dead xrefs in api.md, runbook, skill (#269)
Three internal documentation references pointed at non-existent targets:

- docs/api.md: MessageItem.content linked to #addmessage, which has no
  heading or anchor; corrected to #messageitem (the slug used by every
  other MessageItem cross-reference and matching the ### MessageItem
  heading).
- docs/cascade_runbook.md: the FD-exhaustion cross-ref used a single
  hyphen where the GitHub slug of "FD exhaustion (`os error 24` /
  EMFILE)" has a double hyphen (from the ` / ` separator); corrected to
  #fd-exhaustion-os-error-24--emfile.
- use-cases/claude-code-plugin/skills/memory-tools.md: the always-injected
  skill named two tools (search_memories, get_memory) that the MCP server
  never exposes; replaced with the real evermem_search tool and its
  params (query required, limit default 10 / max 20).

Markdown-only; no runtime behavior change.
2026-06-08 07:10:56 +08:00
306dcfe167 docs(readme): clarify hybrid retrieval wording (#265) 2026-06-07 08:54:08 +08:00
28977ba295 docs(readme): polish repository presentation (#263)
* docs(readme): refine repository watch section

Replace the Stay Tuned section with a clearer Watch EverOS call to action in both English and Chinese READMEs.

Remove the star GIF and keep star history as lighter social proof.

* docs(readme): improve highlights table spacing

Add spacing below each feature title in the EverOS highlights table.

Mirror the same table spacing in the Chinese README.
2026-06-06 21:47:35 +08:00
0bbfb3bf1e docs(readme): clarify EverOS positioning (#262)
Rename the overview section to focus on why EverOS matters in both English and Chinese READMEs.

Simplify the EverMind ecosystem copy around EverOS as the core memory architecture and mirror that positioning in the Chinese README.
2026-06-06 21:15:10 +08:00
79b3df4de2 docs(readme): polish launch highlights and banner (#261)
* docs: simplify README launch highlights

* docs(readme): use six launch highlights

* docs(readme): use optimized banner asset

* ci: lint pull request titles
2026-06-06 19:49:59 +08:00
0a99922f24 docs: add EverMind ecosystem overview (#259)
* docs: add EverMind ecosystem overview

* docs: move ecosystem overview lower

* docs: add EverOS 1.0.0 highlights

* docs: streamline README flow

* docs: refine README showcase layout

* docs: update README banner image

* docs: use uploaded README banner

* docs: expand README highlights and navigation

* docs: normalize README title capitalization

* docs: align EverOS description with banner

* docs: use high-density README banner

* docs: clarify EverOS overview

* docs: add README localization and star history

* docs: expand Chinese README localization
2026-06-06 18:49:45 +08:00
8f175d3f8f docs: document EverOS 1.0.0 issue migration (#258)
* docs: document EverOS 1.0 issue migration

* docs: standardize EverOS 1.0.0 wording
2026-06-06 14:33:00 +08:00
00f1dfaec5 chore: finalize repo audit hygiene (#257) 2026-06-06 13:59:12 +08:00
ab23e40b28 ci: block repository media assets (#256)
* ci: block repository media assets

* test: stabilize cascade scanner loop test
2026-06-06 11:44:45 +08:00
59 changed files with 3044 additions and 311 deletions

View File

@ -1,6 +1,6 @@
# Branch Protection Baseline
Use this as the admin checklist for `main` after the EverOS 1.0 history reset.
Use this as the admin checklist for `main` after the EverOS 1.0.0 history reset.
## Required Repository Rule

View File

@ -11,7 +11,7 @@ body:
attributes:
label: Area
options:
- methods/EverCore
- src/everos
- methods/HyperMem
- benchmarks/EverMemBench
- benchmarks/EvoAgentBench

View File

@ -7,7 +7,7 @@ body:
id: page
attributes:
label: Page or file
placeholder: README.md, methods/EverCore/docs/...
placeholder: README.md, docs/architecture.md, use-cases/...
validations:
required: true
- type: textarea

View File

@ -13,6 +13,18 @@ concurrency:
cancel-in-progress: true
jobs:
title:
name: pull request title
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Validate Conventional Commit PR title
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: make check-pr-title
messages:
name: commit messages
runs-on: ubuntu-latest

35
.gitignore vendored
View File

@ -165,5 +165,40 @@ outputs/
logs/
nohup.out
# Repository media policy: do not commit images, videos, or asset/media folders.
# Use external hosting, release artifacts, or approved storage and link from docs.
asset/
assets/
image/
images/
img/
media/
video/
videos/
*.avif
*.bmp
*.gif
*.heic
*.heif
*.icns
*.ico
*.jpeg
*.jpg
*.png
*.svg
*.tif
*.tiff
*.webp
*.avi
*.flv
*.m4v
*.mkv
*.mov
*.mp4
*.mpeg
*.mpg
*.webm
*.wmv
# Use-cases: exclude lock files to keep the repo lean
use-cases/**/package-lock.json

View File

@ -28,6 +28,19 @@ repos:
- id: detect-private-key
- id: check-merge-conflict
- repo: local
hooks:
- id: no-repo-assets
name: block committed images, videos, and asset directories
entry: python3 scripts/check_repo_assets.py
language: system
pass_filenames: false
- id: no-deprecated-product-names
name: block deprecated product names
entry: python3 scripts/check_deprecated_names.py
language: system
pass_filenames: false
- repo: https://github.com/jorisroovers/gitlint
rev: v0.19.1
hooks:

View File

@ -42,9 +42,11 @@ Use the [feature request template](https://github.com/EverMind-AI/EverOS/issues/
1. Create a branch from `main`.
2. Keep the change scoped to one purpose.
3. Run `make ci` locally before requesting review.
4. Use a Conventional Commit title, such as `fix(search): guard empty profile`.
5. Open a pull request to `main` and fill out the PR template.
3. Do not commit images, videos, or asset/media directories. Use external
hosting, release artifacts, or approved storage and link from docs.
4. Run `make ci` locally before requesting review.
5. Use a Conventional Commit title, such as `fix(search): guard empty profile`.
6. Open a pull request to `main` and fill out the PR template.
By submitting a pull request, you agree that your contribution is licensed under
the project's [Apache-2.0](LICENSE) license.
@ -90,7 +92,7 @@ git clone https://github.com/EverMind-AI/EverOS.git
cd EverOS
make install # deps + pre-commit hooks (one-stop dev setup)
everos init # write ./.env, then fill in the API key slots
make ci # verify: lint + unit + integration
make ci # verify: lint + unit + integration + package
```
### Code style
@ -110,7 +112,7 @@ Highlights:
```bash
make format # ruff fix + format
make lint # ruff check + import-linter
make lint # ruff check + import-linter + hard repo hygiene gates
```
### Branch strategy

View File

@ -1,12 +1,15 @@
.PHONY: help install install-deps lint docs-check check-commits check-cjk check-datetime openapi check-openapi format test integration package cov ci clean
.PHONY: help install install-deps lint docs-check check-commits check-pr-title check-assets check-deprecated-names check-cjk check-datetime openapi check-openapi format test integration package cov ci clean
help:
@echo "Targets:"
@echo " install Install deps + pre-commit hooks (full dev setup)"
@echo " install-deps Install deps only (uv sync --frozen, used by CI)"
@echo " lint ruff (check + format-check) + import-linter + datetime discipline + openapi drift"
@echo " lint ruff + import-linter + repo hygiene + datetime discipline + openapi drift"
@echo " docs-check Validate Markdown links, use-case banners, and issue template YAML"
@echo " check-commits Validate Conventional Commit subjects for a git range"
@echo " check-pr-title Validate PR title uses Conventional Commit format"
@echo " check-assets Block committed images, videos, and asset/media directories"
@echo " check-deprecated-names Block deprecated product names"
@echo " check-cjk Scan for CJK outside the language-policy allowlist (advisory)"
@echo " check-datetime Scan for code that bypasses component/utils/datetime (HARD gate, run via lint)"
@echo " openapi Regenerate docs/openapi.json from the FastAPI app"
@ -34,6 +37,8 @@ lint:
uv run ruff check src tests
uv run ruff format --check src tests
uv run lint-imports
uv run python scripts/check_repo_assets.py
uv run python scripts/check_deprecated_names.py
uv run python scripts/check_datetime_discipline.py
uv run python scripts/dump_openapi.py --check
@ -44,6 +49,18 @@ docs-check:
check-commits:
python3 scripts/check_commit_messages.py $(RANGE)
check-pr-title:
python3 scripts/check_pr_title.py
# Repository media hygiene gate. Images/videos belong in external hosting,
# release artifacts, or other approved storage, then linked from docs.
check-assets:
uv run python scripts/check_repo_assets.py
# Product naming gate. Public repo text should use EverOS or EverMind Cloud.
check-deprecated-names:
uv run python scripts/check_deprecated_names.py
# Advisory CJK scan (see .claude/rules/language-policy.md). Deliberately NOT
# wired into `lint` / `ci`: the policy is enforced by review and the rules
# doc, not a hard gate. Run on demand when touching potentially-CJK files.

364
README.md
View File

@ -1,6 +1,6 @@
<div align="center" id="readme-top">
![banner-gif](https://github.com/user-attachments/assets/0bf97efd-580f-4a53-a2a2-58d6daea7290)
![EverOS banner](https://github.com/EverMind-AI/EverOS/releases/download/v1.0.0/everos-readme-banner-optimized.jpg)
<p align="center">
<a href="https://x.com/evermind"><img src="https://img.shields.io/badge/EverMind-000000?labelColor=gray&style=for-the-badge&logo=x&logoColor=white" alt="X"></a>
@ -9,27 +9,29 @@
<a href="https://github.com/EverMind-AI/EverOS/discussions/67"><img src="https://img.shields.io/badge/WeCom-EverMind_社区-07C160?labelColor=gray&style=for-the-badge&logo=wechat&logoColor=white" alt="WeChat"></a>
</p>
[Website](https://evermind.ai) · [Documentation](https://docs.evermind.ai) · [Blog](https://evermind.ai/blogs)
[Website](https://evermind.ai) · [Documentation](https://docs.evermind.ai) · [Blog](https://evermind.ai/blogs) · [中文](README.zh-CN.md)
</div>
<br>
<details open>
<details>
<summary><kbd>Table of Contents</kbd></summary>
<br>
- [What is EverOS](#what-is-everos)
- [Architecture at a glance](#architecture-at-a-glance)
- [Quick start](#quick-start)
- [Storage layout](#storage-layout)
- [EverOS 1.0.0 Highlights](#everos-100-highlights)
- [Why EverOS](#why-everos)
- [Quick Start](#quick-start)
- [Architecture At A Glance](#architecture-at-a-glance)
- [Storage Layout](#storage-layout)
- [Features](#features)
- [Project structure](#project-structure)
- [Project Structure](#project-structure)
- [Documentation](#documentation)
- [Use Cases](#use-cases)
- [Stay Tuned](#stay-tuned)
- [Watch EverOS](#watch-everos)
- [EverMind Ecosystems](#evermind-ecosystems)
- [Contributing](#contributing)
<br>
@ -37,48 +39,120 @@
</details>
## What is EverOS
## EverOS 1.0.0 Highlights
EverOS is an open-source Python framework that turns conversations, agent trajectories, and files into **structured, retrievable, evolving long-term memory** for AI agents and user chats. Designed for **lightweight local deployments** (small teams, individual developers), with three core principles:
> [!IMPORTANT]
>
> **EverOS 1.0.0 is a major release for self-evolving memory.** It brings a
> local-first runtime, Markdown as the source of truth, hybrid retrieval,
> multimodal ingestion, user and agent memory scopes, and modular algorithms
> through [EverAlgo](https://github.com/EverMind-AI/EverAlgo).
>
> **Watch this repository** for the next wave of memory-system work, including
> Wiki-style knowledge layers and Dreaming for deeper offline evolution.
1. **Markdown as Source of Truth** — All memory persists as plain `.md` files. Open, edit, grep, version with Git, view in Obsidian. No black-box database lock-in.
2. **Lightweight three-piece storage**`Markdown` files (truth) + `SQLite` (state/queue) + `LanceDB` (vector + BM25 + scalar). No MongoDB / Elasticsearch / Milvus / Redis / Kafka required.
3. **[EverAlgo](https://github.com/EverMind-AI/EverAlgo) as pure algorithm library** — Memory extraction algorithms are decoupled into a separate library; this project orchestrates and persists.
<table>
<tr>
<td width="33%" valign="top">
<strong>Markdown As Source Of Truth</strong><br>
<br>
All memory is persisted as <code>.md</code> files: readable, editable,
grep-able, Git-versioned, and openable directly in Obsidian.
</td>
<td width="33%" valign="top">
<strong>Local Three-Part Stack</strong><br>
<br>
Markdown + SQLite + LanceDB keep vectors, BM25, and scalar filters
local. No MongoDB, Elasticsearch, or Redis required.
</td>
<td width="33%" valign="top">
<strong>Dual-Track Memory</strong><br>
<br>
Agent memory (<code>cases</code> / <code>skills</code>) and user memory
(<code>episodes</code> / <code>profile</code>) are extracted independently.
</td>
</tr>
<tr>
<td width="33%" valign="top">
<strong>Multimodal Ingestion</strong><br>
<br>
Text, images, audio, documents, PDFs, HTML, and email are unified into
searchable memory.
</td>
<td width="33%" valign="top">
<strong>Self-Evolution</strong><br>
<br>
Common skills are extracted from real usage; repeated patterns become
reusable workflows, no retraining required.
</td>
<td width="33%" valign="top">
<strong>Orthogonal Retrieval</strong><br>
<br>
Search independently by <code>user_id</code>, <code>agent_id</code>,
<code>app_id</code>, <code>project_id</code>, and <code>session_id</code>.
</td>
</tr>
</table>
<br>
<div align="right">
## Architecture at a glance
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
```
┌───────────────────────────────────────────────┐
│ entrypoints/ (CLI + HTTP API) │ presentation
├───────────────────────────────────────────────┤
│ service/ (use cases: memorize/retrieve) │ application
├───────────────────────────────────────────────┤
│ memory/ (extract + search + cascade) │ domain
├───────────────────────────────────────────────┤
│ infra/ (markdown / sqlite / lancedb) │ infrastructure
└───────────────────────────────────────────────┘
↑ ↑
component/ core/
(LLM/Embedding) (observability/lifespan)
```
</div>
DDD 5 layers, single-direction dependency. See [docs/architecture.md](docs/architecture.md).
## Why EverOS
EverOS is an open-source Python framework for self-evolving long-term
memory across agents and platforms. It gives makers one portable memory
layer for every agent they use - Claude Code, Codex, OpenClaw, Hermes,
and more - so context, decisions, files, and trajectories can follow the
work instead of staying trapped in one tool.
EverOS stores conversations, agent trajectories, and files as readable
Markdown, then syncs local SQLite and LanceDB indexes for fast retrieval.
Agents can reuse past cases and skills, improve from repeated workflows,
and become more proactive over time.
The system is built around three boundaries:
1. **Memory content stays readable** - Markdown is the durable source of truth.
2. **Runtime state stays local** - SQLite tracks state and LanceDB handles vector, BM25, and scalar-filter search.
3. **Algorithms stay modular** - [EverAlgo](https://github.com/EverMind-AI/EverAlgo) owns memory algorithms; EverOS owns runtime, persistence, online flows, and offline evolution.
<br>
<div align="right">
## Quick start
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
### Install as a package
</div>
## Quick Start
### 1. Install EverOS
```bash
uv pip install everos # or: pip install everos
uv pip install everos
# or: pip install everos
```
# Generate a starter .env (OpenRouter + DeepInfra defaults; bundled inside the wheel)
everos init # writes ./.env (use --xdg for ~/.config/everos/.env)
# Edit .env and fill the API key fields (see comments inside).
### 2. Initialize Configuration
Generate a starter `.env` file, then fill the API key fields shown in
the generated comments.
```bash
everos init
```
`everos init` writes `./.env` by default. Use `everos init --xdg` to
write `${XDG_CONFIG_HOME:-~/.config}/everos/.env` instead.
### 3. Start The Server
```bash
everos --help
everos server start
```
@ -86,10 +160,13 @@ everos server start
`everos server start` searches for `.env` in this order: `--env-file <path>`
`./.env` (cwd) → `${XDG_CONFIG_HOME:-~/.config}/everos/.env``~/.everos/.env`.
The endpoint stack is OpenAI-protocol compatible (OpenAI / OpenRouter / vLLM /
Ollama / DeepInfra) override `*__BASE_URL` in the generated `.env` to point
Ollama / DeepInfra) - override `*__BASE_URL` in the generated `.env` to point
at any of them.
#### Multi-modal (optional)
For a step-by-step walkthrough (add a conversation, flush, search, then
read the markdown), see [QUICKSTART.md](QUICKSTART.md).
### Optional: Ingest Multimodal Files
To ingest non-text content (image / pdf / audio / office documents)
through `/api/v1/memory/add` `content` items, install the optional
@ -118,26 +195,53 @@ brew install --cask libreoffice # macOS
sudo apt-get install -y libreoffice # Debian / Ubuntu
```
For a step-by-step walkthrough (add a conversation → flush → search →
read the markdown), see [QUICKSTART.md](QUICKSTART.md).
### Develop locally
### For Contributors
```bash
git clone <repo>
cd everos
git clone https://github.com/EverMind-AI/EverOS.git
cd EverOS
uv sync # creates ./.venv and installs deps
source .venv/bin/activate # or skip activation and prefix every command with `uv run`
everos init # fill in EVEROS_LLM__API_KEY in the generated .env
source .venv/bin/activate # or prefix commands with `uv run`
everos init # fill the four API key slots in .env (two distinct keys)
everos --help
make test
```
<br>
<div align="right">
## Storage layout
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## Architecture At A Glance
```
┌───────────────────────────────────────────────┐
│ entrypoints/ (CLI + HTTP API) │ presentation
├───────────────────────────────────────────────┤
│ service/ (use cases: memorize/retrieve) │ application
├───────────────────────────────────────────────┤
│ memory/ (extract + search + cascade) │ domain
├───────────────────────────────────────────────┤
│ infra/ (markdown / sqlite / lancedb) │ infrastructure
└───────────────────────────────────────────────┘
↑ ↑
component/ core/
(LLM/Embedding) (observability/lifespan)
```
DDD 5 layers, single-direction dependency. See [docs/architecture.md](docs/architecture.md).
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## Storage Layout
```
~/.everos/
@ -165,10 +269,15 @@ is the user-facing memory surface, while extracted derivatives sit
quietly alongside.
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## Features
- **Hybrid retrieval**: BM25 + vector (HNSW/IVF-PQ) + scalar filter, single-query in LanceDB
- **Hybrid retrieval**: BM25 + cosine vector ANN + scalar filters, backed by LanceDB
- **Cascade index sync**: edit a `.md` → file watcher → entry-level diff → LanceDB sync, sub-second
- **Multi-source extraction**: conversations / agent trajectories / file knowledge
- **Dual-track memory**: user-track (Episodes / Profiles) + agent-track (Cases / Skills)
@ -176,8 +285,13 @@ quietly alongside.
- **Multi-modal**: text + small image / audio inline; large media via S3/OSS reference
<br>
<div align="right">
## Project structure
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## Project Structure
```
everos/ # repo root
@ -194,21 +308,36 @@ everos/ # repo root
└── .claude/ # team-shared rules + skills (auto-loaded by Claude Code)
```
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## Documentation
- [docs/overview.md](docs/overview.md) — Project overview & vision
- [docs/architecture.md](docs/architecture.md) — DDD layered architecture & dependency rules
- [docs/engineering.md](docs/engineering.md) — Engineering & dev-efficiency infrastructure (CI / tooling / Claude Code)
- [docs/use-cases.md](docs/use-cases.md) — Full use-case gallery and integration examples
- [docs/migration-to-1.0.0.md](docs/migration-to-1.0.0.md) — Legacy API and infrastructure migration notes
- [CHANGELOG.md](CHANGELOG.md) — Release notes
- [CONTRIBUTING.md](CONTRIBUTING.md) — How to contribute
- [.claude/rules/](.claude/rules/) — Detailed coding conventions (auto-loaded by Claude Code)
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## Use Cases
Use cases show what persistent memory makes possible in real products and workflows. Some examples are packaged in this repository; others point to external demos or integrations you can study and adapt.
Use cases show what persistent memory makes possible in real products and
workflows. Some examples are packaged in this repository; others point to
external demos or integrations you can study and adapt.
<table>
<tr>
@ -216,7 +345,7 @@ Use cases show what persistent memory makes possible in real products and workfl
[![banner-gif](https://github.com/user-attachments/assets/840470d7-a838-4c05-8685-dd797d4e9cdf)](https://evermind.ai/usecase_reunite)
#### Reunite - Find with EverOS
#### Reunite - Find With EverOS
Parents describe what they remember. Children describe what they recall. Reunite uses semantic memory to surface the connections.
@ -229,7 +358,7 @@ Parents describe what they remember. Children describe what they recall. Reunite
#### Hive Orchestrator
Browser-native hive-mind for CLI coding agents Claude Code, Codex, Gemini, and OpenCode collaborate as real PTY processes via a team protocol.
Browser-native hive-mind for CLI coding agents - Claude Code, Codex, Gemini, and OpenCode collaborate as real PTY processes via a team protocol.
[Code](https://github.com/tt-a1i/hive)
@ -241,7 +370,7 @@ Browser-native hive-mind for CLI coding agents — Claude Code, Codex, Gemini, a
[![banner-gif](https://github.com/user-attachments/assets/867d9329-ce9a-496f-ab1e-15c77974e5fa)](https://github.com/tt-a1i/evermemos-mcp)
#### AI Coding Assistants with EverOS
#### AI Coding Assistants With EverOS
Universal long-term memory layer for AI coding assistants, powered by EverOS.
@ -252,9 +381,9 @@ Universal long-term memory layer for AI coding assistants, powered by EverOS.
[![banner-gif](https://github.com/user-attachments/assets/a4f0fd86-1c81-4445-bebc-e51eb5e33b30)](https://github.com/yuansui123/AI-Data-Technician-EverMemOS)
#### AI Data Techician
#### AI Data Technician
An agentic AI system that learns from scientist interaction to inspect, analyze, and classify high-dimensional time series data with persistent memory that improves across sessions.
An agentic AI system that learns from scientist interaction to inspect, analyze, and classify high-dimensional time series data - with persistent memory that improves across sessions.
[Code](https://github.com/yuansui123/AI-Data-Technician-EverMemOS)
@ -266,7 +395,7 @@ An agentic AI system that learns from scientist interaction to inspect, analyze,
![banner-gif](https://github.com/user-attachments/assets/650b901b-c9ba-4001-bac7-626b009df830)
#### Rokid AI Assistant with EverOS
#### Rokid AI Assistant With EverOS
Connect to EverOS within Rokid Glasses enabling long-term memory for all of your smart activities.
@ -277,15 +406,21 @@ Coming soon
![banner-gif](https://github.com/user-attachments/assets/85b338b2-e48e-4a65-9f30-0bc6998df872)
#### Creative Assistant with Memory
#### Creative Assistant With Memory
Creative assistant with long-term memory, never forget your crativites anymore.
Creative assistant with long-term memory, so your creative context stays available across sessions.
Coming soon
</td>
</tr>
<tr>
<td colspan="2" align="right">
<a href="#readme-top"><img src="https://img.shields.io/badge/-Back_to_top-gray?style=flat-square" alt="Back to top"></a>
</td>
</tr>
<tr>
<td width="50%" valign="top">
@ -300,7 +435,7 @@ Earth Online is a memory-aware productivity game that turns everyday planning in
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/57d8cda7-35a5-4561-b794-5520dffc917b)](https://github.com/golutra/golutra)
[![banner-gif](https://github.com/user-attachments/assets/57d8cda7-35a5-4561-b794-5520dffc917b)](https://github.com/golutra/golutra)
#### Multi-Agent Orchestration Platform
@ -324,11 +459,11 @@ Record, visualize, and explore your tasting journey through an immersive 3D star
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/93ac2a68-4f18-4fcb-8d87-80aeb00a9d7c)](https://github.com/kellyvv/OpenHer)
[![banner-gif](https://github.com/user-attachments/assets/93ac2a68-4f18-4fcb-8d87-80aeb00a9d7c)](https://github.com/kellyvv/OpenHer)
#### EverOS Open Her
Build AI that feels. Open-source persona engine personality emerges from neural drives, not prompts. Inspired by Her.
Build AI that feels. Open-source persona engine - personality emerges from neural drives, not prompts. Inspired by Her.
[Code](https://github.com/kellyvv/OpenHer)
@ -340,7 +475,7 @@ Build AI that feels. Open-source persona engine — personality emerges from neu
[![banner-gif](https://github.com/user-attachments/assets/550071c1-dc39-4964-9f67-ffdfad792345)](https://chromewebstore.google.com/detail/ruminer-browser-agent/lbccjohfpdpimbhpckljimgolndfmfif)
#### Browser Agent for Personal Memory
#### Browser Agent For Personal Memory
Ruminer brings persistent memory to a browser agent so it can carry personal context across web tasks.
@ -349,9 +484,9 @@ Ruminer brings persistent memory to a browser agent so it can carry personal con
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/c258a6c4-fe70-497a-98d1-3dade4a932f6)](https://github.com/nanxingw/EverMem)
[![banner-gif](https://github.com/user-attachments/assets/c258a6c4-fe70-497a-98d1-3dade4a932f6)](https://github.com/nanxingw/EverMem)
#### EverMem Sync with EverOS
#### EverMem Sync With EverOS
One command to connect any AI coding CLI to EverMemOS long-term memory.
@ -360,6 +495,12 @@ One command to connect any AI coding CLI to EverMemOS long-term memory.
</td>
</tr>
<tr>
<td colspan="2" align="right">
<a href="#readme-top"><img src="https://img.shields.io/badge/-Back_to_top-gray?style=flat-square" alt="Back to top"></a>
</td>
</tr>
<tr>
<td width="50%" valign="top">
@ -374,9 +515,9 @@ MCO equips your primary agent with an agent team that can work together to solve
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/314c9126-8e08-4688-bbbb-8555ad58cf67)](https://github.com/onenewborn/StudyBuddy-public)
[![banner-gif](https://github.com/user-attachments/assets/314c9126-8e08-4688-bbbb-8555ad58cf67)](https://github.com/onenewborn/StudyBuddy-public)
#### Study Buddy with Self-Evolving Memory
#### Study Buddy With Self-Evolving Memory
Study proactively with an agent that has self-evolving memory.
@ -390,7 +531,7 @@ Study proactively with an agent that has self-evolving memory.
[![banner-gif](https://github.com/user-attachments/assets/21da76aa-9a8b-48e0-9134-42429d7390e7)](https://github.com/TonyLiangDesign/MemoCare)
#### Alzheimers Memory Assistant
#### Alzheimer's Memory Assistant
Empowering individuals with advanced memory support and daily assistance.
@ -399,7 +540,7 @@ Empowering individuals with advanced memory support and daily assistance.
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/e2428df3-ea11-4e88-8f9c-dad437dd8998)](https://github.com/AlexL1024/NeuralConnect)
[![banner-gif](https://github.com/user-attachments/assets/e2428df3-ea11-4e88-8f9c-dad437dd8998)](https://github.com/AlexL1024/NeuralConnect)
#### Memory-Driven Multi-Agent NPC Experience
@ -426,31 +567,37 @@ An iOS app where users create, nurture, and live with a personalized AI companio
[![banner-gif](https://github.com/user-attachments/assets/9aabcaa9-f97a-49d2-9109-0b5bb696ed41)](https://github.com/JaMesLiMers/EvermemCompetition-Spiro)
#### AI Wearable with Memory
#### AI Wearable With Memory
A context-native AI wearable that listens to everyday life and converts conversations into memory.
[Code](https://github.com/JaMesLiMers/EvermemCompetition-Spiro)
</td>
</tr>
<tr>
<td colspan="2" align="right">
<a href="#readme-top"><img src="https://img.shields.io/badge/-Back_to_top-gray?style=flat-square" alt="Back to top"></a>
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/df9677ec-386f-4c56-a428-08bca25c54dc)](https://github.com/EverMind-AI/EverOS/tree/0f49826ba0f9a94e1974c97614a46a68e0a08b52/evermemos-openclaw-plugin)
[![banner-gif](https://github.com/user-attachments/assets/df9677ec-386f-4c56-a428-08bca25c54dc)](docs/migration-to-1.0.0.md)
#### OpenClaw Agent Memory
#### Legacy OpenClaw Agent Memory
A 24/7 agent workflow with continuous learning memory across sessions.
Archived pre-1.0.0 plugin reference. New integrations should use the EverOS 1.0.0 API.
[Plugin](https://github.com/EverMind-AI/EverOS/tree/0f49826ba0f9a94e1974c97614a46a68e0a08b52/evermemos-openclaw-plugin)
[Learn more](docs/migration-to-1.0.0.md)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/3a2357a1-c0c3-464a-8979-0d1cdfc9b0d4)](https://github.com/TEN-framework/ten-framework/tree/04cb80601374fa9e35b4e544b2dbd23286ca7763/ai_agents/agents/examples/voice-assistant-with-EverMemOS)
#### Live2D Character with Memory
#### Live2D Character With Memory
Add long-term memory to a real-time Live2D character, powered by [TEN Framework](https://github.com/TEN-framework/ten-framework).
@ -463,7 +610,7 @@ Add long-term memory to a real-time Live2D character, powered by [TEN Framework]
[![banner-gif](https://github.com/user-attachments/assets/c36bdc04-97d3-4fe9-97d9-4b93b475595a)](https://screenshot-analysis-vercel.vercel.app/)
#### Computer-Use with Memory
#### Computer-Use With Memory
Run screenshot-based analysis with computer-use and store the results in memory.
@ -474,7 +621,7 @@ Run screenshot-based analysis with computer-use and store the results in memory.
[![banner-gif](https://github.com/user-attachments/assets/54a7cf8f-62c4-4fbc-9d50-b214d034e051)](use-cases/game-of-throne-demo)
#### Game of Thrones Memories
#### Game Of Thrones Memories
A demonstration of AI memory infrastructure through an interactive Q&A experience with *A Game of Thrones*.
@ -515,11 +662,19 @@ Explore stored entities and relationships in a graph interface. Frontend demo; b
</div>
## Stay Tuned
## Watch EverOS
Star the repo or join the community links above to follow new architecture methods, benchmark releases, and memory-enabled use cases.
EverOS 1.0.0 is the first release of a larger memory-system roadmap.
Watch this repository for upcoming work on Wiki-style memory, Dreaming,
deeper offline evolution, benchmark releases, and more real-world agent
integrations.
![star us gif](https://github.com/user-attachments/assets/0c512570-945a-483a-9f47-8e067bd34484)
If EverOS is useful to your agent stack, starring the repo helps more
builders discover it.
### Star History
[![Star History Chart](https://api.star-history.com/svg?repos=EverMind-AI/EverOS&type=Date)](https://www.star-history.com/#EverMind-AI/EverOS&Date)
<br>
<div align="right">
@ -528,6 +683,55 @@ Star the repo or join the community links above to follow new architecture metho
</div>
## EverMind Ecosystems
EverMind is an open-source ecosystem for long-term memory, self-evolving agents, and memory evaluation.
<table>
<tr>
<th colspan="2">EverMind Open-Source Ecosystem</th>
</tr>
<tr>
<td><strong>Core Memory Architecture</strong></td>
<td><a href="https://github.com/EverMind-AI/EverOS">EverOS</a> - the local memory operating system and research-backed runtime for agent and user memory.</td>
</tr>
<tr>
<td><strong>Algorithm Engine</strong></td>
<td><a href="https://github.com/EverMind-AI/EverAlgo">EverAlgo</a> - stateless extraction, ranking, parsing, and memory operators that power EverOS.</td>
</tr>
<tr>
<td><strong>Alternative Architecture</strong></td>
<td><a href="https://github.com/EverMind-AI/HyperMem">HyperMem</a> - hypergraph memory for long-term conversations, with its own benchmark-backed topic -> episode -> fact retrieval method.</td>
</tr>
<tr>
<td><strong>Benchmarks</strong></td>
<td><a href="https://github.com/EverMind-AI/EverMemBench">EverMemBench</a> · <a href="https://github.com/EverMind-AI/EvoAgentBench">EvoAgentBench</a> - evaluation suites for conversational memory and agent self-evolution.</td>
</tr>
<tr>
<td><strong>Long-Context Research</strong></td>
<td><a href="https://github.com/EverMind-AI/MSA">MSA</a> - Memory Sparse Attention for scalable latent memory and 100M-token contexts.</td>
</tr>
<tr>
<td><strong>Personal Memory Layer</strong></td>
<td><a href="https://github.com/EverMind-AI/EverMe">EverMe</a> - CLI and agent plugin suite for cross-device, cross-agent personal memory.</td>
</tr>
<tr>
<td><strong>Developer Integrations</strong></td>
<td><a href="https://github.com/EverMind-AI/evermem-claude-code">evermem-claude-code</a> · <a href="https://github.com/EverMind-AI/everos-plugins">everos-plugins</a> - plugins, skills, and migration tooling for AI coding agents.</td>
</tr>
</table>
Together, these repositories form EverMind's research-to-runtime stack: new memory methods, reusable algorithms, benchmark evidence, and practical agent integrations.
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
<br>
## Contributing
Contributions are welcome across the whole repository: architecture methods, benchmark coverage, use-case examples, documentation, and bug fixes. Browse [Issues](https://github.com/EverMind-AI/EverOS/issues) to find a good entry point, then open a PR when you are ready.

764
README.zh-CN.md Normal file
View File

@ -0,0 +1,764 @@
<div align="center" id="readme-top">
![EverOS banner](https://github.com/EverMind-AI/EverOS/releases/download/v1.0.0/everos-readme-banner-optimized.jpg)
<p align="center">
<a href="https://x.com/evermind"><img src="https://img.shields.io/badge/EverMind-000000?labelColor=gray&style=for-the-badge&logo=x&logoColor=white" alt="X"></a>
<a href="https://huggingface.co/EverMind-AI"><img src="https://img.shields.io/badge/🤗_HuggingFace-EverMind-F5C842?labelColor=gray&style=for-the-badge" alt="HuggingFace"></a>
<a href="https://discord.gg/gYep5nQRZJ"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fdiscord.com%2Fapi%2Fv10%2Finvites%2FgYep5nQRZJ%3Fwith_counts%3Dtrue&query=%24.approximate_presence_count&suffix=%20online&label=Discord&color=404EED&labelColor=gray&style=for-the-badge&logo=discord&logoColor=white" alt="Discord"></a>
<a href="https://github.com/EverMind-AI/EverOS/discussions/67"><img src="https://img.shields.io/badge/WeCom-EverMind_社区-07C160?labelColor=gray&style=for-the-badge&logo=wechat&logoColor=white" alt="WeChat"></a>
</p>
[官网](https://evermind.ai) · [文档](https://docs.evermind.ai) · [博客](https://evermind.ai/blogs) · [English](README.md)
</div>
<br>
<details>
<summary><kbd>目录</kbd></summary>
<br>
- [EverOS 1.0.0 亮点](#everos-100-亮点)
- [为什么选择 EverOS](#为什么选择-everos)
- [快速开始](#快速开始)
- [架构概览](#架构概览)
- [存储布局](#存储布局)
- [功能](#功能)
- [项目结构](#项目结构)
- [文档](#文档)
- [使用场景](#使用场景)
- [关注 EverOS](#关注-everos)
- [EverMind 生态](#evermind-生态)
- [参与贡献](#参与贡献)
<br>
</details>
## EverOS 1.0.0 亮点
> [!IMPORTANT]
>
> **EverOS 1.0.0 是面向自进化记忆的一次重要发布。** 它带来了
> local-first 运行时、Markdown 作为 source of truth、混合检索、
> 多模态摄取、用户记忆与 Agent 记忆作用域,以及由
> [EverAlgo](https://github.com/EverMind-AI/EverAlgo) 支撑的模块化算法。
>
> **欢迎 Watch 这个仓库。** 下一阶段我们会继续推进记忆系统方法,
> 包括 Wiki 式知识层和用于更深层离线进化的 Dreaming。
<table>
<tr>
<td width="33%" valign="top">
<strong>Markdown As Source Of Truth</strong><br>
<br>
所有记忆持久化为 <code>.md</code> 文件:可读、可改、可 grep、可 Git 版本化,也可直接用 Obsidian 打开。
</td>
<td width="33%" valign="top">
<strong>Local Three-Part Stack</strong><br>
<br>
Markdown + SQLite + LanceDB 在本地完成向量、BM25 和标量过滤检索,无需 MongoDB、Elasticsearch 或 Redis。
</td>
<td width="33%" valign="top">
<strong>Dual-Track Memory</strong><br>
<br>
Agent 记忆(<code>cases</code> / <code>skills</code>)与用户记忆(<code>episodes</code> / <code>profile</code>)独立提取,互不污染。
</td>
</tr>
<tr>
<td width="33%" valign="top">
<strong>Multimodal Ingestion</strong><br>
<br>
文本、图像、音频、文档、PDF、HTML 和邮件统一抽取为可检索的记忆形态。
</td>
<td width="33%" valign="top">
<strong>Self-Evolution</strong><br>
<br>
从真实使用经验中自动抽取共性 skills重复模式沉淀为可复用流程无需重训。
</td>
<td width="33%" valign="top">
<strong>Orthogonal Retrieval</strong><br>
<br>
<code>user_id</code><code>agent_id</code><code>app_id</code><code>project_id</code><code>session_id</code> 五维独立检索。
</td>
</tr>
</table>
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 为什么选择 EverOS
EverOS 是一个开源 Python 框架,用来构建**跨 Agent、跨平台的自进化长期记忆**。
它为 maker 提供一层可携带的统一记忆层,适用于他们使用的每一个 Agent
Claude Code、Codex、OpenClaw、Hermes 等等。这样,上下文、决策、文件和
Agent 轨迹可以跟着工作流走,而不是被锁在某一个工具里。
EverOS 会把对话、Agent 轨迹和文件保存为可读 Markdown并同步本地 SQLite
和 LanceDB 索引以便快速检索。Agent 可以复用过去的 cases 和 skills从重复
工作流中自我改进,并逐渐变得更加主动。
系统围绕三个边界设计:
1. **记忆内容保持可读** - Markdown 是长期、耐用的 source of truth。
2. **运行时状态保持本地** - SQLite 跟踪状态LanceDB 处理向量、BM25 和结构化过滤搜索。
3. **算法保持模块化** - [EverAlgo](https://github.com/EverMind-AI/EverAlgo) 负责记忆算法EverOS 负责运行时、持久化、在线流程和离线进化。
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 快速开始
### 1. 安装 EverOS
```bash
uv pip install everos
# or: pip install everos
```
### 2. 初始化配置
生成一个 starter `.env` 文件,然后根据生成的注释填入 API key 字段。
```bash
everos init
```
`everos init` 默认写入 `./.env`。也可以使用 `everos init --xdg`
写入 `${XDG_CONFIG_HOME:-~/.config}/everos/.env`
### 3. 启动服务
```bash
everos --help
everos server start
```
`everos server start` 会按以下顺序查找 `.env``--env-file <path>`
`./.env`(当前目录)→ `${XDG_CONFIG_HOME:-~/.config}/everos/.env`
`~/.everos/.env`。端点栈兼容 OpenAI protocolOpenAI / OpenRouter /
vLLM / Ollama / DeepInfra。你可以覆盖生成的 `.env` 中的 `*__BASE_URL`
来指向任意这些模型服务。
完整 walkthrough添加对话、flush、search然后读取 Markdown
[QUICKSTART.md](QUICKSTART.md)。
### 可选:摄取多模态文件
如果要通过 `/api/v1/memory/add``content` items 摄取非文本内容
image / pdf / audio / office documents安装可选 extra
```bash
uv pip install 'everos[multimodal]' # or: pip install 'everos[multimodal]'
```
这会引入 `everalgo-parser`(包含用于 SVG 支持的 `[svg]` bundle通过
cairosvg并接入多模态 LLM client`.env` 中的 `EVEROS_MULTIMODAL__*`
字段,默认通过 OpenRouter 使用 `google/gemini-3-flash-preview`)。
**Office 文档支持需要 LibreOffice 作为系统依赖。** parser 会调用
`soffice`LibreOffice 的 headless renderer先把 `.doc` / `.docx` /
`.ppt` / `.pptx` / `.xls` / `.xlsx` 转换为 PDF再交给多模态 LLM。
如果没有 LibreOfficeoffice 上传会返回 HTTP 415并带有明确错误信息
PDF / image / audio / HTML / email 解析不受影响。
在提供 office 文档服务前,请先在宿主机安装:
```bash
brew install --cask libreoffice # macOS
sudo apt-get install -y libreoffice # Debian / Ubuntu
```
### 贡献者开发
```bash
git clone https://github.com/EverMind-AI/EverOS.git
cd EverOS
uv sync # creates ./.venv and installs deps
source .venv/bin/activate # or prefix commands with `uv run`
everos init # fill the four API key slots in .env (two distinct keys)
everos --help
make test
```
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 架构概览
```
┌───────────────────────────────────────────────┐
│ entrypoints/ (CLI + HTTP API) │ presentation
├───────────────────────────────────────────────┤
│ service/ (use cases: memorize/retrieve) │ application
├───────────────────────────────────────────────┤
│ memory/ (extract + search + cascade) │ domain
├───────────────────────────────────────────────┤
│ infra/ (markdown / sqlite / lancedb) │ infrastructure
└───────────────────────────────────────────────┘
↑ ↑
component/ core/
(LLM/Embedding) (observability/lifespan)
```
DDD 5 层架构,单向依赖。详见 [docs/architecture.md](docs/architecture.md)。
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 存储布局
```
~/.everos/
├── default_app/ # app_id ("default" → "default_app" on disk)
│ └── default_project/ # project_id ("default" → "default_project")
│ ├── users/<user_id>/
│ │ ├── user.md # profile
│ │ ├── episodes/ # daily-log episodes (visible)
│ │ ├── .atomic_facts/ # nested facts (dotfile-hidden)
│ │ └── .foresights/ # predictive memory (dotfile-hidden)
│ └── agents/<agent_id>/
│ ├── agent.md
│ ├── .cases/ # one task case per entry
│ └── skills/ # named procedural memories
├── .index/ # derived indexes (rebuildable from md)
│ ├── sqlite/system.db # state + queue + audit
│ └── lancedb/*.lance/ # vector + BM25 + scalar
└── .tmp/ # transient working files
```
在 Obsidian 中打开任意 `<app>/<project>/users/<user_id>/` 文件夹即可。
你的 Agent 大脑本质上就是一组文件。dotfile 目录(`.atomic_facts/`
`.foresights/``.cases/`)默认保持隐藏,因此可见文件夹仍然是面向用户的
记忆表面,而提取出的衍生信息则安静地放在旁边。
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 功能
- **混合检索**: BM25 + vectorHNSW/IVF-PQ+ scalar filter在 LanceDB 中完成单次查询
- **级联索引同步**: 编辑 `.md` → file watcher → entry-level diff → LanceDB sync亚秒级同步
- **多源提取**: conversations / agent trajectories / file knowledge
- **双轨记忆**: user-trackEpisodes / Profiles+ agent-trackCases / Skills
- **异步优先**: 完整 asyncio单一 event loop
- **多模态**: text + 小图片 / audio inline大媒体通过 S3/OSS reference
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 项目结构
```
everos/ # repo root
├── src/everos/ # main package (src layout)
│ ├── entrypoints/ # cli + api
│ ├── service/ # use case orchestration
│ ├── memory/ # domain: extract + search + cascade + prompt_slots
│ ├── infra/ # storage: markdown + lancedb + sqlite
│ ├── component/ # cross-cutting: llm / embedding / config / utils
│ ├── core/ # runtime: observability / lifespan / context
│ └── config/ # configuration data + Settings schema
├── tests/ # unit / integration / golden / fixtures
├── docs/ # design docs
└── .claude/ # team-shared rules + skills (auto-loaded by Claude Code)
```
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 文档
- [docs/overview.md](docs/overview.md) - 项目概览与愿景
- [docs/architecture.md](docs/architecture.md) - DDD 分层架构与依赖规则
- [docs/engineering.md](docs/engineering.md) - 工程与开发效率基础设施CI / tooling / Claude Code
- [docs/use-cases.md](docs/use-cases.md) - 完整使用场景 gallery 和集成示例
- [docs/migration-to-1.0.0.md](docs/migration-to-1.0.0.md) - Legacy API 与基础设施迁移说明
- [CHANGELOG.md](CHANGELOG.md) - 发布记录
- [CONTRIBUTING.md](CONTRIBUTING.md) - 如何贡献
- [.claude/rules/](.claude/rules/) - 详细代码规范Claude Code 会自动加载)
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 使用场景
这些使用场景展示了持久记忆可以在真实产品和工作流中带来什么能力。
有些示例已经打包在本仓库中,另一些则指向外部 demo 或集成,你可以研究并复用。
<table>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/840470d7-a838-4c05-8685-dd797d4e9cdf)](https://evermind.ai/usecase_reunite)
#### Reunite - 用 EverOS 找回连接
父母描述他们记得的线索孩子描述他们残留的回忆。Reunite 使用语义记忆来浮现这些连接。
[了解更多](https://evermind.ai/usecase_reunite)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/7282b38b-56bf-4356-aa7b-06a845e7683d)](https://github.com/tt-a1i/hive)
#### Hive Orchestrator
面向 CLI coding agents 的 browser-native hive-mind。Claude Code、Codex、Gemini 和 OpenCode 作为真实 PTY 进程,通过团队协议协作。
[代码](https://github.com/tt-a1i/hive)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/867d9329-ce9a-496f-ab1e-15c77974e5fa)](https://github.com/tt-a1i/evermemos-mcp)
#### 接入 EverOS 的 AI 编程助手
由 EverOS 驱动的通用长期记忆层,面向 AI coding assistants。
[代码](https://github.com/tt-a1i/evermemos-mcp)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/a4f0fd86-1c81-4445-bebc-e51eb5e33b30)](https://github.com/yuansui123/AI-Data-Technician-EverMemOS)
#### AI Data Technician
一个 agentic AI 系统,可以从科学家的交互中学习,用于检查、分析和分类高维时间序列数据,并通过跨 session 改进的持久记忆持续变强。
[代码](https://github.com/yuansui123/AI-Data-Technician-EverMemOS)
</td>
</tr>
<tr>
<td width="50%" valign="top">
![banner-gif](https://github.com/user-attachments/assets/650b901b-c9ba-4001-bac7-626b009df830)
#### 接入 EverOS 的 Rokid AI 助手
在 Rokid Glasses 中连接 EverOS为你的智能活动启用长期记忆。
即将推出
</td>
<td width="50%" valign="top">
![banner-gif](https://github.com/user-attachments/assets/85b338b2-e48e-4a65-9f30-0bc6998df872)
#### 带长期记忆的创意助手
拥有长期记忆的创意助手,让你的创作上下文可以跨 session 持续可用。
即将推出
</td>
</tr>
<tr>
<td colspan="2" align="right">
<a href="#readme-top"><img src="https://img.shields.io/badge/-Back_to_top-gray?style=flat-square" alt="Back to top"></a>
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/f30617a1-adc0-4271-bc0e-c3a0b28cb903)](https://github.com/xunyud/Earth-Online)
#### Earth Online 记忆游戏
Earth Online 是一款 memory-aware productivity game把日常计划变成一个持续生长的 quest log。
[代码](https://github.com/xunyud/Earth-Online)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/57d8cda7-35a5-4561-b794-5520dffc917b)](https://github.com/golutra/golutra)
#### 多 Agent 编排平台
Golutra 为工程团队提供 multi-agent workforce把 IDE 从单一 assistant 扩展为协同 agents。
[代码](https://github.com/golutra/golutra)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/75f19db5-30f6-4eed-9b1e-c9c6a0e6b7de)](https://github.com/Yangtze-Seventh/taste-verse)
#### 你的个人品鉴宇宙
通过沉浸式 3D 星图记录、可视化并探索你的 tasting journey。
[代码](https://github.com/Yangtze-Seventh/taste-verse)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/93ac2a68-4f18-4fcb-8d87-80aeb00a9d7c)](https://github.com/kellyvv/OpenHer)
#### EverOS Open Her
构建有感受的 AI。开源 persona engine让 personality 从 neural drives 中涌现,而不是来自 prompts。灵感来自 Her。
[代码](https://github.com/kellyvv/OpenHer)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/550071c1-dc39-4964-9f67-ffdfad792345)](https://chromewebstore.google.com/detail/ruminer-browser-agent/lbccjohfpdpimbhpckljimgolndfmfif)
#### 面向个人记忆的浏览器 Agent
Ruminer 为 browser agent 带来持久记忆,让它能在不同网页任务之间携带个人上下文。
[插件](https://chromewebstore.google.com/detail/ruminer-browser-agent/lbccjohfpdpimbhpckljimgolndfmfif)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/c258a6c4-fe70-497a-98d1-3dade4a932f6)](https://github.com/nanxingw/EverMem)
#### EverMem 与 EverOS 同步
一条命令,把任意 AI coding CLI 连接到 EverMemOS 长期记忆。
[代码](https://github.com/nanxingw/EverMem)
</td>
</tr>
<tr>
<td colspan="2" align="right">
<a href="#readme-top"><img src="https://img.shields.io/badge/-Back_to_top-gray?style=flat-square" alt="Back to top"></a>
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/39274473-ceb3-48fb-a031-e22230decbe2)](https://github.com/mco-org/mco)
#### MCO - 编排 AI Coding Agents
MCO 为你的主 Agent 配备一个 agent team让它们可以一起处理复杂任务。
[代码](https://github.com/mco-org/mco)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/314c9126-8e08-4688-bbbb-8555ad58cf67)](https://github.com/onenewborn/StudyBuddy-public)
#### 带自进化记忆的 Study Buddy
使用拥有 self-evolving memory 的 Agent主动辅助学习。
[代码](https://github.com/onenewborn/StudyBuddy-public)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/21da76aa-9a8b-48e0-9134-42429d7390e7)](https://github.com/TonyLiangDesign/MemoCare)
#### 阿尔茨海默症记忆助手
通过高级记忆支持和日常辅助,帮助有需要的人更好地生活。
[代码](https://github.com/TonyLiangDesign/MemoCare)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/e2428df3-ea11-4e88-8f9c-dad437dd8998)](https://github.com/AlexL1024/NeuralConnect)
#### 记忆驱动的 Multi-Agent NPC 体验
一款 iOS 科幻悬疑游戏,玩家可以探索世界并揭开真相。
[代码](https://github.com/AlexL1024/NeuralConnect)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/e6eaf308-a874-483f-8874-6934bf95a78f)](https://github.com/elontusk5219-prog/Mobi)
#### Mobi Companion
一款 iOS app用户可以创建、养成并与名为 Mobi 的个性化 AI companion 一起生活。
[代码](https://github.com/elontusk5219-prog/Mobi)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/9aabcaa9-f97a-49d2-9109-0b5bb696ed41)](https://github.com/JaMesLiMers/EvermemCompetition-Spiro)
#### 带记忆的 AI 可穿戴设备
一个 context-native AI wearable聆听日常生活并把对话转换为记忆。
[代码](https://github.com/JaMesLiMers/EvermemCompetition-Spiro)
</td>
</tr>
<tr>
<td colspan="2" align="right">
<a href="#readme-top"><img src="https://img.shields.io/badge/-Back_to_top-gray?style=flat-square" alt="Back to top"></a>
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/df9677ec-386f-4c56-a428-08bca25c54dc)](docs/migration-to-1.0.0.md)
#### Legacy OpenClaw Agent 记忆
已归档的 pre-1.0.0 plugin reference。新的集成应使用 EverOS 1.0.0 API。
[了解更多](docs/migration-to-1.0.0.md)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/3a2357a1-c0c3-464a-8979-0d1cdfc9b0d4)](https://github.com/TEN-framework/ten-framework/tree/04cb80601374fa9e35b4e544b2dbd23286ca7763/ai_agents/agents/examples/voice-assistant-with-EverMemOS)
#### 带记忆的 Live2D 角色
为实时 Live2D character 添加长期记忆,由 [TEN Framework](https://github.com/TEN-framework/ten-framework) 驱动。
[代码](https://github.com/TEN-framework/ten-framework/tree/04cb80601374fa9e35b4e544b2dbd23286ca7763/ai_agents/agents/examples/voice-assistant-with-EverMemOS)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/c36bdc04-97d3-4fe9-97d9-4b93b475595a)](https://screenshot-analysis-vercel.vercel.app/)
#### 带记忆的 Computer-Use
运行基于截图的分析任务,并把结果存入记忆。
[在线演示](https://screenshot-analysis-vercel.vercel.app/)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/54a7cf8f-62c4-4fbc-9d50-b214d034e051)](use-cases/game-of-throne-demo)
#### Game Of Thrones Memories
通过与 *A Game of Thrones* 互动问答体验,展示 AI 记忆基础设施。
[代码](use-cases/game-of-throne-demo)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/af37c1f6-7ba5-430c-b99d-2a7e7eac618f)](use-cases/claude-code-plugin)
#### Claude Code Plugin
Claude Code 的持久记忆插件。自动保存并回忆过去 coding sessions 的上下文。
[代码](use-cases/claude-code-plugin)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/d521d28c-0ccd-44ff-aecc-828245e2f973)](https://main.d2j21qxnymu6wl.amplifyapp.com/graph.html)
#### 记忆图谱可视化
在图界面中探索已存储的 entities 和 relationships。前端 demo 已可用;后端集成仍在进行中。
[在线演示](https://main.d2j21qxnymu6wl.amplifyapp.com/graph.html)
</td>
</tr>
</table>
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## 关注 EverOS
EverOS 1.0.0 是更大规模记忆系统路线图的第一个发布版本。Watch 这个仓库,
即可持续关注 Wiki 式记忆、Dreaming、更深入的离线进化、benchmark releases
以及更多真实 Agent 集成。
如果 EverOS 对你的 Agent stack 有帮助Star 这个仓库也会帮助更多 builders
发现它。
### Star 趋势
[![Star 趋势图](https://api.star-history.com/svg?repos=EverMind-AI/EverOS&type=Date)](https://www.star-history.com/#EverMind-AI/EverOS&Date)
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
## EverMind 生态
EverMind 是一个面向长期记忆、自进化 Agent 和记忆评测的开源生态。
<table>
<tr>
<th colspan="2">EverMind 开源生态</th>
</tr>
<tr>
<td><strong>核心记忆架构</strong></td>
<td><a href="https://github.com/EverMind-AI/EverOS">EverOS</a> - 本地记忆操作系统,以及有研究支撑的 Agent 和用户记忆运行时。</td>
</tr>
<tr>
<td><strong>算法引擎</strong></td>
<td><a href="https://github.com/EverMind-AI/EverAlgo">EverAlgo</a> - stateless extraction、ranking、parsing 和 memory operators为 EverOS 提供算法能力。</td>
</tr>
<tr>
<td><strong>替代架构</strong></td>
<td><a href="https://github.com/EverMind-AI/HyperMem">HyperMem</a> - 面向长期对话的 hypergraph memory拥有独立的 benchmark-backed topic -> episode -> fact 检索方法。</td>
</tr>
<tr>
<td><strong>Benchmarks</strong></td>
<td><a href="https://github.com/EverMind-AI/EverMemBench">EverMemBench</a> · <a href="https://github.com/EverMind-AI/EvoAgentBench">EvoAgentBench</a> - conversational memory 和 Agent self-evolution 的评测套件。</td>
</tr>
<tr>
<td><strong>Long-Context Research</strong></td>
<td><a href="https://github.com/EverMind-AI/MSA">MSA</a> - Memory Sparse Attention用于可扩展 latent memory 和 100M-token contexts。</td>
</tr>
<tr>
<td><strong>个人记忆层</strong></td>
<td><a href="https://github.com/EverMind-AI/EverMe">EverMe</a> - CLI 和 Agent plugin suite用于跨设备、跨 Agent 的个人记忆。</td>
</tr>
<tr>
<td><strong>开发者集成</strong></td>
<td><a href="https://github.com/EverMind-AI/evermem-claude-code">evermem-claude-code</a> · <a href="https://github.com/EverMind-AI/everos-plugins">everos-plugins</a> - AI coding agents 的 plugins、skills 和 migration tooling。</td>
</tr>
</table>
这些仓库共同构成 EverMind 的 research-to-runtime stack新的记忆方法、可复用算法、
benchmark evidence以及可落地的 Agent 集成。
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>
<br>
## 参与贡献
欢迎为整个仓库贡献架构方法、benchmark coverage、use-case examples、文档和 bug fixes。
浏览 [Issues](https://github.com/EverMind-AI/EverOS/issues) 找到适合的切入点,
准备好后即可提交 PR。
<br>
> [!TIP]
>
> **欢迎各种形式的贡献** 🎉
>
> 一起让 EverOS 变得更好。代码、文档、benchmark reports、use-case write-ups
> 和 integration examples 都很有价值。也欢迎在社交媒体上分享你的项目,启发更多人。
>
> 你可以在 𝕏 上联系 EverOS maintainer [@elliotchen200](https://x.com/elliotchen200)
> 或在 GitHub 上联系 [@cyfyifanchen](https://github.com/cyfyifanchen),获取项目更新、
> 讨论和协作机会。
![divider](https://github.com/user-attachments/assets/2e2bbcc6-e6d8-4227-83c6-0620fc96f761#gh-light-mode-only)
![divider](https://github.com/user-attachments/assets/d57fad08-4f49-4a1c-bdfc-f659a5d86150#gh-dark-mode-only)
### 代码贡献者
[![EverOS Contributors](https://contrib.rocks/image?repo=EverMind-AI/EverOS)](https://github.com/EverMind-AI/EverOS/graphs/contributors)
![divider](https://github.com/user-attachments/assets/2e2bbcc6-e6d8-4227-83c6-0620fc96f761#gh-light-mode-only)
![divider](https://github.com/user-attachments/assets/d57fad08-4f49-4a1c-bdfc-f659a5d86150#gh-dark-mode-only)
### 许可证
[Apache License 2.0](LICENSE) - 第三方归属说明请见 [NOTICE](NOTICE)。
### 引用
如果你在研究中使用 EverOS请参考 [CITATION.md](CITATION.md)。
<br>
<div align="right">
[![](https://img.shields.io/badge/-Back_to_top-gray?style=flat-square)](#readme-top)
</div>

View File

@ -24,6 +24,17 @@
model = "gpt-4o-mini"
api_key = "sk-..."
base_url = "https://api.openai.com/v1"
timeout_seconds = 180.0
# ── Multimodal LLM ───────────────────────────────────
# Independent vision/audio-capable chat-completions endpoint for parsing.
[multimodal]
model = "google/gemini-3-flash-preview"
api_key = "sk-..."
base_url = "https://openrouter.ai/api/v1"
timeout_seconds = 180.0
resize_images_for_vlm = true
max_concurrency = 4
# ── Embedding ─────────────────────────────────────────
[embedding]

View File

@ -793,7 +793,7 @@ attribution, so `session_id` is the only meaningful query dimension.
| `sender_id` | `string` | Original sender id from `/add` |
| `sender_name` | `string \| null` | Original sender name; `null` if not provided |
| `role` | `"user" \| "assistant" \| "tool"` | Original role |
| `content` | `string \| array<object>` | `string` for the single-text shorthand, `array` of opaque content items for the original multimodal payload (mirrors [MessageItem.content](#addmessage)) |
| `content` | `string \| array<object>` | `string` for the single-text shorthand, `array` of opaque content items for the original multimodal payload (mirrors [MessageItem.content](#messageitem)) |
| `timestamp` | `string` | ISO-8601 with timezone offset — see [Conventions](#conventions) |
| `tool_calls` | `array<object> \| null` | Original tool_calls payload if any |
| `tool_call_id` | `string \| null` | Original tool_call_id if any |

View File

@ -204,7 +204,7 @@ everalgo is:
- **No I/O** — does not touch md files / LanceDB / SQLite
- **No prompts inline** — receives `PromptSlot` parameter, project supplies defaults
This boundary lets everalgo be reused across product forms (this open-source build, EverOS Cloud, OpenClaw plugins, etc.).
This boundary lets everalgo be reused across product forms (this open-source build, EverMind Cloud, OpenClaw plugins, etc.).
## Further reading

View File

@ -204,7 +204,7 @@ Lives in `LanceDBSettings`; overridable via the
`EVEROS_LANCEDB__INDEX_CACHE_SIZE_BYTES` environment variable. This
is the only knob that bounds the steady-state file-descriptor count
of a long-running EverOS daemon — see
[Recovery paths § FD exhaustion](#fd-exhaustion-os-error-24-emfile)
[Recovery paths § FD exhaustion](#fd-exhaustion-os-error-24--emfile)
for why nothing else (prune, rebuild, `drop_index`) helps.
Measured cap → FD ceiling (30 add+optimize cycles + 100-query stress

View File

@ -69,11 +69,13 @@ Reasons this is documented separately:
│ │ ├ check-yaml / check-toml │ │
│ │ ├ check-added-large-files (≥1MB warn) │ │
│ │ ├ detect-private-key │ │
│ │ ├ no committed images/videos/assets │ │
│ │ └ gitlint (commit-msg stage) │ │
│ │ │ │
│ │ ruff lint + format │ │
│ │ (replaces black / isort / flake8) │ │
│ │ import-linter DDD layer-direction enforcement │ │
│ │ repo asset gate blocks images/videos/assets in git │ │
│ │ pytest unit / integration │ │
│ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
@ -94,6 +96,7 @@ Reasons this is documented separately:
│ ┌─ CI/CD (GitHub Actions) ───────────────────────────────────┐ │
│ │ │ │
│ │ CI: .github/workflows/ci.yml lint / test / integ │ │
│ │ / package build │ │
│ │ Docs: .github/workflows/docs.yml Markdown + YAML check │ │
│ │ Gates invoke Makefile targets; the Makefile is the │ │
│ │ single source of truth for commands. │ │
@ -204,19 +207,21 @@ Stage 2: pre-commit (triggered by `git commit`)
├ check-yaml, check-toml
├ check-added-large-files (≥1MB)
├ detect-private-key
├ no-repo-assets (rejects images/videos/assets in git)
└ gitlint (commit-msg stage; rejects malformed messages)
Stage 3: local `make ci` (manual, before push)
├ make lint (ruff check + ruff format --check + import-linter)
├ make lint (ruff + import-linter + repo hygiene gates)
├ make test (pytest tests/unit)
make integration (pytest tests/integration)
make integration (pytest tests/integration)
└ make package (sdist/wheel build + import smoke test)
Stage 4: CI (GitHub Actions, push + PR triggered)
└ re-runs the same `make lint / test / integration` targets
└ re-runs the same `make lint / test / integration / package` targets
@ -272,7 +277,7 @@ dev = ["ruff", "pytest", "pytest-asyncio", "pytest-cov",
make help list all targets
make install uv sync --frozen
make format ruff fix + format
make lint ruff + import-linter + datetime discipline + openapi drift
make lint ruff + import-linter + repo asset/media + datetime discipline + openapi drift
make test pytest tests/unit
make integration pytest tests/integration
make package build sdist/wheel + smoke-test wheel import
@ -338,6 +343,7 @@ Every key has a sensible default except the `API_KEY` fields, which you fill in.
|---|---|---|
| Lint | `make lint` (ruff check + ruff format --check) | any error |
| Layer direction | `make lint` (lint-imports inside) | layer violation |
| Repository media | `make lint` (check_repo_assets.py) | images/videos/assets committed |
| Datetime discipline | `make lint` (check_datetime_discipline.py) | bypasses helper module |
| OpenAPI drift | `make lint` (dump_openapi.py --check) | schema ≠ committed openapi.json |
| Unit | `make test` (pytest tests/unit) | any failure |

View File

@ -15,6 +15,7 @@ already know what you want to do and need to know exactly how.
| [cli.md](cli.md) | `everos` CLI subcommands + env var conventions |
| [storage_layout.md](storage_layout.md) | Memory-root tree + frontmatter chassis + EntryId encoding |
| [prompt_slots.md](prompt_slots.md) | YamlConfigLoader + three-layer prompt override |
| [migration-to-1.0.0.md](migration-to-1.0.0.md) | Legacy API and infrastructure migration notes for EverOS 1.0.0 |
## Explanation
@ -52,6 +53,7 @@ Top-level project files live next to the repo root:
- [README.md](../README.md) — quick start & feature overview
- [QUICKSTART.md](../QUICKSTART.md) — 5-minute walkthrough (install → service → search)
- [use-cases.md](use-cases.md) — full use-case gallery and integration examples
- [CONTRIBUTING.md](../CONTRIBUTING.md) — how to contribute (issue-only model)
- [CHANGELOG.md](../CHANGELOG.md) — release notes
- [SECURITY.md](../SECURITY.md) — security policy & private vulnerability reporting

View File

@ -0,0 +1,67 @@
# EverOS 1.0.0 Migration Notes
EverOS 1.0.0 is a fresh architecture. Historical issues, examples, and
plugins may reference APIs and infrastructure that no longer exist in
the current open-source repository.
## Current 1.0.0 Contract
The supported local OSS HTTP API lives under:
```text
POST /api/v1/memory/add
POST /api/v1/memory/flush
POST /api/v1/memory/search
POST /api/v1/memory/get
```
Use [api.md](api.md) for the canonical request and response schemas.
The storage stack is:
```text
Markdown files + SQLite + LanceDB
```
MongoDB, Elasticsearch, Milvus, Redis, Kafka, longjob workers, and the
old Docker Compose stack are not part of EverOS 1.0.0.
## Legacy API Mapping
These pre-1.0.0 routes are no longer supported by the OSS repo:
| Legacy route | EverOS 1.0.0 replacement |
|---|---|
| `POST /api/v1/memories` | `POST /api/v1/memory/add` |
| `POST /api/v1/memories/group` | `POST /api/v1/memory/add` with `app_id` / `project_id` scoping |
| `GET /api/v1/memories/search` | `POST /api/v1/memory/search` |
| `POST /api/v1/memories/search` | `POST /api/v1/memory/search` |
| `GET /api/v1/memories` | `POST /api/v1/memory/get` |
| `POST /api/v1/memories/get` | `POST /api/v1/memory/get` |
| `/api/v3/agentic/*` | `POST /api/v1/memory/*` |
The 1.0.0 API also changed memory type names. For example,
`episodic_memory` is now `episode` in `/get`; `/search` returns typed
arrays such as `episodes`, `profiles`, `agent_cases`, and
`agent_skills`.
## Integration Guidance
For new integrations:
- Batch messages into one `/api/v1/memory/add` request instead of
sending one flat message object per HTTP call.
- Use `/flush` when a demo or test needs immediate extraction.
- Use `/search` for ranked recall and `/get` for paginated browsing.
- Treat old OpenClaw and EverMem Cloud plugin examples as archived
references unless they have been explicitly updated to the 1.0.0 API.
## Benchmark Guidance
The current LoCoMo reproduction path is documented in
[locomo_benchmark.md](locomo_benchmark.md). The benchmark driver uses
the 1.0.0 server API: add, flush, search, answer, and evaluate.
Old HyperMem / pre-1.0.0 evaluation pipeline reports should not be used
as 1.0.0 bug reports unless they can be reproduced with the current
benchmark commands.

307
docs/use-cases.md Normal file
View File

@ -0,0 +1,307 @@
# EverOS Use Cases
Use cases show what persistent memory makes possible in real products and
workflows. Some examples are packaged in this repository; others point to
external demos or integrations you can study and adapt.
<table>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/840470d7-a838-4c05-8685-dd797d4e9cdf)](https://evermind.ai/usecase_reunite)
#### Reunite - Find with EverOS
Parents describe what they remember. Children describe what they recall. Reunite uses semantic memory to surface the connections.
[Learn more](https://evermind.ai/usecase_reunite)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/7282b38b-56bf-4356-aa7b-06a845e7683d)](https://github.com/tt-a1i/hive)
#### Hive Orchestrator
Browser-native hive-mind for CLI coding agents - Claude Code, Codex, Gemini, and OpenCode collaborate as real PTY processes via a team protocol.
[Code](https://github.com/tt-a1i/hive)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/867d9329-ce9a-496f-ab1e-15c77974e5fa)](https://github.com/tt-a1i/evermemos-mcp)
#### AI Coding Assistants with EverOS
Universal long-term memory layer for AI coding assistants, powered by EverOS.
[Code](https://github.com/tt-a1i/evermemos-mcp)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/a4f0fd86-1c81-4445-bebc-e51eb5e33b30)](https://github.com/yuansui123/AI-Data-Technician-EverMemOS)
#### AI Data Technician
An agentic AI system that learns from scientist interaction to inspect, analyze, and classify high-dimensional time series data - with persistent memory that improves across sessions.
[Code](https://github.com/yuansui123/AI-Data-Technician-EverMemOS)
</td>
</tr>
<tr>
<td width="50%" valign="top">
![banner-gif](https://github.com/user-attachments/assets/650b901b-c9ba-4001-bac7-626b009df830)
#### Rokid AI Assistant with EverOS
Connect to EverOS within Rokid Glasses enabling long-term memory for all of your smart activities.
Coming soon
</td>
<td width="50%" valign="top">
![banner-gif](https://github.com/user-attachments/assets/85b338b2-e48e-4a65-9f30-0bc6998df872)
#### Creative Assistant with Memory
Creative assistant with long-term memory, so your creative context stays available across sessions.
Coming soon
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/f30617a1-adc0-4271-bc0e-c3a0b28cb903)](https://github.com/xunyud/Earth-Online)
#### Earth Online Memory Game
Earth Online is a memory-aware productivity game that turns everyday planning into a living quest log.
[Code](https://github.com/xunyud/Earth-Online)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/57d8cda7-35a5-4561-b794-5520dffc917b)](https://github.com/golutra/golutra)
#### Multi-Agent Orchestration Platform
Golutra presents a multi-agent workforce for engineering teams, extending the IDE model from a single assistant to coordinated agents.
[Code](https://github.com/golutra/golutra)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/75f19db5-30f6-4eed-9b1e-c9c6a0e6b7de)](https://github.com/Yangtze-Seventh/taste-verse)
#### Your Personal Tasting Universe
Record, visualize, and explore your tasting journey through an immersive 3D star map.
[Code](https://github.com/Yangtze-Seventh/taste-verse)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/93ac2a68-4f18-4fcb-8d87-80aeb00a9d7c)](https://github.com/kellyvv/OpenHer)
#### EverOS Open Her
Build AI that feels. Open-source persona engine - personality emerges from neural drives, not prompts. Inspired by Her.
[Code](https://github.com/kellyvv/OpenHer)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/550071c1-dc39-4964-9f67-ffdfad792345)](https://chromewebstore.google.com/detail/ruminer-browser-agent/lbccjohfpdpimbhpckljimgolndfmfif)
#### Browser Agent for Personal Memory
Ruminer brings persistent memory to a browser agent so it can carry personal context across web tasks.
[Plugin](https://chromewebstore.google.com/detail/ruminer-browser-agent/lbccjohfpdpimbhpckljimgolndfmfif)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/c258a6c4-fe70-497a-98d1-3dade4a932f6)](https://github.com/nanxingw/EverMem)
#### EverMem Sync with EverOS
One command to connect any AI coding CLI to EverMemOS long-term memory.
[Code](https://github.com/nanxingw/EverMem)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/39274473-ceb3-48fb-a031-e22230decbe2)](https://github.com/mco-org/mco)
#### MCO - Orchestrate AI Coding Agents
MCO equips your primary agent with an agent team that can work together to solve complex tasks.
[Code](https://github.com/mco-org/mco)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/314c9126-8e08-4688-bbbb-8555ad58cf67)](https://github.com/onenewborn/StudyBuddy-public)
#### Study Buddy with Self-Evolving Memory
Study proactively with an agent that has self-evolving memory.
[Code](https://github.com/onenewborn/StudyBuddy-public)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/21da76aa-9a8b-48e0-9134-42429d7390e7)](https://github.com/TonyLiangDesign/MemoCare)
#### Alzheimer's Memory Assistant
Empowering individuals with advanced memory support and daily assistance.
[Code](https://github.com/TonyLiangDesign/MemoCare)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/e2428df3-ea11-4e88-8f9c-dad437dd8998)](https://github.com/AlexL1024/NeuralConnect)
#### Memory-Driven Multi-Agent NPC Experience
An iOS sci-fi mystery game where players explore and uncover the truth.
[Code](https://github.com/AlexL1024/NeuralConnect)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/e6eaf308-a874-483f-8874-6934bf95a78f)](https://github.com/elontusk5219-prog/Mobi)
#### Mobi Companion
An iOS app where users create, nurture, and live with a personalized AI companion called Mobi.
[Code](https://github.com/elontusk5219-prog/Mobi)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/9aabcaa9-f97a-49d2-9109-0b5bb696ed41)](https://github.com/JaMesLiMers/EvermemCompetition-Spiro)
#### AI Wearable with Memory
A context-native AI wearable that listens to everyday life and converts conversations into memory.
[Code](https://github.com/JaMesLiMers/EvermemCompetition-Spiro)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/df9677ec-386f-4c56-a428-08bca25c54dc)](migration-to-1.0.0.md)
#### Legacy OpenClaw Agent Memory
Archived pre-1.0.0 plugin reference. New integrations should use the EverOS 1.0.0 API.
[Learn more](migration-to-1.0.0.md)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/3a2357a1-c0c3-464a-8979-0d1cdfc9b0d4)](https://github.com/TEN-framework/ten-framework/tree/04cb80601374fa9e35b4e544b2dbd23286ca7763/ai_agents/agents/examples/voice-assistant-with-EverMemOS)
#### Live2D Character with Memory
Add long-term memory to a real-time Live2D character, powered by [TEN Framework](https://github.com/TEN-framework/ten-framework).
[Code](https://github.com/TEN-framework/ten-framework/tree/04cb80601374fa9e35b4e544b2dbd23286ca7763/ai_agents/agents/examples/voice-assistant-with-EverMemOS)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/c36bdc04-97d3-4fe9-97d9-4b93b475595a)](https://screenshot-analysis-vercel.vercel.app/)
#### Computer-Use with Memory
Run screenshot-based analysis with computer-use and store the results in memory.
[Live Demo](https://screenshot-analysis-vercel.vercel.app/)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/54a7cf8f-62c4-4fbc-9d50-b214d034e051)](../use-cases/game-of-throne-demo)
#### Game of Thrones Memories
A demonstration of AI memory infrastructure through an interactive Q&A experience with *A Game of Thrones*.
[Code](../use-cases/game-of-throne-demo)
</td>
</tr>
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/af37c1f6-7ba5-430c-b99d-2a7e7eac618f)](../use-cases/claude-code-plugin)
#### Claude Code Plugin
Persistent memory for Claude Code. Automatically saves and recalls context from past coding sessions.
[Code](../use-cases/claude-code-plugin)
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/d521d28c-0ccd-44ff-aecc-828245e2f973)](https://main.d2j21qxnymu6wl.amplifyapp.com/graph.html)
#### Memory Graph Visualization
Explore stored entities and relationships in a graph interface. Frontend demo; backend integration is in progress.
[Live Demo](https://main.d2j21qxnymu6wl.amplifyapp.com/graph.html)
</td>
</tr>
</table>
<br>
[Back to README](../README.md)

View File

@ -21,9 +21,7 @@ ALLOWED_TYPES = (
"ci",
"revert",
)
TITLE_RE = re.compile(
rf"^({'|'.join(ALLOWED_TYPES)})(\([A-Za-z0-9._/-]+\))?(!)?: .+"
)
TITLE_RE = re.compile(rf"^({'|'.join(ALLOWED_TYPES)})(\([A-Za-z0-9._/-]+\))?(!)?: .+")
MAX_TITLE_LENGTH = 72
@ -33,7 +31,9 @@ def _run_git(args: list[str]) -> str:
def _default_range() -> str:
event_name = os.getenv("GITHUB_EVENT_NAME", "")
before = os.getenv("GITHUB_EVENT_BEFORE", "") or os.getenv("GITHUB_EVENT_BEFORE_SHA", "")
before = os.getenv("GITHUB_EVENT_BEFORE", "") or os.getenv(
"GITHUB_EVENT_BEFORE_SHA", ""
)
after = os.getenv("GITHUB_SHA", "HEAD")
pr_base = os.getenv("GITHUB_PR_BASE_SHA", "")
@ -83,7 +83,8 @@ def _validate(commit_range: str) -> list[str]:
short = commit[:12]
if len(subject) > MAX_TITLE_LENGTH:
failures.append(
f"{short}: subject is {len(subject)} chars; max is {MAX_TITLE_LENGTH}: {subject}"
f"{short}: subject is {len(subject)} chars; "
f"max is {MAX_TITLE_LENGTH}: {subject}"
)
continue

View File

@ -0,0 +1,86 @@
"""Block deprecated product names in tracked repository text."""
from __future__ import annotations
import re
import subprocess
from collections.abc import Iterable
from dataclasses import dataclass
from pathlib import Path
DEPRECATED_NAME_RE = re.compile(r"\bever[\s_-]*core\b", flags=re.IGNORECASE)
SKIP_SUFFIXES = frozenset(
{
".avif",
".bmp",
".gif",
".heic",
".heif",
".icns",
".ico",
".jpeg",
".jpg",
".mov",
".mp4",
".png",
".webp",
}
)
@dataclass(frozen=True)
class Violation:
path: str
line_number: int
line: str
def find_violations(files: Iterable[tuple[str, str]]) -> list[Violation]:
violations: list[Violation] = []
for path, text in files:
for line_number, line in enumerate(text.splitlines(), start=1):
if DEPRECATED_NAME_RE.search(line):
violations.append(
Violation(path=path, line_number=line_number, line=line.strip())
)
return violations
def _tracked_paths() -> list[Path]:
result = subprocess.run(
["git", "ls-files", "-z"],
check=True,
stdout=subprocess.PIPE,
text=False,
)
return [Path(raw.decode("utf-8")) for raw in result.stdout.split(b"\0") if raw]
def _tracked_text_files() -> Iterable[tuple[str, str]]:
for path in _tracked_paths():
if path.suffix.lower() in SKIP_SUFFIXES:
continue
try:
text = path.read_text(encoding="utf-8")
except UnicodeDecodeError:
continue
yield path.as_posix(), text
def main() -> int:
violations = find_violations(_tracked_text_files())
if not violations:
print("Deprecated-name check passed.")
return 0
print(
"Deprecated-name check failed.\n"
"Use EverOS or EverMind Cloud. Do not use deprecated product naming.\n"
)
for violation in violations:
print(f"- {violation.path}:{violation.line_number}: {violation.line}")
return 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -3,7 +3,6 @@
from __future__ import annotations
import re
import sys
from pathlib import Path
SKIP_DIRS = {".git", "node_modules", ".venv", ".uv-cache"}

70
scripts/check_pr_title.py Normal file
View File

@ -0,0 +1,70 @@
"""Validate pull request titles against the EverOS Conventional Commits policy."""
from __future__ import annotations
import importlib.util
import os
import sys
from pathlib import Path
from types import ModuleType
_SCRIPT_DIR = Path(__file__).resolve().parent
def _load_commit_policy() -> ModuleType:
policy_path = _SCRIPT_DIR / "check_commit_messages.py"
spec = importlib.util.spec_from_file_location("_commit_message_policy", policy_path)
if spec is None or spec.loader is None:
raise RuntimeError(f"Unable to load commit policy from {policy_path}")
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module
_POLICY = _load_commit_policy()
ALLOWED_TYPES = _POLICY.ALLOWED_TYPES
MAX_TITLE_LENGTH = _POLICY.MAX_TITLE_LENGTH
TITLE_RE = _POLICY.TITLE_RE
def validate_title(title: str) -> list[str]:
title = title.strip()
if not title:
return ["missing PR title"]
if len(title) > MAX_TITLE_LENGTH:
return [f"PR title is {len(title)} chars; max is {MAX_TITLE_LENGTH}: {title}"]
if not TITLE_RE.match(title):
allowed = ", ".join(ALLOWED_TYPES)
return [
f"invalid PR title: {title}\n"
" expected: <type>[(scope)][!]: <description>\n"
f" allowed types: {allowed}"
]
return []
def _title_from_args_or_env(argv: list[str]) -> str:
if argv:
return " ".join(argv)
return os.getenv("PR_TITLE", "")
def main(argv: list[str] | None = None) -> int:
title = _title_from_args_or_env(sys.argv[1:] if argv is None else argv)
failures = validate_title(title)
if failures:
print("Pull request title check failed:")
print("\n".join(failures))
return 1
print("Pull request title follows Conventional Commits.")
return 0
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,117 @@
"""Block committed image/video files and asset-style directories."""
from __future__ import annotations
import subprocess
from collections.abc import Iterable
from dataclasses import dataclass
from pathlib import PurePosixPath
BLOCKED_DIR_NAMES = frozenset(
{
"asset",
"assets",
"image",
"images",
"img",
"media",
"video",
"videos",
}
)
IMAGE_EXTENSIONS = frozenset(
{
".avif",
".bmp",
".gif",
".heic",
".heif",
".icns",
".ico",
".jpeg",
".jpg",
".png",
".svg",
".tif",
".tiff",
".webp",
}
)
VIDEO_EXTENSIONS = frozenset(
{
".avi",
".flv",
".m4v",
".mkv",
".mov",
".mp4",
".mpeg",
".mpg",
".webm",
".wmv",
}
)
@dataclass(frozen=True)
class Violation:
path: str
reason: str
def _normalise_path(path: str) -> PurePosixPath:
return PurePosixPath(path.replace("\\", "/"))
def _violation_reason(path: str) -> str | None:
posix_path = _normalise_path(path)
lower_parts = tuple(part.lower() for part in posix_path.parts)
if any(part in BLOCKED_DIR_NAMES for part in lower_parts):
return "asset/media directory"
suffix = posix_path.suffix.lower()
if suffix in IMAGE_EXTENSIONS:
return "image file"
if suffix in VIDEO_EXTENSIONS:
return "video file"
return None
def find_violations(paths: Iterable[str]) -> list[Violation]:
violations: list[Violation] = []
for path in paths:
reason = _violation_reason(path)
if reason is not None:
violations.append(Violation(path=path, reason=reason))
return violations
def _tracked_paths() -> list[str]:
result = subprocess.run(
["git", "ls-files", "-z"],
check=True,
stdout=subprocess.PIPE,
text=False,
)
return [raw.decode("utf-8") for raw in result.stdout.split(b"\0") if raw]
def main() -> int:
violations = find_violations(_tracked_paths())
if not violations:
print("Repository asset/media check passed.")
return 0
print(
"Repository asset/media check failed.\n"
"Do not commit images, videos, or asset/media directories. "
"Host visual media externally, in release artifacts, or another "
"approved storage location, then link to it from docs.\n"
)
for violation in violations:
print(f"- {violation.path}: {violation.reason}")
return 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -9,9 +9,16 @@ provider) instead of silently failing per-request downstream.
from __future__ import annotations
import base64
import binascii
from io import BytesIO
from typing import Any
from everalgo.llm import build_client
from everalgo.llm.config import LLMConfig
from everalgo.llm.protocols import LLMClient
from everalgo.llm.types import ChatMessage, ChatResponse, ImageUrlPart, TextPart
from pydantic import BaseModel
from everos.config import load_settings
from everos.core.observability.logging import get_logger
@ -25,6 +32,212 @@ class LLMNotConfiguredError(RuntimeError):
_llm_client: LLMClient | None = None
_multimodal_client: LLMClient | None = None
_VLM_IMAGE_MIN_SIDE = 1024
_NO_THINKING_EXTRA_BODY_KEY = "chat_template_kwargs"
_NO_THINKING_PARAM = {"enable_thinking": False}
_IMAGE_VISUAL_MEMORY_PROMPT = """Describe this image for visual memory retrieval.
Output final Markdown directly; do not include reasoning.
Focus on:
1. Key visible objects and their names, brands, colors, labels, quantities.
2. Spatial relationships and relative positions: left/right/above/below/center,
foreground/background, nearby objects, and supporting surfaces.
3. Location-query facts, e.g. "the milk carton is center-left, to the right of
X and to the left of Y".
4. Important visible text, but extract only useful labels/interface text; do
not exhaustively OCR every key or menu item if that would crowd out object
locations.
Do NOT describe the parser, assistant, or ChatGPT as processing the image.
If "ChatGPT" is visible, list it only as visible interface text.
"""
class _NoThinkingRequestDefaultsClient:
"""Inject default no-thinking request params for OpenAI-compatible servers."""
def __init__(self, inner: LLMClient) -> None:
self._inner = inner
async def chat(
self,
messages: list[ChatMessage],
*,
model: str | None = None,
temperature: float | None = None,
max_tokens: int | None = None,
response_format: type[BaseModel] | None = None,
**extra: Any,
) -> ChatResponse:
return await self._inner.chat(
messages,
model=model,
temperature=temperature,
max_tokens=max_tokens,
response_format=response_format,
**_with_no_thinking_defaults(extra),
)
class _MultimodalImageDetailCompatClient:
"""Patch image parts for strict OpenAI-compatible gateways.
everalgo-core 0.2.0 serialises ``image_url.detail`` as ``None`` when the
field is unset. Some gateways reject that literal null and require one of
OpenAI's enum values. EverOS only uses this wrapper for multimodal parsing.
"""
def __init__(self, inner: LLMClient, *, resize_images_for_vlm: bool) -> None:
self._inner = inner
self._resize_images_for_vlm = resize_images_for_vlm
async def chat(
self,
messages: list[ChatMessage],
*,
model: str | None = None,
temperature: float | None = None,
max_tokens: int | None = None,
response_format: type[BaseModel] | None = None,
**extra: Any,
) -> ChatResponse:
return await self._inner.chat(
[
_with_multimodal_image_defaults(
m,
resize_images_for_vlm=self._resize_images_for_vlm,
)
for m in messages
],
model=model,
temperature=temperature,
max_tokens=max_tokens,
response_format=response_format,
**_with_no_thinking_defaults(extra),
)
def _with_no_thinking_defaults(extra: dict[str, Any]) -> dict[str, Any]:
"""Return request kwargs with no-thinking enabled unless caller overrides."""
patched = dict(extra)
extra_body = dict(patched.get("extra_body") or {})
chat_template_kwargs = dict(extra_body.get(_NO_THINKING_EXTRA_BODY_KEY) or {})
chat_template_kwargs.setdefault(
"enable_thinking", _NO_THINKING_PARAM["enable_thinking"]
)
extra_body[_NO_THINKING_EXTRA_BODY_KEY] = chat_template_kwargs
patched["extra_body"] = extra_body
return patched
def _with_multimodal_image_defaults(
message: ChatMessage, *, resize_images_for_vlm: bool = True
) -> ChatMessage:
"""Return a copy with stricter-gateway + visual-memory image defaults."""
content = message.content
if not isinstance(content, list):
return message
has_image = any(_is_image_part(part) for part in content)
instructions_added = False
changed = False
patched_parts: list[object] = []
for part in content:
patched = part
if isinstance(part, ImageUrlPart):
image_url_updates: dict[str, object] = {}
if part.image_url.detail is None:
image_url_updates["detail"] = "auto"
if resize_images_for_vlm:
resized_url = _resize_image_data_url(part.image_url.url)
if resized_url != part.image_url.url:
image_url_updates["url"] = resized_url
if image_url_updates:
image_url = part.image_url.model_copy(update=image_url_updates)
patched = part.model_copy(update={"image_url": image_url})
changed = True
if (
has_image
and not instructions_added
and isinstance(patched, TextPart)
and patched.text != _IMAGE_VISUAL_MEMORY_PROMPT
):
patched = patched.model_copy(
update={"text": _IMAGE_VISUAL_MEMORY_PROMPT}
)
instructions_added = True
changed = True
patched_parts.append(patched)
if not changed:
return message
return message.model_copy(update={"content": patched_parts})
def _is_image_part(part: object) -> bool:
return (
isinstance(part, ImageUrlPart)
and part.image_url.url.startswith("data:image/")
)
def _resize_image_data_url(url: str) -> str:
"""Resize base64 data-url images so the shorter side is 64 pixels."""
if not url.startswith("data:image/"):
return url
try:
header, encoded = url.split(",", 1)
except ValueError:
return url
if ";base64" not in header.lower():
return url
mime_type = header[5:].split(";", 1)[0].lower()
image_format = {
"image/jpeg": "JPEG",
"image/jpg": "JPEG",
"image/png": "PNG",
"image/webp": "WEBP",
}.get(mime_type)
if image_format is None:
return url
try:
from PIL import Image, ImageOps
raw = base64.b64decode(encoded, validate=True)
with Image.open(BytesIO(raw)) as image:
image = ImageOps.exif_transpose(image)
target_size = _image_size_with_min_side(
image.size, _VLM_IMAGE_MIN_SIDE
)
resized = image.resize(target_size, Image.Resampling.LANCZOS)
if image_format == "JPEG" and resized.mode not in ("RGB", "L"):
resized = resized.convert("RGB")
buffer = BytesIO()
save_kwargs: dict[str, object] = {"format": image_format}
if image_format == "JPEG":
save_kwargs["quality"] = 85
resized.save(buffer, **save_kwargs)
except (ImportError, ValueError, OSError, binascii.Error):
return url
resized_encoded = base64.b64encode(buffer.getvalue()).decode("ascii")
return f"{header},{resized_encoded}"
def _image_size_with_min_side(
size: tuple[int, int],
min_side: int,
) -> tuple[int, int]:
width, height = size
shortest = min(width, height)
if shortest <= 0:
return (max(1, width), max(1, height))
scale = min_side / shortest
return (max(1, round(width * scale)), max(1, round(height * scale)))
def get_llm_client() -> LLMClient:
@ -46,11 +259,14 @@ def get_llm_client() -> LLMClient:
raise LLMNotConfiguredError(
"LLM is required; set EVEROS_LLM__API_KEY + EVEROS_LLM__BASE_URL"
)
_llm_client = build_client(
LLMConfig(
model=llm_cfg.model,
api_key=api_key,
base_url=llm_cfg.base_url,
_llm_client = _NoThinkingRequestDefaultsClient(
build_client(
LLMConfig(
model=llm_cfg.model,
api_key=api_key,
base_url=llm_cfg.base_url,
timeout=llm_cfg.timeout_seconds,
)
)
)
logger.info("llm_client_built", model=llm_cfg.model)
@ -78,12 +294,16 @@ def get_multimodal_llm_client() -> LLMClient:
"Multimodal LLM is required for parsing; set "
"EVEROS_MULTIMODAL__API_KEY + EVEROS_MULTIMODAL__BASE_URL"
)
_multimodal_client = build_client(
LLMConfig(
model=cfg.model,
api_key=api_key,
base_url=cfg.base_url,
)
_multimodal_client = _MultimodalImageDetailCompatClient(
build_client(
LLMConfig(
model=cfg.model,
api_key=api_key,
base_url=cfg.base_url,
timeout=cfg.timeout_seconds,
)
),
resize_images_for_vlm=cfg.resize_images_for_vlm,
)
logger.info("multimodal_llm_client_built", model=cfg.model)
return _multimodal_client

View File

@ -42,4 +42,5 @@ def build_llm_provider(settings: LLMSettings) -> LLMClient:
model=settings.model,
api_key=settings.api_key.get_secret_value(),
base_url=settings.base_url,
timeout=settings.timeout_seconds,
)

View File

@ -39,7 +39,7 @@ from .protocol import RerankError, RerankResult
# Qwen3-Reranker chat template. The DeepInfra inference API treats the reranker
# as a yes/no generator, so the prompt scaffolding must be supplied client-side
# (verbatim mirror of the EverCore benchmark's reranker client). Without it the
# (verbatim mirror of the benchmark reranker client). Without it the
# model scores raw text off-template and returns uncalibrated relevance.
_QWEN3_PREFIX = (
"<|im_start|>system\n"

View File

@ -56,17 +56,21 @@ cache_size_kb = 2048
[llm]
# Provider-agnostic OpenAI-protocol client config. Override via env:
# EVEROS_LLM__MODEL, EVEROS_LLM__API_KEY, EVEROS_LLM__BASE_URL
# EVEROS_LLM__MODEL, EVEROS_LLM__API_KEY, EVEROS_LLM__BASE_URL, EVEROS_LLM__TIMEOUT_SECONDS
# Or via a ``.env`` file next to the project root (auto-loaded).
model = "gpt-4o-mini"
timeout_seconds = 180.0
# api_key = ""
# base_url = ""
[multimodal]
# Independent LLM for multimodal parsing (everalgo-parser); must accept
# image / pdf / audio image_url parts. Override via env:
# EVEROS_MULTIMODAL__MODEL, EVEROS_MULTIMODAL__API_KEY, EVEROS_MULTIMODAL__BASE_URL
# EVEROS_MULTIMODAL__MODEL, EVEROS_MULTIMODAL__API_KEY, EVEROS_MULTIMODAL__BASE_URL,
# EVEROS_MULTIMODAL__TIMEOUT_SECONDS, EVEROS_MULTIMODAL__RESIZE_IMAGES_FOR_VLM
model = "google/gemini-3-flash-preview"
timeout_seconds = 180.0
resize_images_for_vlm = true
max_concurrency = 4
# api_key = ""
# base_url = ""

View File

@ -121,11 +121,13 @@ class LLMSettings(BaseModel):
EVEROS_LLM__MODEL
EVEROS_LLM__API_KEY
EVEROS_LLM__BASE_URL
EVEROS_LLM__TIMEOUT_SECONDS
"""
model: str = "gpt-4o-mini"
api_key: SecretStr | None = None
base_url: str | None = None
timeout_seconds: float = Field(default=180.0, gt=0)
class MultimodalSettings(BaseModel):
@ -140,6 +142,8 @@ class MultimodalSettings(BaseModel):
EVEROS_MULTIMODAL__MODEL
EVEROS_MULTIMODAL__API_KEY
EVEROS_MULTIMODAL__BASE_URL
EVEROS_MULTIMODAL__TIMEOUT_SECONDS
EVEROS_MULTIMODAL__RESIZE_IMAGES_FOR_VLM
EVEROS_MULTIMODAL__MAX_CONCURRENCY
EVEROS_MULTIMODAL__FILE_URI_ALLOW_DIRS
EVEROS_MULTIMODAL__FILE_URI_MAX_BYTES
@ -148,6 +152,8 @@ class MultimodalSettings(BaseModel):
model: str = "google/gemini-3-flash-preview"
api_key: SecretStr | None = None
base_url: str | None = None
timeout_seconds: float = Field(default=180.0, gt=0)
resize_images_for_vlm: bool = True
max_concurrency: int = 4
# ``file://`` content-item support (read locally by EverOS, not everalgo).

View File

@ -180,4 +180,6 @@ def register(parent: typer.Typer) -> None:
typer.echo("Next steps:")
typer.echo(" 1. Edit the file and fill in the API keys (see comments inside).")
typer.echo(" 2. Run `everos server start`.")
typer.echo("Docs: https://github.com/evermind/everos/blob/master/QUICKSTART.md")
typer.echo(
"Docs: https://github.com/EverMind-AI/EverOS/blob/main/QUICKSTART.md"
)

View File

@ -47,6 +47,7 @@ class CascadeWatcher:
self._observer = Observer()
self._handler = _Handler(memory_root, loop)
self._started = False
self._observer_started = False
def start(self) -> None:
if self._started:
@ -54,18 +55,31 @@ class CascadeWatcher:
# The memory root is created lazily by other layers; watchdog
# rejects non-existent paths so we ensure it exists here.
self._memory_root.ensure()
self._observer.schedule(
self._handler, str(self._memory_root.root), recursive=True
)
self._observer.start()
watch_roots = _watch_roots(self._memory_root.root)
for root in watch_roots:
self._observer.schedule(self._handler, str(root), recursive=True)
if watch_roots:
self._observer.start()
self._observer_started = True
else:
logger.warning(
"cascade_watcher_no_user_visible_roots",
root=str(self._memory_root.root),
)
self._started = True
logger.info("cascade_watcher_started", root=str(self._memory_root.root))
logger.info(
"cascade_watcher_started",
root=str(self._memory_root.root),
watched_roots=[str(root) for root in watch_roots],
)
def stop(self) -> None:
if not self._started:
return
self._observer.stop()
self._observer.join(timeout=5)
if self._observer_started:
self._observer.stop()
self._observer.join(timeout=5)
self._observer_started = False
self._started = False
logger.info("cascade_watcher_stopped")
@ -163,6 +177,22 @@ def _relative_to_root(root: Path, raw: str) -> str | None:
return rel.as_posix()
def _watch_roots(root: Path) -> list[Path]:
"""Return user-visible top-level dirs to watch, excluding system dot dirs."""
try:
children = list(root.iterdir())
except OSError:
return []
return sorted(
(
child
for child in children
if child.is_dir() and not child.name.startswith(".")
),
key=lambda p: p.name,
)
def _safe_mtime(raw: str) -> float:
"""Return mtime in seconds, falling back to 0.0 on stat failure."""
try:

View File

@ -17,6 +17,11 @@ from everos.core.observability.logging import get_logger
logger = get_logger(__name__)
_IMAGE_VISUAL_FACTS_NOTE = (
"Context: image visual facts extracted from an uploaded image; "
"treat these as image content, not assistant actions."
)
def coerce_items(
content: str | list[dict[str, Any]] | list[Any],
@ -83,6 +88,8 @@ def _render_item(item: dict[str, Any]) -> str | None:
kind = str(item.get("type") or "file").upper()
name = item.get("name") or ""
tag = f"[{kind}: {name}]" if name else f"[{kind}]"
if kind == "IMAGE":
return f"{tag}\n{_IMAGE_VISUAL_FACTS_NOTE}\n{parsed}"
return f"{tag}\n{parsed}"

View File

@ -518,7 +518,7 @@ class SearchManager:
``atomic_fact`` table) for finer-grained semantic match — long
episodes whose single mean-pooled vector dilutes a specific topic
recover via the matching atomic fact's own embedding. Mirrors
EverOS/EverCore's MaxSim retrieval pattern.
the EverOS MaxSim retrieval pattern.
"""
vector = await self._embed_query(req.query)
if not vector:

View File

@ -33,6 +33,8 @@
EVEROS_LLM__MODEL=openai/gpt-4.1-mini
EVEROS_LLM__API_KEY=
EVEROS_LLM__BASE_URL=https://openrouter.ai/api/v1
# Per-request chat-completions timeout in seconds (default 180):
# EVEROS_LLM__TIMEOUT_SECONDS=180
# ─── Multimodal LLM (independent from [llm]; vision/audio capable) ────
@ -43,6 +45,11 @@ EVEROS_LLM__BASE_URL=https://openrouter.ai/api/v1
EVEROS_MULTIMODAL__MODEL=google/gemini-3-flash-preview
EVEROS_MULTIMODAL__API_KEY=
EVEROS_MULTIMODAL__BASE_URL=https://openrouter.ai/api/v1
# Per-request multimodal chat-completions timeout in seconds (default 180):
# EVEROS_MULTIMODAL__TIMEOUT_SECONDS=180
# Resize inline images to half width/height before sending them to the VLM
# (default true):
# EVEROS_MULTIMODAL__RESIZE_IMAGES_FOR_VLM=true
# Concurrency cap for parallel multimodal calls (default 4):
# EVEROS_MULTIMODAL__MAX_CONCURRENCY=4
#

View File

@ -2,20 +2,30 @@
from __future__ import annotations
import base64
import importlib
from io import BytesIO
import pytest
from everalgo.llm.types import (
ChatMessage,
ChatResponse,
ImageUrlInner,
ImageUrlPart,
TextPart,
)
from pydantic import SecretStr
from everos.component.llm import LLMNotConfiguredError
from everos.config import Settings
from everos.config.settings import LLMSettings
from everos.config.settings import LLMSettings, MultimodalSettings
_client_mod = importlib.import_module("everos.component.llm.client")
def _reset_singleton(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(_client_mod, "_llm_client", None, raising=False)
monkeypatch.setattr(_client_mod, "_multimodal_client", None, raising=False)
def _patch_settings(
@ -23,6 +33,7 @@ def _patch_settings(
*,
api_key: str | None,
base_url: str | None,
timeout_seconds: float | None = None,
) -> None:
"""Stub the ``load_settings`` reference bound inside the client module."""
cfg = Settings(
@ -30,11 +41,86 @@ def _patch_settings(
model="gpt-4o-mini",
api_key=SecretStr(api_key) if api_key is not None else None,
base_url=base_url,
**(
{"timeout_seconds": timeout_seconds}
if timeout_seconds is not None
else {}
),
)
)
monkeypatch.setattr(_client_mod, "load_settings", lambda: cfg)
def _patch_multimodal_settings(
monkeypatch: pytest.MonkeyPatch,
*,
api_key: str | None,
base_url: str | None,
timeout_seconds: float | None = None,
resize_images_for_vlm: bool | None = None,
) -> None:
cfg = Settings(
multimodal=MultimodalSettings(
model="vision-model",
api_key=SecretStr(api_key) if api_key is not None else None,
base_url=base_url,
**(
{"timeout_seconds": timeout_seconds}
if timeout_seconds is not None
else {}
),
**(
{"resize_images_for_vlm": resize_images_for_vlm}
if resize_images_for_vlm is not None
else {}
),
)
)
monkeypatch.setattr(_client_mod, "load_settings", lambda: cfg)
class _CapturingLLM:
def __init__(self) -> None:
self.messages: list[ChatMessage] | None = None
self.kwargs: dict[str, object] | None = None
async def chat(
self,
messages: list[ChatMessage],
**kwargs: object,
) -> ChatResponse:
self.messages = messages
self.kwargs = kwargs
return ChatResponse(content="ok", model="fake")
def _assert_no_thinking_param(kwargs: dict[str, object] | None) -> None:
assert kwargs is not None
extra_body = kwargs.get("extra_body")
assert isinstance(extra_body, dict)
chat_template_kwargs = extra_body.get("chat_template_kwargs")
assert isinstance(chat_template_kwargs, dict)
assert chat_template_kwargs["enable_thinking"] is False
def _png_data_url(size: tuple[int, int]) -> str:
from PIL import Image
image = Image.new("RGB", size, color=(255, 0, 0))
buffer = BytesIO()
image.save(buffer, format="PNG")
encoded = base64.b64encode(buffer.getvalue()).decode("ascii")
return f"data:image/png;base64,{encoded}"
def _data_url_image_size(data_url: str) -> tuple[int, int]:
from PIL import Image
_, encoded = data_url.split(",", 1)
with Image.open(BytesIO(base64.b64decode(encoded))) as image:
return image.size
def test_raises_when_api_key_missing(monkeypatch: pytest.MonkeyPatch) -> None:
_reset_singleton(monkeypatch)
_patch_settings(monkeypatch, api_key=None, base_url="https://example.test")
@ -60,5 +146,295 @@ def test_returns_singleton_when_configured(monkeypatch: pytest.MonkeyPatch) -> N
first = _client_mod.get_llm_client()
second = _client_mod.get_llm_client()
assert first is sentinel
assert first is second
assert first._inner is sentinel
@pytest.mark.asyncio
async def test_llm_client_defaults_to_no_thinking_param(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_settings(monkeypatch, api_key="sk-test", base_url="https://example.test")
captured = _CapturingLLM()
monkeypatch.setattr(_client_mod, "build_client", lambda cfg: captured)
client = _client_mod.get_llm_client()
await client.chat([ChatMessage(role="user", content="hello")])
_assert_no_thinking_param(captured.kwargs)
def test_llm_client_passes_configured_timeout(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_settings(
monkeypatch,
api_key="sk-test",
base_url="https://example.test",
timeout_seconds=180.0,
)
captured_configs = []
sentinel = object()
def capture_build_client(cfg):
captured_configs.append(cfg)
return sentinel
monkeypatch.setattr(_client_mod, "build_client", capture_build_client)
client = _client_mod.get_llm_client()
assert client._inner is sentinel
assert captured_configs[0].timeout == 180.0
def test_multimodal_client_passes_configured_timeout(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_multimodal_settings(
monkeypatch,
api_key="sk-test",
base_url="https://example.test",
timeout_seconds=240.0,
)
captured_configs = []
sentinel = _CapturingLLM()
def capture_build_client(cfg):
captured_configs.append(cfg)
return sentinel
monkeypatch.setattr(_client_mod, "build_client", capture_build_client)
_client_mod.get_multimodal_llm_client()
assert captured_configs[0].timeout == 240.0
@pytest.mark.asyncio
async def test_multimodal_client_sets_default_image_detail(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_multimodal_settings(
monkeypatch,
api_key="sk-test",
base_url="https://example.test",
)
captured = _CapturingLLM()
monkeypatch.setattr(_client_mod, "build_client", lambda cfg: captured)
client = _client_mod.get_multimodal_llm_client()
original = ChatMessage(
role="user",
content=[
TextPart(text="describe"),
ImageUrlPart(
image_url=ImageUrlInner(url="data:image/jpeg;base64,abcd")
),
],
)
await client.chat([original], max_tokens=10)
assert captured.messages is not None
sent_content = captured.messages[0].content
assert isinstance(sent_content, list)
sent_image = sent_content[1]
assert isinstance(sent_image, ImageUrlPart)
assert sent_image.image_url.detail == "auto"
original_content = original.content
assert isinstance(original_content, list)
original_image = original_content[1]
assert isinstance(original_image, ImageUrlPart)
assert original_image.image_url.detail is None
@pytest.mark.asyncio
async def test_multimodal_client_adds_visual_memory_instructions(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_multimodal_settings(
monkeypatch,
api_key="sk-test",
base_url="https://example.test",
)
captured = _CapturingLLM()
monkeypatch.setattr(_client_mod, "build_client", lambda cfg: captured)
client = _client_mod.get_multimodal_llm_client()
original = ChatMessage(
role="user",
content=[
TextPart(text="Read this image and return its content."),
ImageUrlPart(
image_url=ImageUrlInner(url="data:image/jpeg;base64,abcd")
),
],
)
await client.chat([original], max_tokens=10)
assert captured.messages is not None
sent_content = captured.messages[0].content
assert isinstance(sent_content, list)
sent_text = sent_content[0]
assert isinstance(sent_text, TextPart)
sent_text_lower = sent_text.text.lower()
assert "spatial relationships" in sent_text_lower
assert "relative positions" in sent_text_lower
assert "Do NOT describe the parser, assistant, or ChatGPT" in sent_text.text
original_content = original.content
assert isinstance(original_content, list)
original_text = original_content[0]
assert isinstance(original_text, TextPart)
assert "spatial relationships" not in original_text.text
@pytest.mark.asyncio
async def test_multimodal_client_defaults_to_no_thinking_param(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_multimodal_settings(
monkeypatch,
api_key="sk-test",
base_url="https://example.test",
)
captured = _CapturingLLM()
monkeypatch.setattr(_client_mod, "build_client", lambda cfg: captured)
client = _client_mod.get_multimodal_llm_client()
original = ChatMessage(
role="user",
content=[
TextPart(text="Read this image and return its content."),
ImageUrlPart(
image_url=ImageUrlInner(url="data:image/jpeg;base64,abcd")
),
],
)
await client.chat(
[original],
max_tokens=10,
extra_body={"provider": {"only": ["test"]}},
)
_assert_no_thinking_param(captured.kwargs)
assert captured.kwargs is not None
extra_body = captured.kwargs["extra_body"]
assert isinstance(extra_body, dict)
assert extra_body["provider"] == {"only": ["test"]}
assert captured.messages is not None
sent_content = captured.messages[0].content
assert isinstance(sent_content, list)
sent_text = sent_content[0]
assert isinstance(sent_text, TextPart)
assert "/no_think" not in sent_text.text
@pytest.mark.asyncio
async def test_multimodal_client_resizes_landscape_image_to_64_min_side_by_default(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_multimodal_settings(
monkeypatch,
api_key="sk-test",
base_url="https://example.test",
)
captured = _CapturingLLM()
monkeypatch.setattr(_client_mod, "build_client", lambda cfg: captured)
image_url = _png_data_url((640, 480))
client = _client_mod.get_multimodal_llm_client()
original = ChatMessage(
role="user",
content=[
TextPart(text="describe"),
ImageUrlPart(image_url=ImageUrlInner(url=image_url)),
],
)
await client.chat([original], max_tokens=10)
assert captured.messages is not None
sent_content = captured.messages[0].content
assert isinstance(sent_content, list)
sent_image = sent_content[1]
assert isinstance(sent_image, ImageUrlPart)
assert _data_url_image_size(sent_image.image_url.url) == (85, 64)
assert _data_url_image_size(image_url) == (640, 480)
@pytest.mark.asyncio
async def test_multimodal_client_resizes_portrait_image_to_64_min_side_by_default(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_multimodal_settings(
monkeypatch,
api_key="sk-test",
base_url="https://example.test",
)
captured = _CapturingLLM()
monkeypatch.setattr(_client_mod, "build_client", lambda cfg: captured)
image_url = _png_data_url((480, 640))
client = _client_mod.get_multimodal_llm_client()
original = ChatMessage(
role="user",
content=[
TextPart(text="describe"),
ImageUrlPart(image_url=ImageUrlInner(url=image_url)),
],
)
await client.chat([original], max_tokens=10)
assert captured.messages is not None
sent_content = captured.messages[0].content
assert isinstance(sent_content, list)
sent_image = sent_content[1]
assert isinstance(sent_image, ImageUrlPart)
assert _data_url_image_size(sent_image.image_url.url) == (64, 85)
assert _data_url_image_size(image_url) == (480, 640)
@pytest.mark.asyncio
async def test_multimodal_client_keeps_image_when_resize_disabled(
monkeypatch: pytest.MonkeyPatch,
) -> None:
_reset_singleton(monkeypatch)
_patch_multimodal_settings(
monkeypatch,
api_key="sk-test",
base_url="https://example.test",
resize_images_for_vlm=False,
)
captured = _CapturingLLM()
monkeypatch.setattr(_client_mod, "build_client", lambda cfg: captured)
image_url = _png_data_url((640, 480))
client = _client_mod.get_multimodal_llm_client()
original = ChatMessage(
role="user",
content=[
TextPart(text="describe"),
ImageUrlPart(image_url=ImageUrlInner(url=image_url)),
],
)
await client.chat([original], max_tokens=10)
assert captured.messages is not None
sent_content = captured.messages[0].content
assert isinstance(sent_content, list)
sent_image = sent_content[1]
assert isinstance(sent_image, ImageUrlPart)
assert sent_image.image_url.url == image_url

View File

@ -6,6 +6,7 @@ import pytest
from pydantic import SecretStr
from everos.component.llm import build_llm_provider
from everos.component.llm import factory as factory_mod
from everos.component.llm.openai_provider import OpenAIProvider
from everos.config.settings import LLMSettings
@ -26,3 +27,23 @@ def test_builds_openai_provider() -> None:
s = LLMSettings(model="m", api_key=SecretStr("k"), base_url="https://x")
p = build_llm_provider(s)
assert isinstance(p, OpenAIProvider)
def test_passes_configured_timeout(monkeypatch: pytest.MonkeyPatch) -> None:
captured_kwargs = {}
sentinel = object()
def capture_provider(**kwargs):
captured_kwargs.update(kwargs)
return sentinel
monkeypatch.setattr(factory_mod, "OpenAIProvider", capture_provider)
s = LLMSettings(
model="m",
api_key=SecretStr("k"),
base_url="https://x",
timeout_seconds=240.0,
)
assert build_llm_provider(s) is sentinel
assert captured_kwargs["timeout"] == 240.0

View File

@ -105,6 +105,9 @@ def test_embedding_rerank_defaults() -> None:
assert s.embedding.model is None
assert s.embedding.api_key is None
assert s.embedding.base_url is None
assert s.llm.timeout_seconds == 180.0
assert s.multimodal.timeout_seconds == 180.0
assert s.multimodal.resize_images_for_vlm is True
# Runtime knobs come from default.toml.
assert s.embedding.timeout_seconds == 30.0
assert s.embedding.max_retries == 3
@ -126,6 +129,16 @@ def test_embedding_env_overrides(monkeypatch: pytest.MonkeyPatch) -> None:
assert s.embedding.batch_size == 32
def test_llm_timeout_env_overrides(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("EVEROS_LLM__TIMEOUT_SECONDS", "240")
monkeypatch.setenv("EVEROS_MULTIMODAL__TIMEOUT_SECONDS", "300")
monkeypatch.setenv("EVEROS_MULTIMODAL__RESIZE_IMAGES_FOR_VLM", "false")
s = Settings()
assert s.llm.timeout_seconds == 240.0
assert s.multimodal.timeout_seconds == 300.0
assert s.multimodal.resize_images_for_vlm is False
def test_rerank_env_overrides(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("EVEROS_RERANK__MODEL", "BAAI/bge-reranker-v2-m3")
monkeypatch.setenv("EVEROS_RERANK__MAX_CONCURRENT", "8")

View File

@ -41,6 +41,9 @@ def test_default_writes_dotenv_in_cwd(runner: CliRunner, in_tmp: Path) -> None:
assert written.exists()
assert written.stat().st_size > 0
assert "EVEROS_LLM__API_KEY" in written.read_text()
assert "https://github.com/EverMind-AI/EverOS/blob/main/QUICKSTART.md" in (
result.output
)
def test_default_file_permissions_are_0600(runner: CliRunner, in_tmp: Path) -> None:

View File

@ -13,6 +13,7 @@ from pathlib import Path
import pytest
from everos.core.persistence import MemoryRoot
from everos.memory.cascade import scanner as scanner_module
from everos.memory.cascade.scanner import CascadeScanner, _collect_scan_inputs
@ -112,16 +113,26 @@ async def test_run_loop_swallows_scan_exception(
scanner = CascadeScanner(mr, scan_interval_seconds=0.05)
call_count = {"n": 0}
second_scan = asyncio.Event()
logged_errors: list[str] = []
async def fake_scan() -> list: # type: ignore[type-arg]
call_count["n"] += 1
if call_count["n"] == 1:
raise RuntimeError("simulated scanner failure")
second_scan.set()
return []
def fake_exception(_event: str, *, error: str) -> None:
logged_errors.append(error)
monkeypatch.setattr(scanner, "scan_once", fake_scan)
monkeypatch.setattr(scanner_module.logger, "exception", fake_exception)
await scanner.start()
# Let the loop iterate at least twice (interval is 50ms).
await asyncio.sleep(0.2)
await scanner.stop()
try:
await asyncio.wait_for(second_scan.wait(), timeout=1.0)
finally:
await scanner.stop()
assert logged_errors == ["simulated scanner failure"]
assert call_count["n"] >= 2 # second call ran despite first throwing

View File

@ -9,7 +9,11 @@ from __future__ import annotations
from pathlib import Path
from everos.memory.cascade.watcher import _relative_to_root, _safe_mtime
from everos.memory.cascade.watcher import (
_relative_to_root,
_safe_mtime,
_watch_roots,
)
def test_relative_to_root_within(tmp_path: Path) -> None:
@ -34,3 +38,14 @@ def test_safe_mtime_existing_path_returns_positive(tmp_path: Path) -> None:
f = tmp_path / "f.md"
f.write_text("ok")
assert _safe_mtime(str(f)) > 0
def test_watch_roots_excludes_system_dot_dirs(tmp_path: Path) -> None:
(tmp_path / ".index" / "lancedb" / "episode").mkdir(parents=True)
(tmp_path / ".tmp").mkdir()
(tmp_path / "default_app" / "default_project" / "users").mkdir(parents=True)
(tmp_path / "default_app" / "default_project" / "agents").mkdir()
roots = _watch_roots(tmp_path)
assert roots == [tmp_path / "default_app"]

View File

@ -21,7 +21,10 @@ def test_derive_text_renders_parsed_nontext_as_tag() -> None:
]
text, non_text = derive_text(items)
assert "[IMAGE: p.png]\nOCR TEXT" in text
assert "[IMAGE: p.png]" in text
assert "image visual facts" in text
assert "not assistant actions" in text
assert text.index("image visual facts") < text.index("OCR TEXT")
assert text.startswith("before")
assert text.endswith("after")
assert non_text == 0

View File

@ -163,7 +163,7 @@ async def test_merges_into_existing_cluster_when_algo_matches() -> None:
preview=["earlier intent"],
members=["ac_20260517_0000"],
)
# Simulate evercore _merge: id passes through from existing, members appended.
# Simulate merge behavior: id passes through from existing, members appended.
merged_cluster = AlgoCluster(
id="cl_existing0001",
centroid=np.array([0.17] * 1024, dtype=np.float32),

View File

@ -0,0 +1,59 @@
"""Self-tests for ``scripts/check_deprecated_names.py``."""
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
_REPO_ROOT = Path(__file__).resolve().parents[3]
_CHECKER_PATH = _REPO_ROOT / "scripts" / "check_deprecated_names.py"
def _load_checker():
assert _CHECKER_PATH.exists(), "deprecated-name checker should exist"
spec = importlib.util.spec_from_file_location(
"_deprecated_name_checker", _CHECKER_PATH
)
assert spec and spec.loader
mod = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = mod
spec.loader.exec_module(mod)
return mod
def test_clean_text_is_allowed() -> None:
checker = _load_checker()
violations = checker.find_violations(
[("README.md", "EverOS is the public project name.\n")]
)
assert violations == []
def test_deprecated_name_variants_are_blocked() -> None:
checker = _load_checker()
compact_name = "Ever" + "Core"
spaced_name = "ever" + " core"
hyphenated_name = "ever" + "-core"
violations = checker.find_violations(
[
("README.md", f"{compact_name} should not appear.\n"),
("docs/example.md", f"{spaced_name} should not appear.\n"),
("src/example.py", f"{hyphenated_name} should not appear.\n"),
]
)
assert [(violation.path, violation.line_number) for violation in violations] == [
("README.md", 1),
("docs/example.md", 1),
("src/example.py", 1),
]
def test_real_repo_has_no_deprecated_names() -> None:
checker = _load_checker()
assert checker.main() == 0

View File

@ -0,0 +1,56 @@
"""Self-tests for ``scripts/check_pr_title.py``."""
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
_REPO_ROOT = Path(__file__).resolve().parents[3]
_CHECKER_PATH = _REPO_ROOT / "scripts" / "check_pr_title.py"
def _load_checker():
assert _CHECKER_PATH.exists(), "PR title checker should exist"
spec = importlib.util.spec_from_file_location("_pr_title_checker", _CHECKER_PATH)
assert spec and spec.loader
mod = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = mod
spec.loader.exec_module(mod)
return mod
def test_conventional_pr_title_is_allowed() -> None:
checker = _load_checker()
assert checker.validate_title("docs(readme): polish launch highlights") == []
def test_bracketed_codex_pr_title_is_blocked() -> None:
checker = _load_checker()
failures = checker.validate_title("[codex] simplify README launch highlights")
assert len(failures) == 1
assert "invalid PR title" in failures[0]
assert "expected: <type>[(scope)][!]: <description>" in failures[0]
def test_long_pr_title_is_blocked() -> None:
checker = _load_checker()
title = (
"docs(readme): polish launch highlights and banner with a title that is "
"too long"
)
failures = checker.validate_title(title)
assert len(failures) == 1
assert "max is 72" in failures[0]
def test_main_reads_pr_title_environment(monkeypatch) -> None:
checker = _load_checker()
monkeypatch.setenv("PR_TITLE", "docs(readme): polish launch highlights")
assert checker.main([]) == 0

View File

@ -0,0 +1,80 @@
"""Self-tests for ``scripts/check_repo_assets.py``."""
from __future__ import annotations
import importlib.util
import sys
from pathlib import Path
_REPO_ROOT = Path(__file__).resolve().parents[3]
_CHECKER_PATH = _REPO_ROOT / "scripts" / "check_repo_assets.py"
def _load_checker():
assert _CHECKER_PATH.exists(), "repo asset checker should exist"
spec = importlib.util.spec_from_file_location("_repo_asset_checker", _CHECKER_PATH)
assert spec and spec.loader
mod = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = mod
spec.loader.exec_module(mod)
return mod
def test_clean_source_and_docs_paths_are_allowed() -> None:
checker = _load_checker()
violations = checker.find_violations(
[
"README.md",
"docs/engineering.md",
"src/everos/__init__.py",
"use-cases/claude-code-plugin/dashboard/dashboard.html",
]
)
assert violations == []
def test_image_extensions_are_blocked() -> None:
checker = _load_checker()
violations = checker.find_violations(["docs/banner.png", "icons/logo.svg"])
assert [violation.path for violation in violations] == [
"docs/banner.png",
"icons/logo.svg",
]
assert {violation.reason for violation in violations} == {"image file"}
def test_video_extensions_are_blocked() -> None:
checker = _load_checker()
violations = checker.find_violations(["demo/launch.mp4", "docs/clip.webm"])
assert [violation.path for violation in violations] == [
"demo/launch.mp4",
"docs/clip.webm",
]
assert {violation.reason for violation in violations} == {"video file"}
def test_asset_and_media_directories_are_blocked() -> None:
checker = _load_checker()
violations = checker.find_violations(
[
"assets/banner.txt",
"docs/images/diagram.txt",
"use-cases/example/media/story.md",
"use-cases/example/videos/walkthrough.md",
]
)
assert [violation.path for violation in violations] == [
"assets/banner.txt",
"docs/images/diagram.txt",
"use-cases/example/media/story.md",
"use-cases/example/videos/walkthrough.md",
]
assert {violation.reason for violation in violations} == {"asset/media directory"}

View File

@ -92,7 +92,7 @@ Earth Online is a memory-aware productivity game that turns everyday planning in
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/57d8cda7-35a5-4561-b794-5520dffc917b)](https://github.com/golutra/golutra)
[![banner-gif](https://github.com/user-attachments/assets/57d8cda7-35a5-4561-b794-5520dffc917b)](https://github.com/golutra/golutra)
#### Multi-Agent Orchestration Platform
@ -116,7 +116,7 @@ Record, visualize, and explore your tasting journey through an immersive 3D star
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/93ac2a68-4f18-4fcb-8d87-80aeb00a9d7c)](https://github.com/kellyvv/OpenHer)
[![banner-gif](https://github.com/user-attachments/assets/93ac2a68-4f18-4fcb-8d87-80aeb00a9d7c)](https://github.com/kellyvv/OpenHer)
#### EverOS Open Her
@ -141,7 +141,7 @@ Ruminer brings persistent memory to a browser agent so it can carry personal con
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/c258a6c4-fe70-497a-98d1-3dade4a932f6)](https://github.com/nanxingw/EverMem)
[![banner-gif](https://github.com/user-attachments/assets/c258a6c4-fe70-497a-98d1-3dade4a932f6)](https://github.com/nanxingw/EverMem)
#### EverMem Sync with EverOS
@ -166,7 +166,7 @@ MCO equips your primary agent with an agent team that can work together to solve
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/314c9126-8e08-4688-bbbb-8555ad58cf67)](https://github.com/onenewborn/StudyBuddy-public)
[![banner-gif](https://github.com/user-attachments/assets/314c9126-8e08-4688-bbbb-8555ad58cf67)](https://github.com/onenewborn/StudyBuddy-public)
#### Study Buddy with Self-Evolving Memory
@ -191,7 +191,7 @@ Empowering individuals with advanced memory support and daily assistance.
</td>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/e2428df3-ea11-4e88-8f9c-dad437dd8998)](https://github.com/AlexL1024/NeuralConnect)
[![banner-gif](https://github.com/user-attachments/assets/e2428df3-ea11-4e88-8f9c-dad437dd8998)](https://github.com/AlexL1024/NeuralConnect)
#### Memory-Driven Multi-Agent NPC Experience
@ -229,13 +229,13 @@ A context-native AI wearable that listens to everyday life and converts conversa
<tr>
<td width="50%" valign="top">
[![banner-gif](https://github.com/user-attachments/assets/df9677ec-386f-4c56-a428-08bca25c54dc)](https://github.com/EverMind-AI/EverOS/tree/0f49826ba0f9a94e1974c97614a46a68e0a08b52/evermemos-openclaw-plugin)
[![banner-gif](https://github.com/user-attachments/assets/df9677ec-386f-4c56-a428-08bca25c54dc)](../docs/migration-to-1.0.0.md)
#### OpenClaw Agent Memory
#### Legacy OpenClaw Agent Memory
A 24/7 agent workflow with continuous learning memory across sessions.
Archived pre-1.0.0 plugin reference. New integrations should use the EverOS 1.0.0 API.
[Plugin](https://github.com/EverMind-AI/EverOS/tree/0f49826ba0f9a94e1974c97614a46a68e0a08b52/evermemos-openclaw-plugin)
[Learn more](../docs/migration-to-1.0.0.md)
</td>
<td width="50%" valign="top">

View File

@ -2,6 +2,13 @@
Persistent memory for Claude Code. Automatically saves and recalls context from past coding sessions.
> Compatibility note: this folder documents a legacy EverMem Cloud
> plugin. It still uses the old cloud `/api/v1/memories/*` routes and
> should not be treated as the canonical local EverOS 1.0.0 OSS API.
> New integrations should follow
> [EverOS 1.0.0 migration notes](../../docs/migration-to-1.0.0.md) and
> [the API reference](../../docs/api.md).
![Memory Hub Screenshot](https://github.com/user-attachments/assets/af37c1f6-7ba5-430c-b99d-2a7e7eac618f)
## Features
@ -889,7 +896,7 @@ function getGroupsForKey(keyId) {
// Proxy forwards to upstream API with Authorization header
```
### Dashboard (`assets/dashboard.html`)
### Dashboard (`dashboard/dashboard.html`)
**Data Loading Flow:**
@ -1059,7 +1066,7 @@ evermem-plugin/
│ ├── config.js # Configuration utilities
│ ├── debug.js # Shared debug logging utility
│ └── groups-store.js # Local groups persistence
├── assets/
├── dashboard/
│ └── dashboard.html # Memory Hub dashboard
├── server/
│ └── proxy.js # Local proxy server for dashboard

View File

@ -58,4 +58,4 @@ export function debug(...args) {
*/
export function isDebugEnabled() {
return DEBUG;
}
}

View File

@ -9,8 +9,7 @@ You have access to memory tools that can recall context from the user's past cod
## Available Tools
- **search_memories**: Search past conversations using semantic + keyword matching
- **get_memory**: Retrieve full details of a specific memory by ID
- **evermem_search**: Search past conversations using semantic + keyword matching. Params: `query` (required), `limit` (default 10, max 20)
## When to Use Memory Search

View File

@ -1,6 +1,6 @@
# EverMem Story Memory Demo
> Built on [EverCore](https://github.com/EverMind-AI/EverOS/) - Open-source AI memory infrastructure
> Built on [EverOS](https://github.com/EverMind-AI/EverOS/) - Open-source AI memory infrastructure
A demonstration web application showcasing [EverMem](https://evermind.ai)'s AI memory infrastructure through an interactive Q&A experience with "A Game of Thrones" (Book 1).
@ -30,7 +30,7 @@ Ask questions about the book and watch two AI responses stream side-by-side: one
- [Bun](https://bun.sh/) (latest version)
- OpenAI API key (or OpenRouter API key)
- EverMind Cloud API key (apply at [EverCore Cloud](https://console.evermind.ai/))
- EverMind Cloud API key (apply at [EverMind Cloud](https://console.evermind.ai/))
### Installation

View File

@ -1,7 +1,7 @@
import express from 'express';
import cors from 'cors';
import { MockMemoryService } from './services/MockMemoryService.js';
import { EverCoreService } from './services/EverMemOSService.js';
import { EverOSService } from './services/EverMemOSService.js';
import { OpenAIService } from './services/OpenAIService.js';
import { createChatRouter } from './routes/chat.js';
import { createHealthRouter } from './routes/health.js';
@ -24,7 +24,7 @@ if (!OPENAI_API_KEY) {
// Initialize services
const memoryService = USE_EVERMEMOS
? new EverCoreService({
? new EverOSService({
baseUrl: EVERMEMOS_URL,
apiKey: EVERMEMOS_API_KEY || undefined,
groupId: EVERMEMOS_GROUP_ID,
@ -33,9 +33,9 @@ const memoryService = USE_EVERMEMOS
const openaiService = new OpenAIService(OPENAI_API_KEY, OPENAI_MODEL);
const isCloudMode = USE_EVERMEMOS && !!EVERMEMOS_API_KEY;
logger.info('Server', `Memory service: ${USE_EVERMEMOS ? (isCloudMode ? 'EverMind Cloud' : 'EverCore (local)') : 'Mock'}`);
logger.info('Server', `Memory service: ${USE_EVERMEMOS ? (isCloudMode ? 'EverMind Cloud' : 'EverOS (local)') : 'Mock'}`);
if (USE_EVERMEMOS) {
logger.info('Server', `EverCore URL: ${EVERMEMOS_URL}`);
logger.info('Server', `EverOS URL: ${EVERMEMOS_URL}`);
if (isCloudMode) {
logger.info('Server', `EverMind Cloud API Key: ${EVERMEMOS_API_KEY.slice(0, 8)}...`);
}

View File

@ -1,6 +1,6 @@
import type { IMemoryService, Memory } from './IMemoryService';
interface EverCoreMemoryItem {
interface EverOSMemoryItem {
memory_type: string;
summary: string | null;
subject?: string; // Concise title/headline
@ -38,12 +38,12 @@ interface ProfileSearchItem {
score: number;
}
interface EverCoreSearchResponse {
interface EverOSSearchResponse {
status: string;
message?: string;
result: {
profiles: ProfileSearchItem[];
memories: EverCoreMemoryItem[];
memories: EverOSMemoryItem[];
total_count: number;
scores: number[];
has_more: boolean;
@ -53,7 +53,7 @@ interface EverCoreSearchResponse {
};
}
interface EverCoreHealthResponse {
interface EverOSHealthResponse {
status: string;
[key: string]: unknown;
}
@ -70,25 +70,25 @@ const BOOK_TITLES: Record<string, string> = {
};
/**
* Configuration for EverCore/EverMind Cloud service
* Configuration for EverOS/EverMind Cloud service
*/
interface EverCoreConfig {
interface EverOSConfig {
baseUrl: string;
apiKey?: string; // Required for cloud API
groupId?: string; // Group ID for search (default: 'asoiaf')
}
/**
* EverCore service implementation for memory retrieval
* Supports both local EverCore and EverMind Cloud API
* EverOS service implementation for memory retrieval
* Supports both local EverOS and EverMind Cloud API
*/
export class EverCoreService implements IMemoryService {
export class EverOSService implements IMemoryService {
private baseUrl: string;
private apiKey?: string;
private groupId: string;
private isCloudMode: boolean;
constructor(config: string | EverCoreConfig) {
constructor(config: string | EverOSConfig) {
if (typeof config === 'string') {
// Legacy: just a URL string (local mode)
this.baseUrl = config.replace(/\/$/, '');
@ -104,7 +104,7 @@ export class EverCoreService implements IMemoryService {
}
/**
* Retrieve relevant memories for a query using EverCore search
* Retrieve relevant memories for a query using EverOS search
*/
async retrieveMemories(query: string, limit: number = 5): Promise<Memory[]> {
try {
@ -136,20 +136,20 @@ export class EverCoreService implements IMemoryService {
});
if (!response.ok) {
console.error(`EverCore search failed: HTTP ${response.status}`);
console.error(`EverOS search failed: HTTP ${response.status}`);
return [];
}
const data = await response.json() as EverCoreSearchResponse;
const data = await response.json() as EverOSSearchResponse;
return this.mapSearchResultsToMemories(data);
} catch (error) {
console.error('Error retrieving memories from EverCore:', error);
console.error('Error retrieving memories from EverOS:', error);
return []; // Graceful degradation
}
}
/**
* Check if EverCore service is available
* Check if EverOS service is available
*/
async isAvailable(): Promise<boolean> {
try {
@ -168,19 +168,19 @@ export class EverCoreService implements IMemoryService {
return false;
}
const data = await response.json() as EverCoreHealthResponse;
const data = await response.json() as EverOSHealthResponse;
// Cloud API returns "ok" status, local returns "healthy"
return data.status === 'healthy' || data.status === 'ok';
} catch (error) {
console.warn('EverCore health check failed:', error);
console.warn('EverOS health check failed:', error);
return false;
}
}
/**
* Map EverCore search results to our Memory interface
* Map EverOS search results to our Memory interface
*/
private mapSearchResultsToMemories(data: EverCoreSearchResponse): Memory[] {
private mapSearchResultsToMemories(data: EverOSSearchResponse): Memory[] {
const memories: Memory[] = [];
const result = data.result;
@ -206,10 +206,10 @@ export class EverCoreService implements IMemoryService {
}
/**
* Map a single EverCore memory item to our Memory interface
* Map a single EverOS memory item to our Memory interface
*/
private mapMemoryItem(
item: EverCoreMemoryItem,
item: EverOSMemoryItem,
score: number,
originalContents: OriginalDataItem[] = []
): Memory | null {

View File

@ -1,4 +1,4 @@
# OpenHer × EverCore Use Case
# OpenHer × EverOS Use Case
# Copy this file to .env and fill in your values
# ─── LLM Provider (pick one) ───
@ -17,12 +17,12 @@ GEMINI_API_KEY=your_gemini_api_key_here
# OpenAI (alternative)
# OPENAI_API_KEY=your_openai_api_key_here
# ─── EverCore Long-Term Memory ───
# ─── EverOS Long-Term Memory ───
# Option A: EverCore Cloud
# Option A: EverMind Cloud
EVERMEMOS_BASE_URL=https://api.evermind.ai/v1
EVERMEMOS_API_KEY=your_evermemos_api_key_here
# Option B: Self-Hosted EverCore
# cd vendor/EverCore && docker compose up -d && uv run python src/run.py
# Option B: Self-Hosted EverOS
# cd vendor/EverOS && docker compose up -d && uv run python src/run.py
# EVERMEMOS_BASE_URL=http://localhost:1995/api/v1

View File

@ -1,10 +1,10 @@
# OpenHer — Teaching AI to Remember Who You Are
Built on [EverCore](https://github.com/EverMind-AI/EverOS/tree/main/methods/EverCore) — Open-source AI memory infrastructure
Built on [EverOS](https://github.com/EverMind-AI/EverOS) — open-source AI memory infrastructure
**OpenHer** doesn't build chatbots. It doesn't build AI assistants. It builds **AI Beings** — entities with personality, emotion, and memory that *feel*, *remember*, and *grow* through every interaction.
**EverCore** is her long-term memory — the part that lets her carry your story across sessions, remember who you are, what you've talked about, and how your relationship has evolved.
**EverOS** is her long-term memory — the part that lets her carry your story across sessions, remember who you are, what you've talked about, and how your relationship has evolved.
Full Project: [github.com/kellyvv/OpenHer](https://github.com/kellyvv/OpenHer)
@ -14,7 +14,7 @@ Full Project: [github.com/kellyvv/OpenHer](https://github.com/kellyvv/OpenHer)
Without memory, every conversation starts from zero. She doesn't know your name. She doesn't remember that three weeks ago you mentioned you drink your coffee black. She doesn't know you once had a fight and made up.
With EverCore:
With EverOS:
**She remembers what you said.**
Three weeks ago you casually mentioned no sugar in your coffee. Today she says: "Americano, no sugar, right?"
@ -31,19 +31,19 @@ Last time you mentioned work stress. This time she asks: "How's that project goi
## Memory Architecture
OpenHer's memory has three layers. EverCore powers the deepest one:
OpenHer's memory has three layers. EverOS powers the deepest one:
| Layer | What it does | Analogy |
|:------|:-------------|:--------|
| **Style Memory** | Her behavioral habits — tone, expression patterns | Muscle memory |
| **Local Facts** | Your preferences, personal info | Short-term memory |
| **Long-Term Memory** | What happened between you, her understanding of you, her hunches | **Episodic memory (EverCore)** |
| **Long-Term Memory** | What happened between you, her understanding of you, her hunches | **Episodic memory (EverOS)** |
---
## How Memory Feeds Into Personality
OpenHer's core is a living neural network (25D input, 24D hidden, 8D behavioral signals). EverCore provides 4 key dimensions that let her tell the difference between a stranger and an old friend:
OpenHer's core is a living neural network (25D input, 24D hidden, 8D behavioral signals). EverOS provides 4 key dimensions that let her tell the difference between a stranger and an old friend:
```
Relationship Depth 0 ─────────────────── 1
@ -96,13 +96,13 @@ User sends a message
|
v
Load memory -- First turn: load "who you are", "what we talked about",
| "what's on her mind" from EverCore
| "what's on her mind" from EverOS
v
Perceive -- LLM evaluates the current moment: your emotion, topic
| intimacy, conflict level... (8 dimensions)
| + relationship dimensions from EverCore (4 dimensions) = 12D
| + relationship dimensions from EverOS (4 dimensions) = 12D
v
Relationship evolves -- Blend EverCore history with this turn's changes
Relationship evolves -- Blend EverOS history with this turn's changes
| Smoothed so a single remark can't flip the relationship
v
Neural network -- 25D input (drives + context + relationship + internal state)
@ -115,7 +115,7 @@ User sends a message
Respond -- Internal monologue first, then choose what to say and how
|
v
Remember this turn -- Store the conversation in EverCore (async, non-blocking)
Remember this turn -- Store the conversation in EverOS (async, non-blocking)
|
v
Prepare for next -- Search for memories related to what you just said
@ -128,7 +128,7 @@ User sends a message
- **Emergent Personality** — Not written in a prompt. Emerges from random neural networks, 5D drives, and Hebbian learning
- **Emotional Thermodynamics** — Drives metabolize over real time. She gets lonely when you're away, irritated when ignored
- **Feel First** — Every response starts with an internal monologue before choosing words
- **Cross-Session Memory** — EverCore stores your shared story across every conversation
- **Cross-Session Memory** — EverOS stores your shared story across every conversation
- **Relationship Evolution** — The relationship vector deepens naturally with each turn
- **Proactive Messages** — She reaches out not on a timer, but because her connection hunger is rising
- **Modal Expression** — She chooses text, voice, or photos based on what the moment calls for
@ -140,7 +140,7 @@ User sends a message
|:------|:-----------|
| Runtime | Python 3.11+, FastAPI, WebSocket, asyncio |
| LLM | Gemini, Claude, Qwen3, GPT-5.4-mini, MiniMax, Moonshot, StepFun, Ollama |
| Memory | **EverCore** (self-hosted / cloud) + SQLite local state |
| Memory | **EverOS** (self-hosted / cloud) + SQLite local state |
| Desktop | SwiftUI (macOS native) |
| Voice | DashScope, OpenAI, MiniMax |
@ -152,7 +152,7 @@ User sends a message
- Python 3.11+
- Any supported LLM provider API key
- EverCore (self-hosted or cloud)
- EverOS (self-hosted or cloud)
### 1. Clone & Install
@ -174,12 +174,12 @@ DEFAULT_PROVIDER=gemini
DEFAULT_MODEL=gemini-3.1-flash-lite-preview
GEMINI_API_KEY=your_key
# EverCore — Cloud
# EverMind Cloud
EVERMEMOS_BASE_URL=https://api.evermind.ai/v1
EVERMEMOS_API_KEY=your_key
# EverCore — Self-hosted
# cd vendor/EverCore && docker compose up -d && uv run python src/run.py
# EverOS — Self-hosted
# cd vendor/EverOS && docker compose up -d && uv run python src/run.py
# EVERMEMOS_BASE_URL=http://localhost:1995/api/v1
```
@ -194,7 +194,7 @@ python main.py
```bash
python demo/evermemos_demo.py
# Runs in simulation mode even without EverCore
# Runs in simulation mode even without EverOS
```
---
@ -205,11 +205,11 @@ python demo/evermemos_demo.py
OpenHer/
├── agent/
│ ├── chat_agent.py # Main agent, full lifecycle
│ ├── evermemos_mixin.py # EverCore integration (load/store/search/EMA)
│ ├── evermemos_mixin.py # EverOS integration (load/store/search/EMA)
│ └── prompt_builder.py # Memory injection into Actor prompt
├── engine/
│ └── genome/
│ ├── genome_engine.py # Neural network + 12D context (incl. 4D EverCore)
│ ├── genome_engine.py # Neural network + 12D context (incl. 4D EverOS)
│ ├── critic.py # LLM perception: 8D context + relationship deltas
│ ├── drive_metabolism.py # Emotional thermodynamics
│ └── style_memory.py # KNN behavioral memory + Hawking radiation decay
@ -219,7 +219,7 @@ OpenHer/
├── persona/
│ └── personas/ # 10 pre-built personas (SOUL.md + seeds)
├── vendor/
│ └── EverCore/ # Self-hosted EverCore
│ └── EverOS/ # Self-hosted EverOS
└── main.py # FastAPI server
```
@ -227,7 +227,7 @@ OpenHer/
## Integration Code at a Glance
### EverCore Mixin
### EverOS Mixin
The core integration is a mixin class handling four async operations:
@ -265,7 +265,7 @@ class SessionContext:
## Without Memory vs. With Memory
| | Without EverCore | With EverCore |
| | Without EverOS | With EverOS |
|:--|:--|:--|
| First meeting | "Hi! I'm Luna" | "Hi! I'm Luna" |
| Second meeting | "Hi! I'm Luna" | "Hey Alex! How's that project going?" |
@ -278,7 +278,7 @@ class SessionContext:
## Links
- Full Project: [github.com/kellyvv/OpenHer](https://github.com/kellyvv/OpenHer)
- EverCore: [evermind.ai](https://evermind.ai)
- EverOS: [evermind.ai](https://evermind.ai)
## License

View File

@ -1,18 +1,18 @@
#!/usr/bin/env python3
"""
OpenHer × EverCore Integration Demo
OpenHer × EverOS Integration Demo
Demonstrates how EverCore provides long-term memory to the
Demonstrates how EverOS provides long-term memory to the
AI Being persona engine. Shows session context loading, memory
storage, search, and relationship vector evolution.
Usage:
# With EverCore Cloud
# With EverMind Cloud
export EVERMEMOS_BASE_URL=https://api.evermind.ai/v1
export EVERMEMOS_API_KEY=your_key
python demo/evermemos_demo.py
# With self-hosted EverCore
# With self-hosted EverOS
export EVERMEMOS_BASE_URL=http://localhost:1995/api/v1
python demo/evermemos_demo.py
"""
@ -20,12 +20,10 @@ Usage:
import asyncio
import os
import sys
import json
from datetime import datetime
from typing import Optional
# ──────────────────────────────────────────────
# EverCore Client (minimal standalone version)
# EverOS Client (minimal standalone version)
# ──────────────────────────────────────────────
try:
@ -35,8 +33,8 @@ except ImportError:
sys.exit(1)
class EverCoreClient:
"""Minimal EverCore client for demo purposes."""
class EverOSClient:
"""Minimal EverOS client for demo purposes."""
def __init__(self, base_url: str, api_key: str = ""):
self.base_url = base_url.rstrip("/")
@ -51,7 +49,7 @@ class EverCoreClient:
return h
async def health_check(self) -> bool:
"""Check if EverCore is reachable."""
"""Check if EverOS is reachable."""
try:
# Try the health endpoint (remove /api/v1 suffix)
health_url = self.base_url.replace("/api/v1", "") + "/health"
@ -129,12 +127,13 @@ class EverCoreClient:
# ──────────────────────────────────────────────
# Relationship Vector (from EverCore session)
# Relationship Vector (from EverOS session)
# ──────────────────────────────────────────────
def compute_relationship_vector(profile_data: dict) -> dict:
"""
Extract 4D relationship vector from EverCore profile data.
Extract 4D relationship vector from EverOS profile data.
These 4 dimensions expand the persona engine's neural network
from 8D to 12D input, allowing it to differentiate behavior
@ -152,12 +151,12 @@ def apply_relationship_ema(
prior: dict,
delta: dict,
conversation_depth: float,
prev_ema: Optional[dict] = None,
prev_ema: dict | None = None,
) -> dict:
"""
Semi-emergent relationship update (Step 2.5 of ChatAgent lifecycle).
Blends EverCore prior with LLM-judged delta through EMA:
Blends EverOS prior with LLM-judged delta through EMA:
- alpha modulated by conversation depth (deeper = trust LLM more)
- Clips to valid ranges
- Preserves momentum through prev_ema
@ -181,25 +180,26 @@ def apply_relationship_ema(
# Demo
# ──────────────────────────────────────────────
async def main():
base_url = os.getenv("EVERMEMOS_BASE_URL", "")
api_key = os.getenv("EVERMEMOS_API_KEY", "")
if not base_url:
print("=" * 60)
print("OpenHer × EverCore Integration Demo")
print("OpenHer × EverOS Integration Demo")
print("=" * 60)
print()
print("⚠️ EVERMEMOS_BASE_URL not set.")
print()
print("To run this demo, set up EverCore:")
print("To run this demo, set up EverOS:")
print()
print(" Option A — Cloud:")
print(" Option A — EverMind Cloud:")
print(" export EVERMEMOS_BASE_URL=https://api.evermind.ai/v1")
print(" export EVERMEMOS_API_KEY=your_key")
print()
print(" Option B — Self-hosted:")
print(" cd vendor/EverCore && docker compose up -d")
print(" cd vendor/EverOS && docker compose up -d")
print(" uv run python src/run.py")
print(" export EVERMEMOS_BASE_URL=http://localhost:1995/api/v1")
print()
@ -209,20 +209,20 @@ async def main():
await demo_simulation()
return
client = EverCoreClient(base_url, api_key)
client = EverOSClient(base_url, api_key)
print("=" * 60)
print("OpenHer × EverCore Integration Demo")
print("OpenHer × EverOS Integration Demo")
print("=" * 60)
print(f"\n📡 EverCore: {base_url}")
print(f"\n📡 EverOS: {base_url}")
# Health check
healthy = await client.health_check()
if not healthy:
print("❌ EverCore is not reachable. Check your URL and try again.")
print("❌ EverOS is not reachable. Check your URL and try again.")
await client.close()
return
print("✅ EverCore is healthy\n")
print("✅ EverOS is healthy\n")
# ── Demo conversation ──
user_id = "demo_user"
@ -232,8 +232,15 @@ async def main():
group_id = f"{persona_id}__{user_id}"
conversations = [
("My name is Alex, I'm a software engineer", "Nice to meet you Alex! What kind of software do you work on?"),
("I love hiking in the mountains on weekends", "That sounds wonderful! There's something about being up high that makes everything else feel small."),
(
"My name is Alex, I'm a software engineer",
"Nice to meet you Alex! What kind of software do you work on?",
),
(
"I love hiking in the mountains on weekends",
"That sounds wonderful! There's something about being up high "
"that makes everything else feel small.",
),
("I drink my coffee black, no sugar", "Noted! A purist. I respect that."),
]
@ -249,7 +256,7 @@ async def main():
agent_reply=agent_reply,
)
status = "" if "error" not in result else ""
print(f" {status} User: \"{user_msg[:50]}...\"")
print(f' {status} User: "{user_msg[:50]}..."')
# Wait for indexing
print("\n⏳ Waiting for memory indexing (3s)...")
@ -270,7 +277,7 @@ async def main():
group_id=group_id,
)
memories = result.get("result", {}).get("memories", [])
print(f" Q: \"{query}\"")
print(f' Q: "{query}"')
if memories:
for mem in memories[:2]:
content = str(mem)[:100]
@ -281,7 +288,12 @@ async def main():
# Relationship vector
print("📊 Relationship Vector Evolution:\n")
prior = {"relationship_depth": 0.0, "emotional_valence": 0.0, "trust_level": 0.0, "pending_foresight": 0.0}
prior = {
"relationship_depth": 0.0,
"emotional_valence": 0.0,
"trust_level": 0.0,
"pending_foresight": 0.0,
}
deltas = [
{"relationship_depth": 0.1, "emotional_valence": 0.2, "trust_level": 0.05},
{"relationship_depth": 0.05, "emotional_valence": 0.1, "trust_level": 0.1},
@ -290,61 +302,147 @@ async def main():
ema = None
for i, delta in enumerate(deltas):
ema = apply_relationship_ema(prior, delta, conversation_depth=0.2 * (i + 1), prev_ema=ema)
print(f" Turn {i+1}: depth={ema['relationship_depth']:.3f} "
f"valence={ema['emotional_valence']:.3f} "
f"trust={ema['trust_level']:.3f}")
ema = apply_relationship_ema(
prior, delta, conversation_depth=0.2 * (i + 1), prev_ema=ema
)
print(
f" Turn {i + 1}: depth={ema['relationship_depth']:.3f} "
f"valence={ema['emotional_valence']:.3f} "
f"trust={ema['trust_level']:.3f}"
)
prior = ema
print(f"\n → After 3 turns: no longer a stranger (depth={ema['relationship_depth']:.3f})")
print(f" → Neural network now produces warmer, more familiar behavioral signals\n")
print(
"\n → After 3 turns: no longer a stranger "
f"(depth={ema['relationship_depth']:.3f})"
)
print(" → Neural network now produces warmer, more familiar behavioral signals\n")
await client.close()
print("✅ Demo complete!")
async def demo_simulation():
"""Run demo in simulation mode (no EverCore connection)."""
"""Run demo in simulation mode (no EverOS connection)."""
print("📊 Simulating Relationship Vector Evolution:\n")
print(" This shows how the 4D EverCore relationship vector")
print(" This shows how the 4D EverOS relationship vector")
print(" deepens over multiple conversation turns.\n")
prior = {"relationship_depth": 0.0, "emotional_valence": 0.0, "trust_level": 0.0, "pending_foresight": 0.0}
prior = {
"relationship_depth": 0.0,
"emotional_valence": 0.0,
"trust_level": 0.0,
"pending_foresight": 0.0,
}
# Simulate 10 turns of conversation
simulated_deltas = [
(0.3, {"relationship_depth": 0.10, "emotional_valence": 0.15, "trust_level": 0.05}),
(0.4, {"relationship_depth": 0.08, "emotional_valence": 0.10, "trust_level": 0.08}),
(0.5, {"relationship_depth": 0.05, "emotional_valence": 0.20, "trust_level": 0.12}),
(0.6, {"relationship_depth": 0.06, "emotional_valence": -0.10, "trust_level": 0.03}),
(0.7, {"relationship_depth": 0.04, "emotional_valence": 0.08, "trust_level": 0.10}),
(0.7, {"relationship_depth": 0.03, "emotional_valence": 0.12, "trust_level": 0.08}),
(0.8, {"relationship_depth": 0.02, "emotional_valence": 0.05, "trust_level": 0.06}),
(0.8, {"relationship_depth": 0.03, "emotional_valence": 0.10, "trust_level": 0.05}),
(0.9, {"relationship_depth": 0.01, "emotional_valence": 0.08, "trust_level": 0.04}),
(0.9, {"relationship_depth": 0.02, "emotional_valence": 0.06, "trust_level": 0.03}),
(
0.3,
{
"relationship_depth": 0.10,
"emotional_valence": 0.15,
"trust_level": 0.05,
},
),
(
0.4,
{
"relationship_depth": 0.08,
"emotional_valence": 0.10,
"trust_level": 0.08,
},
),
(
0.5,
{
"relationship_depth": 0.05,
"emotional_valence": 0.20,
"trust_level": 0.12,
},
),
(
0.6,
{
"relationship_depth": 0.06,
"emotional_valence": -0.10,
"trust_level": 0.03,
},
),
(
0.7,
{
"relationship_depth": 0.04,
"emotional_valence": 0.08,
"trust_level": 0.10,
},
),
(
0.7,
{
"relationship_depth": 0.03,
"emotional_valence": 0.12,
"trust_level": 0.08,
},
),
(
0.8,
{
"relationship_depth": 0.02,
"emotional_valence": 0.05,
"trust_level": 0.06,
},
),
(
0.8,
{
"relationship_depth": 0.03,
"emotional_valence": 0.10,
"trust_level": 0.05,
},
),
(
0.9,
{
"relationship_depth": 0.01,
"emotional_valence": 0.08,
"trust_level": 0.04,
},
),
(
0.9,
{
"relationship_depth": 0.02,
"emotional_valence": 0.06,
"trust_level": 0.03,
},
),
]
ema = None
for i, (depth, delta) in enumerate(simulated_deltas, 1):
alpha = max(0.15, min(0.65, 0.15 + 0.5 * depth))
ema = apply_relationship_ema(prior, delta, conversation_depth=depth, prev_ema=ema)
ema = apply_relationship_ema(
prior, delta, conversation_depth=depth, prev_ema=ema
)
bar_d = "" * int(ema["relationship_depth"] * 20)
bar_v = "" * int(max(0, ema["emotional_valence"]) * 20)
bar_t = "" * int(ema["trust_level"] * 20)
print(f" Turn {i:2d} (α={alpha:.2f}): "
f"depth={ema['relationship_depth']:.3f} {bar_d}")
print(f" "
f"valence={ema['emotional_valence']:+.3f} {bar_v}")
print(f" "
f"trust={ema['trust_level']:.3f} {bar_t}")
print(
f" Turn {i:2d} (α={alpha:.2f}): "
f"depth={ema['relationship_depth']:.3f} {bar_d}"
)
print(f" valence={ema['emotional_valence']:+.3f} {bar_v}")
print(f" trust={ema['trust_level']:.3f} {bar_t}")
print()
prior = ema
print(" ──────────────────────────────────")
print(f" Final state: depth={ema['relationship_depth']:.3f}, "
f"valence={ema['emotional_valence']:+.3f}, "
f"trust={ema['trust_level']:.3f}")
print(
f" Final state: depth={ema['relationship_depth']:.3f}, "
f"valence={ema['emotional_valence']:+.3f}, "
f"trust={ema['trust_level']:.3f}"
)
print()
print(" Turn 4 shows a negative emotional event (valence delta = -0.10),")
print(" but the EMA smoothing prevents overreaction — the relationship")

View File

@ -1,8 +1,8 @@
"""
Neural network context features — showing how EverCore expands
Neural network context features — showing how EverOS expands
the persona engine's perception from 8D to 12D.
The 4 additional relationship dimensions from EverCore allow the
The 4 additional relationship dimensions from EverOS allow the
neural network to produce different behavioral signals depending
on the history between user and persona.
@ -13,21 +13,21 @@ Full source: https://github.com/kellyvv/OpenHer/blob/main/engine/genome/genome_e
# 5D Drive System (internal motivation)
# ══════════════════════════════════════════════
DRIVES = ['connection', 'novelty', 'expression', 'safety', 'play']
DRIVES = ["connection", "novelty", "expression", "safety", "play"]
# ══════════════════════════════════════════════
# 8D Behavioral Signals (neural network output)
# ══════════════════════════════════════════════
SIGNALS = [
'directness', # 0=indirect hints → 1=straight talk
'vulnerability', # 0=guarded → 1=emotionally open
'playfulness', # 0=serious → 1=playful/teasing
'initiative', # 0=reactive → 1=proactive leading
'depth', # 0=small talk → 1=deep conversation
'warmth', # 0=cold/distant → 1=warm/caring
'defiance', # 0=compliant → 1=rebellious/stubborn
'curiosity', # 0=indifferent → 1=intensely curious
"directness", # 0=indirect hints → 1=straight talk
"vulnerability", # 0=guarded → 1=emotionally open
"playfulness", # 0=serious → 1=playful/teasing
"initiative", # 0=reactive → 1=proactive leading
"depth", # 0=small talk → 1=deep conversation
"warmth", # 0=cold/distant → 1=warm/caring
"defiance", # 0=compliant → 1=rebellious/stubborn
"curiosity", # 0=indifferent → 1=intensely curious
]
# ══════════════════════════════════════════════
@ -36,31 +36,30 @@ SIGNALS = [
CONTEXT_FEATURES = [
# ── 8D from Critic LLM (per-turn perception) ──
'user_emotion', # -1=negative → 1=positive
'topic_intimacy', # 0=professional → 1=intimate
'time_of_day', # 0=morning → 1=late night
'conversation_depth', # 0=just started → 1=deep conversation
'user_engagement', # 0=dismissive → 1=invested
'conflict_level', # 0=harmonious → 1=conflict
'novelty_level', # 0=routine topic → 1=novel topic
'user_vulnerability', # 0=guarded → 1=open
# ── 4D from EverCore (cross-session relationship) ──
'relationship_depth', # 0=stranger → 1=old friend
'emotional_valence', # -1=negative history → 1=positive history
'trust_level', # 0=no trust → 1=deep trust
'pending_foresight', # 0=nothing pending → 1=unresolved concern
"user_emotion", # -1=negative → 1=positive
"topic_intimacy", # 0=professional → 1=intimate
"time_of_day", # 0=morning → 1=late night
"conversation_depth", # 0=just started → 1=deep conversation
"user_engagement", # 0=dismissive → 1=invested
"conflict_level", # 0=harmonious → 1=conflict
"novelty_level", # 0=routine topic → 1=novel topic
"user_vulnerability", # 0=guarded → 1=open
# ── 4D from EverOS (cross-session relationship) ──
"relationship_depth", # 0=stranger → 1=old friend
"emotional_valence", # -1=negative history → 1=positive history
"trust_level", # 0=no trust → 1=deep trust
"pending_foresight", # 0=nothing pending → 1=unresolved concern
]
# Neural network dimensions
N_DRIVES = len(DRIVES) # 5
N_CONTEXT = len(CONTEXT_FEATURES) # 12 (8 + 4 from EverCore)
N_SIGNALS = len(SIGNALS) # 8
RECURRENT_SIZE = 8 # Internal "mood" state
N_DRIVES = len(DRIVES) # 5
N_CONTEXT = len(CONTEXT_FEATURES) # 12 (8 + 4 from EverOS)
N_SIGNALS = len(SIGNALS) # 8
RECURRENT_SIZE = 8 # Internal "mood" state
INPUT_SIZE = N_DRIVES + N_CONTEXT + RECURRENT_SIZE # 5 + 12 + 8 = 25
HIDDEN_SIZE = 24
# Architecture: 25D input → 24D hidden (tanh) → 8D output (sigmoid)
# The 4 EverCore dimensions mean the same neural network produces
# The 4 EverOS dimensions mean the same neural network produces
# DIFFERENT behavioral signals for strangers vs. old friends,
# even with identical conversation context.

View File

@ -1,14 +1,14 @@
"""
EverMemosMixin — EverCore integration for ChatAgent.
EverMemosMixin — EverOS integration for ChatAgent.
This mixin handles all async memory operations in the ChatAgent lifecycle:
Step 0: Session context loading (first turn)
Step 2.5: Relationship EMA (blend EverCore prior + LLM delta)
Step 2.5: Relationship EMA (blend EverOS prior + LLM delta)
Step 8.5: Collect async search results
Step 11: Fire-and-forget turn storage
Step 12: Async prefetch for next turn
The mixin pattern keeps EverCore concerns cleanly separated from the
The mixin pattern keeps EverOS concerns cleanly separated from the
core persona engine (drives, metabolism, neural network, style memory).
Full source: https://github.com/kellyvv/OpenHer/blob/main/agent/evermemos_mixin.py
@ -20,19 +20,19 @@ import asyncio
class EverMemosMixin:
"""EverCore async memory integration methods."""
"""EverOS async memory integration methods."""
async def _evermemos_gather(self) -> dict:
"""
Step 0: Load EverCore session context (first turn only).
Step 0: Load EverOS session context (first turn only).
Subsequent turns reuse cached _session_ctx.
Returns relationship_4d dict for GenomeEngine context.
"""
empty_4d = {
'relationship_depth': 0.0,
'emotional_valence': 0.0,
'trust_level': 0.0,
'pending_foresight': 0.0,
"relationship_depth": 0.0,
"emotional_valence": 0.0,
"trust_level": 0.0,
"pending_foresight": 0.0,
}
if not (self.evermemos and self.evermemos.available):
@ -75,10 +75,10 @@ class EverMemosMixin:
"""
# Map Critic output keys → context feature keys
delta_map = {
'relationship_depth': rel_delta.get('relationship_delta', 0.0),
'emotional_valence': rel_delta.get('emotional_valence', 0.0),
'trust_level': rel_delta.get('trust_delta', 0.0),
'pending_foresight': 0.0, # No delta for foresight (data-driven only)
"relationship_depth": rel_delta.get("relationship_delta", 0.0),
"emotional_valence": rel_delta.get("emotional_valence", 0.0),
"trust_level": rel_delta.get("trust_delta", 0.0),
"pending_foresight": 0.0, # No delta for foresight (data-driven only)
}
# Initialize EMA on first turn
@ -88,7 +88,7 @@ class EverMemosMixin:
# Compute posterior = clip(prior + delta)
posterior = {}
for k in prior:
lo = -1.0 if k == 'emotional_valence' else 0.0
lo = -1.0 if k == "emotional_valence" else 0.0
posterior[k] = max(lo, min(1.0, prior[k] + delta_map.get(k, 0.0)))
# Depth-modulated alpha: shallow → trust prior, deep → trust LLM
@ -104,9 +104,10 @@ class EverMemosMixin:
return ema
def _evermemos_store_bg(self, user_message: str, reply: str) -> None:
"""Step 11: Fire-and-forget EverCore storage (asyncio.create_task)."""
"""Step 11: Fire-and-forget EverOS storage (asyncio.create_task)."""
if not (self.evermemos and self.evermemos.available):
return
async def _do_store():
try:
await self.evermemos.store_turn(
@ -120,6 +121,7 @@ class EverMemosMixin:
)
except Exception as e:
print(f" [evermemos] ❌ store failed: {type(e).__name__}: {e}")
try:
asyncio.create_task(_do_store())
except Exception as e:
@ -180,7 +182,7 @@ class EverMemosMixin:
self._relevant_facts = facts
self._relevant_episodes = episodes
self._relevant_profile = profile
except asyncio.TimeoutError:
except TimeoutError:
# Graceful degradation: use static session context
self._relevant_facts = ""
self._relevant_episodes = ""

View File

@ -3,9 +3,9 @@ Memory shared types for OpenHer.
These types bridge the two memory providers:
- SoulMem (behavioral memory, always-on SQLite layer)
- EverCore (declarative memory, cross-session persistence)
- EverOS (declarative memory, cross-session persistence)
The SessionContext is the key data structure loaded from EverCore
The SessionContext is the key data structure loaded from EverOS
at session start — it provides relationship priors, user profile,
episode summaries, and foresight data that expand the neural
network's perception from 8D to 12D.
@ -16,12 +16,12 @@ Full source: https://github.com/kellyvv/OpenHer/blob/main/memory/types.py
from __future__ import annotations
from dataclasses import dataclass
from typing import Optional
@dataclass
class Memory:
"""A single memory entry (SoulMem behavioral layer)."""
memory_id: int = 0
user_id: str = ""
persona_id: str = ""
@ -35,7 +35,7 @@ class Memory:
@dataclass
class SessionContext:
"""
EverCore session context (declarative memory).
EverOS session context (declarative memory).
Loaded once at session start, this contains everything the
persona needs to know about the user from past sessions:
@ -53,6 +53,7 @@ class SessionContext:
- Step 5: 4D vector enters neural network as context features
- Step 8.5: Used as fallback when async search times out
"""
user_id: str = ""
persona_id: str = ""
user_profile: str = ""
@ -63,4 +64,4 @@ class SessionContext:
trust_level: float = 0.0
pending_foresight: float = 0.0
has_history: bool = False
raw_data: Optional[dict] = None
raw_data: dict | None = None