Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,82 @@ For Local / self-hosted installations, every CLI flag has a matching variable:
Run `<binary> doctor` to display the resolved mode and verify the same retrieval
path the host uses.

### Optional Jev relevance reranking

memU can use [TypeSafe Jev](https://typesafe.ai/) as an opt-in second-stage
relevance gate after the normal vector search. Jev evaluates all returned
segment and resource candidates in one System One request, removes candidates
below the configured probability threshold, and reranks the rest. The original
vector `score` remains in each result and the Jev probability is added as
`jev_score`.

> [!IMPORTANT]
> Enabling this integration sends the retrieval query and the bounded candidate
> text to TypeSafe. It does not send embeddings or unrelated memory rows. Jev is
> disabled by default, and a default installation never imports its SDK or sends
> memory content to TypeSafe.

Install the optional dependency:

```bash
pip install "memu-cli[jev]"
```

Then add these values to `~/.memu/config.env` (keep the file private, for
example with mode `600` on POSIX systems):

```dotenv
MEMU_RETRIEVAL_RERANKER=jev
TYPESAFE_API_KEY=your-typesafe-key
```

Do not put the API key in a command-line argument. Optional tuning settings:

| Setting | Default | Meaning |
|---|---|---|
| `MEMU_JEV_MODEL` | `jev-latest` | TypeSafe model name or alias |
| `MEMU_JEV_MIN_RELEVANCE` | `0.5` | Minimum Noul probability retained |
| `MEMU_JEV_MAX_CANDIDATES` | `32` | Maximum candidates sent in one request |
| `MEMU_JEV_TIMEOUT_SECONDS` | `1.5` | Per-request timeout |
| `MEMU_JEV_ON_ERROR` | `fallback` | Return vector results with fallback metadata, or `raise` |

The normal retrieve commands now use Jev automatically. Successful responses
retain the existing `segments`, `files`, and `resources` layers and add:

```json
{
"segments": [{"text": "...", "score": 0.81, "jev_score": 0.96}],
"files": [{"name": "deploy", "score": 0.81, "jev_score": 0.96}],
"resources": [],
"jev": {
"applied": true,
"fallback": false,
"model": "jev-1.13.0",
"latency_ms": 112,
"candidate_count": 6,
"retained_count": 2
}
}
```

`fallback` is explicit: if TypeSafe is temporarily unavailable, the three
layers are returned unchanged and `jev.fallback` is `true`. Set
`MEMU_JEV_ON_ERROR=raise` when callers must refuse unevaluated results.

To validate the SDK contract without contacting TypeSafe, run:

```bash
uv run --extra jev python -m pytest tests/test_jev.py tests/test_jev_sdk.py -m "not integration"
```

With `TYPESAFE_API_KEY` exported or stored in `~/.memu/config.env`, the live
smoke test and latency benchmark are:

```bash
MEMU_RUN_LIVE_JEV=1 uv run --extra jev python -m pytest tests/test_jev_sdk.py -m integration
uv run --extra jev python scripts/benchmark_jev_retrieval.py
```

### Storage backends

| Provider | DSN | Vector search | Use for |
Expand Down
72 changes: 72 additions & 0 deletions docs/adr/0019-optional-jev-retrieval-reranker.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# ADR 0019: Put Jev in an Optional Retrieval Wrapper, Not `MemoryService`

- Status: Accepted
- Date: 2026-09-21
- Builds on: ADR 0002, ADR 0005, ADR 0012

## Context

memU's interactive read path embeds a query, retrieves bounded segment and
resource candidates by vector similarity, and rolls segment hits up to files.
This is fast and portable, but vector similarity can rank topical overlap above
memory that directly answers the query.

TypeSafe Jev is a System One decision model. It accepts structured state and
typed questions and returns probabilities rather than generated text. A bounded
set of Noul relevance questions can therefore distinguish useful memory from
false-positive similarity hits in one request.

Putting that request inside `MemoryService` would violate two existing
boundaries. The service is intentionally embedding-only, and its three methods
work identically across pluggable storage backends. Jev also sends candidate
content to an external processor and adds an optional dependency, so it cannot
become a silent default.

## Decision

Implement Jev as `JevRerankedMemoryBackend`, a structural
`AgenticMemoryBackend` wrapper under `memu.integrations.jev`.

The wrapper delegates `list_all_recall_files` and `commit_results` unchanged.
For `progressive_retrieve`, it:

1. calls the configured local or cloud backend once;
2. merges the returned segment and resource candidates by vector score;
3. sends a bounded candidate set in one real Jev System One request, with one
independent Noul relevance question per candidate;
4. filters and orders segments/resources by Jev probability; and
5. rolls surviving segment probabilities up to files.

The wrapper preserves the original vector `score`, adds `jev_score`, and emits a
small `jev` metadata object showing whether evaluation was applied or fell back.
It never changes storage, embeddings, or user-scope filtering.

The integration disables SDK retries so the one-call bound also means at most
one outbound System One HTTP attempt. Availability remains governed by the
wrapper's explicit fallback or raise policy instead of hidden retry latency.

The official asynchronous TypeSafe SDK is an optional `jev` package extra. The
shared backend builder installs the wrapper only when
`MEMU_RETRIEVAL_RERANKER=jev`. The default path does not import the SDK, require
a TypeSafe credential, change the backend object, or send memory content to
TypeSafe.

Runtime provider failures either return the untouched vector result with
explicit fallback metadata or raise, selected by configuration. Cancellation
always propagates. Configuration errors are never converted into fallback.

## Consequences

- `MemoryService` remains embedding-only and keeps its exact public surface.
- Local and cloud retrieval gain the same opt-in Jev behavior without storage
backend changes or migrations.
- Jev is materially responsible for final inclusion and ordering when the
evaluation succeeds, while the vector stage remains the scalable candidate
generator.
- One retrieval makes no more than one Jev inference request.
- Enabling the integration sends the query and bounded candidate text to
TypeSafe; documentation must disclose this beside the opt-in instructions.
- End-to-end latency gains one network decision call. Benchmarks report observed
warm p50/p95 rather than treating TypeSafe's published range as a guarantee.
- The default fallback favors availability but is visibly distinguishable from
a Jev-ranked response; strict callers can select the raise policy.
1 change: 1 addition & 0 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
- [0016: Client Event Reporting — One Envelope, a Spool by Default, Bounded Payloads](0016-client-event-reporting.md)
- [0017: `config.env` Is Written by a Command — `init` for the Entry, `config` for the Detail](0017-config-env-as-a-command.md)
- [0018: Mine Claude Cowork Through the Claude Code Bridge](0018-cowork-through-claude-code-bridge.md)
- [0019: Put Jev in an Optional Retrieval Wrapper, Not `MemoryService`](0019-optional-jev-retrieval-reranker.md)
Loading
Loading