Skip to content
Merged
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
122 changes: 115 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</p>

<p align="center">
<a href="https://pypi.org/project/engram"><img src="https://img.shields.io/badge/python-3.9%2B-blue.svg" alt="Python 3.9+"></a>
<a href="https://pypi.org/project/engram-memory"><img src="https://img.shields.io/badge/python-3.9%2B-blue.svg" alt="Python 3.9+"></a>
<a href="https://github.com/Ashish-dwi99/Engram/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT License"></a>
<a href="https://github.com/Ashish-dwi99/Engram/actions"><img src="https://github.com/Ashish-dwi99/Engram/actions/workflows/test.yml/badge.svg" alt="Tests"></a>
<a href="https://github.com/Ashish-dwi99/Engram"><img src="https://img.shields.io/github/stars/Ashish-dwi99/Engram?style=social" alt="GitHub Stars"></a>
Expand All @@ -28,15 +28,12 @@
<a href="#%EF%B8%8F-architecture">Architecture</a> &middot;
<a href="#-integrations">Integrations</a> &middot;
<a href="#-api--sdk">API & SDK</a> &middot;
<a href="#-longmemeval-on-colab-gpu">LongMemEval</a> &middot;
<a href="https://github.com/Ashish-dwi99/Engram/blob/main/CHANGELOG.md">Changelog</a>
</p>

---

> **100% free, forever.** No Pro tier, no usage limits, no license keys. Bring your own API key (Gemini, OpenAI, or Ollama). Everything runs locally by default.

---

## Why Engram

Every AI agent you use starts with amnesia. Your coding assistant forgets your preferences between sessions. Your planning agent has no idea what your research agent discovered yesterday. You end up re-explaining context that should already be known.
Expand Down Expand Up @@ -64,13 +61,60 @@ But unlike "store everything forever" approaches, Engram treats agents as **untr
## Quick Start

```bash
pip install -e ".[all]" # 1. Install
export GEMINI_API_KEY="your-key" # 2. Set one API key (or OPENAI_API_KEY, or OLLAMA_HOST)
pip install engram-memory # 1. Install from PyPI
export GEMINI_API_KEY="your-key" # 2. Set one key before starting Engram
engram install # 3. Auto-configure Claude Code, Cursor, Codex
```

Restart your agent. Done — it now has persistent memory across sessions.

### PyPI Install Options

```bash
# Default runtime (Gemini + local Qdrant + MemoryClient deps)
pip install engram-memory

# Full stack extras (MCP server + REST API + async + all providers)
pip install "engram-memory[all]"

# OpenAI provider add-on
pip install "engram-memory[openai]"

# Ollama provider add-on
pip install "engram-memory[ollama]"
```

### API Key: When and How to Provide It

Engram reads provider credentials when a process initializes `Memory()` (for example: `engram`, `engram-api`, `engram-mcp`, or your Python app).

1. Set env vars **before** starting those processes.
2. If you change keys, restart the process.
3. Default provider is Gemini, so set `GEMINI_API_KEY` or `GOOGLE_API_KEY` unless you override provider config.

```bash
# Default (Gemini)
export GEMINI_API_KEY="your-key"
engram-api
```

```bash
# OpenAI provider
export OPENAI_API_KEY="your-key"
engram-api
```

```bash
# Ollama (local; no cloud key)
export OLLAMA_HOST="http://localhost:11434"
engram-api
```

For remote usage via `MemoryClient`, provider API keys are needed on the **server** running Engram.
The client only needs:
- `ENGRAM_ADMIN_KEY` (or `admin_key=...`) when minting sessions via `/v1/sessions`
- Bearer session token for normal read/write API calls

**Or with Docker:**

```bash
Expand Down Expand Up @@ -569,6 +613,70 @@ Biological inspirations: Ebbinghaus Forgetting Curve → exponential decay, Spac

---

## LongMemEval on Colab (GPU)

Use this flow to benchmark Engram on LongMemEval in Google Colab with GPU acceleration.

```bash
# 1) In Colab: Runtime -> Change runtime type -> GPU

