Metadata-Version: 2.4
Name: prajnyavan
Version: 0.3.0
Summary: Persistent, emotionally-weighted memory for any LLM
Author: Prajnyavan Team
License: MIT
Project-URL: Homepage, https://github.com/prajnyavan/prajnyavan
Project-URL: Repository, https://github.com/prajnyavan/prajnyavan
Project-URL: Issues, https://github.com/prajnyavan/prajnyavan/issues
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Operating System :: OS Independent
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == "openai"
Provides-Extra: cohere
Requires-Dist: cohere>=4.0; extra == "cohere"
Provides-Extra: local
Requires-Dist: sentence-transformers>=2.2; extra == "local"
Provides-Extra: uds
Requires-Dist: requests-unixsocket>=0.3; extra == "uds"
Provides-Extra: async
Requires-Dist: httpx>=0.27; extra == "async"
Provides-Extra: pydantic
Requires-Dist: pydantic>=2.0; extra == "pydantic"
Provides-Extra: langchain
Requires-Dist: langchain>=0.2; extra == "langchain"
Provides-Extra: llamaindex
Requires-Dist: llama-index>=0.10; extra == "llamaindex"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Provides-Extra: all
Requires-Dist: openai>=1.0; extra == "all"
Requires-Dist: cohere>=4.0; extra == "all"
Requires-Dist: sentence-transformers>=2.2; extra == "all"
Requires-Dist: requests-unixsocket>=0.3; extra == "all"
Requires-Dist: httpx>=0.27; extra == "all"
Requires-Dist: pydantic>=2.0; extra == "all"
Requires-Dist: langchain>=0.2; extra == "all"
Requires-Dist: llama-index>=0.10; extra == "all"

# Prajnyavan - The Memory Layer for AI

Persistent, emotionally-weighted memory for any LLM. Zero-config locally, honest about what is stable today.

---
## Why Prajnyavan
Launch-facing benchmark snapshot from the current tree:

| metric | current evidence | status |
| --- | --- | --- |
| token compression | 96 vs 1,348 tokens | 51% smaller context |
| GPU proxy savings | 165 vs 442 tokens | 86% lower proxy attention cost |
| storage per memory | 3,274 vs 3,298 bytes | smaller than naive JSON |
| import throughput | best measured local run: 500 memories in 0.3258s | 1,534.80 memories/sec |
| RAM delta for 1,000 memories | 47.88 MB | under the 100 MB gate |

Notes:
- Token, GPU, storage, and import numbers are measured from the local benchmark and CI harness in this repo.
- Throughput depends on hardware, process model, and payload shape. Treat the table above as a measured run, not a universal guarantee.
- `linux_verified` is still pending in `benchmarks/results.json`, so the RAM benchmark should be treated as provisional until the Linux artifact is committed back.

Installed-wheel throughput check:
```bash
prajnyavan benchmark import --memories 500 --json
```

Repo-checkout benchmark:
```bash
python scripts/import_benchmark.py --memories 500
```

The `scripts/` and `evals/` commands below are source-tree tools. They are not installed into the PyPI wheel. For an installed-wheel health check, use `prajnyavan smoke`, `prajnyavan doctor`, `prajnyavan benchmark import`, and the Python round-trip examples in this README.

### Windows-first release
- `0.3.0` keeps the trust-release focus and adds committed retrieval proof: expanded eval datasets, committed `v030` eval artifacts, long-horizon coverage, cold-archive lifecycle tests, and CI wiring to persist benchmark sync on `main`.
- If you try it, please file bugs, onboarding friction, and DX feedback at the GitHub issues page linked on PyPI.
- Release leadership summary: [docs/RELEASE_READINESS_0_3_0.md](/E:/BRAIN/brain-db/docs/RELEASE_READINESS_0_3_0.md)

---
## Zero-config quickstart (5 lines)
```bash
pip install prajnyavan
prajnyavan dev                   # auto-starts the real local daemon
```
```python
from prajnyavan import MemoryClient
mem = MemoryClient.dev(user_id="user_123")   # zero config, hash embeddings

@mem.wrap_llm(user_id="user_123")
def chat(prompt):
    return your_llm_call(prompt)
```
- No Docker or manual tokens locally. CLI control files live in `~/.prajnyavan`; the daemon stores its durable state in the OS app-data directory.
- Set OPENAI_API_KEY or COHERE_API_KEY to auto-upgrade embeddings; otherwise hash embeddings keep deps at zero.
- For full features in one go: `pip install "prajnyavan[all]"`.

