Metadata-Version: 2.4
Name: prajnyavan
Version: 0.1.7
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: License :: OSI Approved :: MIT License
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: 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"

# Prajnyavan - The Memory Layer for AI

Persistent, emotionally-weighted memory for any LLM. Zero-config locally, production-ready when you need it.

---
## Zero-config quickstart (5 lines)
```bash
pip install prajnyavan
prajnyavan dev                   # auto-starts daemon in ~/.prajnyavan
```
```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. Data/log/cache live in ~/.prajnyavan.
- 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]"`.

### One-minute PyPI smoke test
```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
```

### 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 |

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 logs -n 50`        # tail daemon log
- `prajnyavan export --user alice` / `import --user alice --file backup.json`
- `prajnyavan stop` | `prajnyavan reset`

---
## 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`.
- 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`.

## 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` to refresh.

## 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.

## Memory filtering and search modes
- To scope results to a namespace, pass namespace="ns" (tags are enforced client-side).
- For exact text matching, pass exact_match=True to MemoryClient.search/search_batch; results are filtered to contain the raw query.
- Update an existing memory from the CLI:
  - List to get an id: `prajnyavan list --user demo`
  - Update fields: `prajnyavan update --id <uuid> --content "new text" --importance 0.8 --tags reviewed`

## Advanced search filters

search() and search_batch() support additional filters:

```python
# Filter by emotional valence
results = mem.search(user_id, "meeting", valence_bias="positive")  # or "negative" / "neutral"

# Filter by minimum importance score
results = mem.search(user_id, "project", min_importance=0.7)

# Filter by category tag
results = mem.search(user_id, "python", category="work")

# Exact substring match (post-filter on semantic results)
results = mem.search(user_id, "sprint review", exact_match=True)
```

## Memory insights

```python
# Top emotionally significant memories
highlights = mem.highlights(user_id, limit=5)

# Natural language summary of recent activity
summary = mem.summary(user_id, days=7)

# Debug: see which memories would influence a query
explanations = mem.explain("What is the user working on?")

# Current emotional state of the brain
emotion = mem.get_emotion()
# {'primary_emotion': 'joy', 'valence': 0.6, 'arousal': 0.4}
```

## 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