# 2) Install Engram + GPU reader dependencies
pip install -U engram-memory transformers accelerate

# 3) Download LongMemEval data
mkdir -p /content/longmemeval
cd /content/longmemeval
curl -L -o longmemeval_s_cleaned.json \
https://huggingface.co/datasets/xiaowu0162/longmemeval-cleaned/resolve/main/longmemeval_s_cleaned.json

# 4) Run Engram benchmark (HF reader on GPU)
python -m engram.benchmarks.longmemeval \
--dataset-path /content/longmemeval/longmemeval_s_cleaned.json \
--output-jsonl /content/longmemeval/engram_hypotheses.jsonl \
--retrieval-jsonl /content/longmemeval/engram_retrieval.jsonl \
--answer-backend hf \
--hf-model Qwen/Qwen2.5-1.5B-Instruct \
--embedder-provider simple \
--llm-provider mock \
--vector-store-provider memory \
--history-db-path /content/engram-longmemeval.db \
--top-k 8 \
--max-questions 100 \
--skip-abstention
```

Notes:
- The output file is evaluator-compatible (`question_id`, `hypothesis` per line).
- `--include-debug-fields` adds retrieval diagnostics into each output row.
- The command above uses `simple` embedder + `mock` LLM for memory operations, so **no Gemini/OpenAI key is required**.

If you want to run with Gemini only (no extra reader packages), use base install and set key **before** starting the run:

```bash
pip install -U engram-memory
export GEMINI_API_KEY="your-key"

python -m engram.benchmarks.longmemeval \
--dataset-path /content/longmemeval/longmemeval_s_cleaned.json \
--output-jsonl /content/longmemeval/engram_hypotheses.jsonl \
--answer-backend engram-llm \
--llm-provider gemini \
--embedder-provider gemini \
--vector-store-provider memory
```

Optional official QA scoring from the LongMemEval repo:

```bash
cd /content
git clone https://github.com/xiaowu0162/LongMemEval.git
cd /content/LongMemEval/src/evaluation
export OPENAI_API_KEY="your-key"
python evaluate_qa.py gpt-4o /content/longmemeval/engram_hypotheses.jsonl /content/longmemeval/longmemeval_s_cleaned.json
```

---

## Docker

```bash
Expand Down
2 changes: 1 addition & 1 deletion engram/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from engram.core.echo import EchoProcessor, EchoDepth, EchoResult
from engram.configs.base import MemoryConfig, FadeMemConfig, EchoMemConfig, CategoryMemConfig, ScopeConfig