### Supported launch contract
The launch-ready contract is intentionally narrow:

- `store`
- `search`
- `get_context`
- `get_memory`
- `update`
- `delete`
- `export/import`
- sessions / SSE
- `status`, `doctor`, `smoke`

Maintenance endpoints such as compaction and forced WAL recovery are currently disabled by default until that path is hardened. Trust comes before surface area.

### Retrieval profiles
`get_context()` now supports explicit runtime-cost profiles so developers can choose their cost/quality point instead of guessing:

- `edge`: smallest recall window, fewer entity groups, lowest token spend
- `standard`: balanced default with moderate recall depth and context budget
- `quality`: broader recall window, more entity groups, and the fullest context budget

```python
ctx = mem.get_context("user_123", "what matters here?", max_tokens=300, profile="edge")
print(ctx["profile_used"], ctx["tokens_used"], ctx["omitted_facts_count"])
for fact in ctx["facts"]:
    print(fact["layer"], fact["token_cost"], fact["content"])
```

### One-minute PyPI smoke test
```bash
prajnyavan smoke
```

Or, if you want the equivalent Python round-trip:
```bash
python - <<'PY'
from prajnyavan import MemoryClient
mem = MemoryClient.dev(user_id="smoke")
mid = mem.store("smoke", "Hello, memory!", importance=0.7)
print("stored", mid)
print("search", mem.search("smoke", "hello", exact_match=True))
print("emotion", mem.get_emotion())
PY
```

### Repo eval runner
Requires a source checkout of this repository:

```bash
cargo build --release -p prajnyavan_service
python evals/run_evals.py --eval all --output evals/results/v030.json
```

This runs profile-aware checks for:
- entity disambiguation
- contradiction handling
- stale-memory suppression
- preference recall
- emotional retrieval bias
- multi-agent shared-memory correctness
- token efficiency vs raw-history / vector / summary baselines
- long-horizon memory checks across 100 turns

Current local proof snapshot from [evals/results/v030.json](/E:/BRAIN/brain-db/evals/results/v030.json):

| eval | edge | standard | quality |
| --- | --- | --- | --- |
| entity disambiguation | 0.80 | 0.80 | 0.80 |
| contradiction handling | 1.00 | 1.00 | 1.00 |
| stale suppression | 1.00 | 1.00 | 1.00 |
| preference recall | 0.50 | 1.00 | 1.00 |
| multi-agent shared memory | 1.00 | 1.00 | 1.00 |
| long-horizon memory | 0.50 | 0.75 | 1.00 |

Turn-50 token comparison from [evals/results/v030_comparison.md](/E:/BRAIN/brain-db/evals/results/v030_comparison.md):

| system | tokens at turn 50 |
| --- | --- |
| raw history | 2773 |
| vector baseline | 289 |
| summary baseline | 128 |
| Prajnyavan edge | 22 |
| Prajnyavan standard | 25 |
| Prajnyavan quality | 36 |

These eval results come from a source-built local run on March 26, 2026. They are stronger proof than the earlier tiny eval scaffold, but Linux benchmark verification in [benchmarks/results.json](/E:/BRAIN/brain-db/benchmarks/results.json) is still pending.

### Embedding options
| provider | quality | deps | cost | note |
| --- | --- | --- | --- | --- |
| hash (default) | low | none | $0 | deterministic, zero setup |
| local (sentence-transformers) | medium | torch + model download | $0 | set embedding_provider="local" |
| openai | high | openai>=1.0 | $ | set OPENAI_API_KEY |
| cohere | high | cohere>=4.0 | $ | set COHERE_API_KEY |

### GPU & storage tradeoffs
Smaller context windows cut GPU work because transformer attention scales quadratically with sequence length. Smaller embeddings cut storage directly, and Prajnyavan already benefits from compact on-disk formats in the semantic store and archive layers.