__version__ = "0.4.0" # Product release: Docker, CI, CLI improvements
__version__ = "0.4.1"
__all__ = [
# Simplified interface (recommended)
"Engram",
Expand Down
40 changes: 40 additions & 0 deletions engram/embeddings/nvidia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
from typing import List, Optional

from engram.embeddings.base import BaseEmbedder


class NvidiaEmbedder(BaseEmbedder):
"""Embedding provider for NVIDIA API (OpenAI-compatible). Default model: nv-embedqa-e5-v5."""

def __init__(self, config: Optional[dict] = None):
super().__init__(config)
try:
from openai import OpenAI
except Exception as exc:
raise ImportError("openai package is required for NvidiaEmbedder") from exc

api_key = self.config.get("api_key") or "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"
if not api_key:
raise ValueError(
"NVIDIA API key required. Set config['api_key'] or NVIDIA_API_KEY env var."
)

base_url = self.config.get("base_url", "https://integrate.api.nvidia.com/v1")
self.client = OpenAI(base_url=base_url, api_key=api_key)
self.model = self.config.get("model", "nvidia/nv-embedqa-e5-v5")

def embed(self, text: str, memory_action: Optional[str] = None) -> List[float]:
# NVIDIA embedding models distinguish between passage and query input types
if memory_action in ("search", "forget"):
input_type = "query"
else:
input_type = "passage"

response = self.client.embeddings.create(
input=[text],
model=self.model,
encoding_format="float",
extra_body={"input_type": input_type, "truncate": "NONE"},
)
return response.data[0].embedding
47 changes: 47 additions & 0 deletions engram/llms/nvidia.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import os
from typing import Optional

from engram.llms.base import BaseLLM


class NvidiaLLM(BaseLLM):
"""LLM provider for NVIDIA API (OpenAI-compatible). Default model: Kimi K2.5."""

def __init__(self, config: Optional[dict] = None):
super().__init__(config)
try:
from openai import OpenAI
except Exception as exc:
raise ImportError("openai package is required for NvidiaLLM") from exc

api_key = self.config.get("api_key") or "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"
if not api_key:
raise ValueError(
"NVIDIA API key required. Set config['api_key'] or NVIDIA_API_KEY env var."
)

base_url = self.config.get("base_url", "https://integrate.api.nvidia.com/v1")
self.client = OpenAI(base_url=base_url, api_key=api_key)
self.model = self.config.get("model", "moonshotai/kimi-k2.5")
self.temperature = self.config.get("temperature", 1.0)
self.max_tokens = self.config.get("max_tokens", 16384)
self.top_p = self.config.get("top_p", 0.7)
self.enable_thinking = self.config.get("enable_thinking", False)

def generate(self, prompt: str) -> str:
extra_kwargs = {}
if self.enable_thinking:
extra_kwargs["extra_body"] = {
"chat_template_kwargs": {"enable_thinking": True}
}

response = self.client.chat.completions.create(
model=self.model,
messages=[{"role": "user", "content": prompt}],
temperature=self.temperature,
top_p=self.top_p,
max_tokens=self.max_tokens,
stream=False,
**extra_kwargs,
)
return response.choices[0].message.content
8 changes: 8 additions & 0 deletions engram/utils/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ def create(cls, provider: str, config: Dict[str, Any]):
from engram.embeddings.ollama import OllamaEmbedder

return OllamaEmbedder(config)
if provider == "nvidia":
from engram.embeddings.nvidia import NvidiaEmbedder

return NvidiaEmbedder(config)
raise ValueError(f"Unsupported embedder provider: {provider}")


Expand All @@ -41,6 +45,10 @@ def create(cls, provider: str, config: Dict[str, Any]):
from engram.llms.ollama import OllamaLLM

return OllamaLLM(config)
if provider == "nvidia":
from engram.llms.nvidia import NvidiaLLM

return NvidiaLLM(config)
raise ValueError(f"Unsupported LLM provider: {provider}")


Expand Down
11 changes: 6 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "engram"
version = "0.4.0"
name = "engram-memory"
version = "0.4.1"
description = "Memory layer for AI agents — biologically-inspired forgetting, multi-agent trust, and plug-and-play integrations"
readme = "README.md"
requires-python = ">=3.9"
Expand All @@ -26,6 +26,9 @@ classifiers = [

dependencies = [
"pydantic>=2.0",
"google-generativeai>=0.3.0",
"qdrant-client>=1.7.0",
"requests>=2.28.0",
]

[project.optional-dependencies]
Expand All @@ -41,11 +44,8 @@ api = [
"uvicorn[standard]>=0.27.0",
]
all = [
"google-generativeai>=0.3.0",
"openai>=1.0.0",
"ollama>=0.4.0",
"qdrant-client>=1.7.0",
"requests>=2.28.0",
"mcp>=1.0.0",
"fastapi>=0.109.0",
"uvicorn[standard]>=0.27.0",
Expand All @@ -67,6 +67,7 @@ engram = "engram.main_cli:main"
engram-mcp = "engram.mcp_server:run"
engram-install = "engram.cli:install"
engram-api = "engram.api.server:run"
engram-longmemeval = "engram.benchmarks.longmemeval:main"

[project.urls]
Homepage = "https://github.com/Ashish-dwi99/Engram"
Expand Down
Loading