| context tokens | relative attention ops | GPU time (relative) | cost vs 2000 tokens |
| --- | --- | --- | --- |
| 2000 | 4,000,000 | 1.0x | baseline |
| 1000 | 1,000,000 | 0.25x | 75% cheaper |
| 500 | 250,000 | 0.0625x | 93.75% cheaper |
| 200 | 40,000 | 0.01x | 99% cheaper |

| `embedding_dim` | storage change vs 768 | typical quality tradeoff | note |
| --- | --- | --- | --- |
| 768 | baseline | highest | default for the strongest semantic signal |
| 384 | 50% smaller | small drop | good balance for `all-MiniLM-L6-v2`-style embeddings |
| 96 | 87.5% smaller | noticeable drop | edge mode for tiny models or hash-style embeddings |

The exact runtime speedup depends on the model implementation, but the direction is stable: less context means less GPU work, and fewer embedding dimensions mean less storage.

wrap_llm caveat: it enriches the prompt. If your function accepts `memory_context` or `enriched_prompt`, it will receive them; otherwise the call signature stays the same.

### CLI helpers
- `prajnyavan status --verbose`  # health, uptime, memory count, cache size
- `prajnyavan status --json`     # machine-readable daemon status for scripts/CI
- `prajnyavan doctor`            # readiness checks (writable dirs, port, health, token issue)
- `prajnyavan doctor --json`     # machine-readable readiness report
- `prajnyavan smoke`             # start/store/search/emotion smoke test
- `prajnyavan benchmark import --memories 500 --json`  # wheel-available import throughput check with exact-count assertions
- `prajnyavan logs -n 50`        # tail daemon log
- `prajnyavan logs --rotate --max-mb 5`  # rotate daemon.log before tailing (defaults to 5 MB if unset)
- `prajnyavan export --user alice` / `import --user alice --file backup.json`
- `prajnyavan stop` | `prajnyavan reset`

### Launch notes for 0.3.0
- Repo proof is now committed in-tree: expanded eval datasets, `v030` eval results, turn-50 comparison output, restart persistence coverage, and cold-archive lifecycle tests.
- CI now has the missing benchmark-sync commit step so Linux verification can persist once the Ubuntu run succeeds on `main`.
- Linux benchmark parity is still pending in [benchmarks/results.json](/E:/BRAIN/brain-db/benchmarks/results.json); keep the RAM claim provisional until that artifact flips.
### Launch notes for 0.2.5
- Installed-wheel users now have a real benchmark entrypoint: `prajnyavan benchmark import --memories 500 --json`.
- The README now clearly separates wheel-local commands from repo-only benchmark and eval scripts.
- The existing 0.2.x trust fixes remain in place: honest `status` / `doctor`, real-daemon startup on Windows, safe maintenance failure, stable core CRUD, large-batch chunking, and measurable profile behavior.

### Legacy notes from 0.1.10
- Windows-friendly CLI: ASCII-safe output everywhere; `stop --force` cleans stale PID/locks.
- Built-in log rotation: `prajnyavan logs --rotate [--max-mb N]`.
- Fast-dev integration stubs for CI stay behind `RUN_PRAJNYAVAN_INTEGRATION=1`; normal runs use full MAS.
- Version alignment gates and wheel now include the Windows daemon binary.

---
## Works with any LLM
```python
import anthropic
from prajnyavan import MemoryClient

mem = MemoryClient.dev(user_id="user_123")

@mem.wrap_llm(user_id="user_123")
def chat_with_claude(prompt):
    return anthropic.Anthropic().messages.create(
        model="claude-opus-4-6",
        max_tokens=1000,
        messages=[{"role": "user", "content": prompt}]
    ).content[0].text
```

---
## Production / Advanced
- Docker: copy platform binary to /usr/local/bin/prajnyavan_service, set BRAIN_SECRET_KEY, bind 127.0.0.1:9999 by default.
- Env entrypoint: `PRAJNYAVAN_URL`, `PRAJNYAVAN_TOKEN`, optional `PRAJNYAVAN_EMBEDDING_PROVIDER`.
- Hosted API: `MemoryClient.hosted(base_url="https://your-hosted-url", token=os.environ["PRAJNYAVAN_TOKEN"])`.
- UDS (Linux/macOS): set PRAJNYAVAN_USE_UDS=1 before `prajnyavan dev` for unix socket transport.

---
## Troubleshooting (8 quick fixes)
1) `prajnyavan dev` hangs -> run `prajnyavan logs -n 50`; then `prajnyavan reset`.
2) 401/403 from client -> MemoryClient.dev() auto-refreshes; or `prajnyavan dev` to restart.
3) Binary missing -> reinstall or let `_daemon` auto-download from GitHub Releases.
4) Port in use -> daemon scans 9999-10008; set PRAJNYAVAN_USE_UDS=1 to avoid ports.
5) OpenAI/Cohere errors -> check API keys; fall back to hash by setting embedding_provider="hash".
6) Slow first call -> local model warms on first use; hash embedding is instant.
7) Log file missing -> start once with `prajnyavan dev`; logs at ~/.prajnyavan/daemon.log.
8) Corrupted dev.json -> delete ~/.prajnyavan/dev.json or run `prajnyavan reset`.
9) Unsure whether the install is healthy -> run `prajnyavan doctor` then `prajnyavan smoke`.

## Security
- BRAIN_SECRET_KEY required in prod; auto-generated per run in dev (tokens rotate on restart).
- dev.json is chmod 600 when possible; contains local bearer token and base_url.
- Use HTTPS/reverse proxy in prod; keep daemon bound to 127.0.0.1 by default.

## Performance baseline
Measured on this machine with release build, perf_targets dataset (hash embeddings):

| Operation       | p50  | p95  | p99  |
|-----------------|------|------|------|
| store()         | 12ms | 13ms | 14ms |
| search() k=5    | 0.3ms| 0.4ms| 0.5ms|
| list_memories() | 12ms | 13ms | 14ms |

Notes: timings are per-op averages derived from the `perf_targets` release tests on this machine (500 episodic writes + 50 reads, 200 semantic writes + 40 reads). Adjust for your hardware; rerun `scripts/bench_and_report.sh` from a repo checkout to refresh.

## RAM benchmark
Prajnyavan now exposes resident-memory measurement directly in the core and service layers. These commands require a repo checkout:

- `cargo test -p brain_core ram_usage_under_100mb_for_1000_memories -- --nocapture`
- `cargo test -p brain_core ram_compare_to_naive_dict -- --nocapture`
- `GET /v1/brain/status` now returns `memory_mb`, `hot_buffer`, `l1_count`, `l2_count`, `wal_bytes`, and `uptime_secs`

Target profiles:

| profile | key settings | target RSS delta for 1,000 memories |
| --- | --- | --- |
| standard | defaults | under 100 MiB |
| edge | `edge_mode = true`, `max_hot_buffer_size = 64`, `episodic_mmap_size_bytes = 4194304`, `embedding_dim = 384`, `ann_dimensions = 384` | under 50 MiB target |

Example local config (`prajnyavan.toml` or `brain.toml`):

```toml
edge_mode = true
max_hot_buffer_size = 64
episodic_mmap_size_bytes = 4194304
embedding_dim = 384
ann_dimensions = 384
```

## Contributing
- git clone && pip install -e '.[all]'
- cargo build -p prajnyavan_service
- pytest tests/ -v
- RUN_PRAJNYAVAN_INTEGRATION=1 pytest tests/integration/ -vv
- Good first issues: add Ollama adapter to wrap_llm; export/import CLI enhancements; make PRAJNYAVAN_TOKEN_TTL configurable.

## Python API Reference

### MemoryClient construction

| Method | Description |
|--------|-------------|
| MemoryClient.dev(user_id="developer") | Zero-config local daemon. Auto-starts. |
| MemoryClient.from_env()               | Production: reads PRAJNYAVAN_URL + TOKEN. |

### Storing memories

```python
# Basic store — returns memory id string
id = mem.store("alice", "Loves hiking in Nepal", importance=0.8, category="interests")

# Store with namespace scoping
id = mem.store("alice", "Q3 report done", namespace="work", ttl_days=90)

# Store a structured profile fact (upserts — same key replaces previous value)
mem.store_profile("alice", "role", "Lead Engineer", importance=0.9)

# Batch store
ids = mem.store_batch("alice", [
    {"content": "Likes coffee", "importance": 0.5},
    {"content": "Allergic to cats", "importance": 0.95, "category": "health"},
])

# Smart ingestion: infer category/memory type heuristically
mem.smart_store("alice", "Alice prefers bullet points in weekly updates.")

# Review an important memory to advance spaced repetition
mem.review_memory(ids[0])
```

### Searching memories

```python
# Basic semantic search
results = mem.search("alice", "outdoor activities", k=5)

# Filters — all optional, combinable
results = mem.search("alice", "work tasks",
    namespace="work",         # scope to namespace
    category="tasks",         # scope to category
    min_importance=0.6,       # only important memories
    valence_bias="positive",  # only positive-emotion memories
    exact_match=True,         # must contain query text (server-side)
)

# result shape: {id, content_text, relevance_score, primary_emotion, importance, tags}
```

### Retrieving and managing memories

```python
mem.get_memory(id)                      # fetch a single memory by UUID
mem.get_related(id, depth=2)            # graph-connected memories (spread activation)
mem.list_memories("alice")              # all memories for a user
mem.list_memories("alice", namespace="work")  # scoped list
profile = mem.export_user("alice")      # portable profile document (schema_version=1)
mem.import_profile(profile, target_user_id="alice")  # import/replay a portable profile
mem.update(id, {"importance": 0.9})     # partial update
mem.forget(id, "outdated",              # fine-grained delete
    tombstone_only=False,
    purge_archive=True)
mem.delete_memory(id)                   # simple delete
mem.delete_user("alice")                # delete all memories for user
```

### Portable Profiles

```python
profile = mem.export_user("alice")
assert profile["schema_version"] == 1

# Migrate the same profile into another Prajnyavan-backed product.
remote = MemoryClient.from_env()  # expects PRAJNYAVAN_URL + PRAJNYAVAN_TOKEN
remote.import_profile(profile, target_user_id="alice")
```

```bash
# Export from Product A
prajnyavan export --user alice --output alice_profile.json

# Import into Product B
PRAJNYAVAN_URL=https://productb.example.com \
PRAJNYAVAN_TOKEN="Bearer <token>" \
prajnyavan import --user alice --file alice_profile.json
```

### Analytics and state

```python
mem.highlights("alice", limit=10)       # highest-importance memories
mem.summary("alice", days=7)            # text summary of recent memories
mem.explain("outdoor activities")       # debug: scored source breakdown
mem.stats()                             # daemon performance metrics
mem.get_emotion()                       # global emotional state
mem.brain_status()                      # developmental stage
mem.last_strong_event("alice",          # most emotionally intense memory
    min_arousal=0.7)
```

### LLM context enrichment (decorator)

```python
@mem.wrap_llm(user_id="alice", k=5, valence_bias="positive", debug=False)
def chat(prompt):
    return your_llm_call(prompt)
# Automatically injects relevant memory context into prompt,
# stores the exchange after response.
```

### Knowledge graph

```python
mem.associate(id_a, id_b, edge_type=1, strength=0.9)  # create graph edge
# edge_type: 0=Temporal 1=Semantic 2=Causal 3=Episodic
#            4=Analogical 5=Cooccurrence 6=Contrary 7=PartOf
```

### Maintenance

```python
mem.compact("semantic")   # compact semantic vector store
mem.compact("episodic")   # compact ANN index
mem.compact("graph")      # compact knowledge graph
mem.recover_from_wal()    # replay WAL after crash
```

## Importance and emotion
- importance: float in [0,1]; higher = more salient; currently user-supplied or default from service.
- primary_emotion may be None for neutral text; surface your own emotion scores via tags if needed.

## Emotional context
The brain maintains a continuous emotional state that influences memory retrieval.

```python
state = mem.get_emotion()
# {'primary_emotion': 'joy', 'valence': 0.6, 'arousal': 0.4, 'mood': 'positive'}
```

To store a memory with explicit emotional tone:
```python
mem.store(user_id, "Great meeting today", valence=0.8, arousal=0.5)
```

valence: -1.0 (negative) to 1.0 (positive)  
arousal: 0.0 (calm) to 1.0 (excited)  
primary_emotion: joy, sadness, anger, fear, surprise, disgust, neutral
