Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ SENTENCE LAYER (L2) <- raw conversation. Sequential NEXT edges. The full story.
<img src="assets/screenshots/layers.jpeg" alt="Three-layer memory graph: Facts → Episodes → Sentences" width="680" />
</p>

Search hits Facts, graph discovers Episodes, traces back to source Sentences. SQLite by default — swap to Postgres, Neo4j, or Qdrant when you're ready to scale.
Search hits Facts, graph discovers Episodes, traces back to source Sentences. SQLite by default — swap to Postgres, Neo4j, Qdrant, or Milvus when you're ready to scale.

---

Expand All @@ -49,7 +49,8 @@ Still improving. Run your own in [`/benchmarks`](benchmarks/).
pip install vektori # SQLite + Postgres
pip install 'vektori[neo4j]' # + Neo4j support
pip install 'vektori[qdrant]' # + Qdrant support
pip install 'vektori[neo4j,qdrant]' # all backends
pip install 'vektori[milvus]' # + Milvus support
pip install 'vektori[neo4j,qdrant,milvus]' # all backends
```

No Docker, no external services. SQLite by default.
Expand Down Expand Up @@ -221,6 +222,13 @@ v = Vektori(
embedding_dimension=1024,
)

# Milvus — high-scale vector store with partition-key isolation
v = Vektori(
storage_backend="milvus",
database_url="http://localhost:19530",
embedding_dimension=1024,
)

# In-memory — tests / CI
v = Vektori(storage_backend="memory")
```
Expand All @@ -229,7 +237,7 @@ v = Vektori(storage_backend="memory")
```bash
git clone https://github.com/vektori-ai/vektori
cd vektori
docker compose up -d # starts Postgres, Neo4j, and Qdrant
docker compose up -d # starts Postgres, Neo4j, Qdrant, and Milvus

# Postgres
DATABASE_URL=postgresql://vektori:vektori@localhost:5432/vektori python examples/quickstart_postgres.py
Expand All @@ -239,11 +247,15 @@ VEKTORI_STORAGE_BACKEND=neo4j VEKTORI_DATABASE_URL=bolt://localhost:7687 vektori

# Qdrant
VEKTORI_STORAGE_BACKEND=qdrant VEKTORI_DATABASE_URL=http://localhost:6333 vektori add "I prefer dark mode" --user-id u1

# Milvus
VEKTORI_STORAGE_BACKEND=milvus VEKTORI_DATABASE_URL=http://localhost:19530 vektori add "I prefer dark mode" --user-id u1
```

**CLI storage flags:**
```bash
vektori config --storage-backend qdrant --database-url http://localhost:6333
vektori config --storage-backend milvus --database-url http://localhost:19530
vektori add "my note" --user-id u1
vektori search "preferences" --user-id u1
```
Expand Down
63 changes: 63 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,70 @@ services:
timeout: 5s
retries: 10

etcd:
image: quay.io/coreos/etcd:v3.5.5
environment:
ETCD_AUTO_COMPACTION_MODE: revision
ETCD_AUTO_COMPACTION_RETENTION: "1000"
ETCD_QUOTA_BACKEND_BYTES: "4294967296"
ETCD_SNAPSHOT_COUNT: "50000"
command:
- /usr/local/bin/etcd
- -advertise-client-urls=http://127.0.0.1:2379

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

etcd is configured with -advertise-client-urls=http://127.0.0.1:2379. In a Compose network this typically causes other containers (like milvus) to receive/learn a loopback endpoint that isn’t reachable from outside the etcd container. Prefer advertising the service DNS name (e.g., http://etcd:2379) so Milvus can reliably connect.

Suggested change
- -advertise-client-urls=http://127.0.0.1:2379
- -advertise-client-urls=http://etcd:2379

Copilot uses AI. Check for mistakes.
- -listen-client-urls=http://0.0.0.0:2379
- --data-dir=/etcd
ports:
- "2379:2379"
volumes:
- etcddata:/etcd
healthcheck:
test: ["CMD-SHELL", "ETCDCTL_API=3 etcdctl --endpoints=http://127.0.0.1:2379 endpoint health || exit 1"]
interval: 5s
timeout: 5s
retries: 20

minio:
image: minio/minio:RELEASE.2024-02-17T01-15-57Z
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
command: minio server /minio_data
ports:
- "9000:9000"
volumes:
- miniodata:/minio_data
healthcheck:
test: ["CMD-SHELL", "/usr/bin/mc ready local || exit 1"]
interval: 5s
timeout: 5s
retries: 20

milvus:
image: milvusdb/milvus:v2.5.3
command: ["milvus", "run", "standalone"]
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
ports:
- "19530:19530" # Milvus API
- "9091:9091" # Health/metrics
volumes:
- milvusdata:/var/lib/milvus
depends_on:
etcd:
condition: service_healthy
minio:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "curl -sf http://localhost:9091/healthz || exit 1"]
interval: 5s
timeout: 5s
retries: 20

volumes:
pgdata:
neo4jdata:
qdrantdata:
etcddata:
miniodata:
milvusdata:
93 changes: 93 additions & 0 deletions examples/ollama_milvus_session_demo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
"""Local Vektori demo using Ollama + Milvus.

Prerequisites:
1) Ollama running locally with:
- nomic-embed-text
- qwen2.5:1.5b (or adjust EXTRACTION_MODEL)
2) Milvus available at http://localhost:19530

Run:
python examples/ollama_milvus_session_demo.py
"""

from __future__ import annotations

import asyncio

from vektori import Vektori


EMBEDDING_MODEL = "ollama:nomic-embed-text"
EXTRACTION_MODEL = "ollama:qwen2.5:1.5b"
MILVUS_URL = "http://localhost:19530"


async def main() -> None:
# nomic-embed-text returns 768-dim vectors; Milvus schema must match.
client = Vektori(
storage_backend="milvus",
database_url=MILVUS_URL,
embedding_model=EMBEDDING_MODEL,
extraction_model=EXTRACTION_MODEL,
embedding_dimension=768,
async_extraction=False,
)

user_id = "demo-user-ollama-milvus"
session_id = "session-ollama-milvus-001"

messages = [
{
"role": "user",
"content": (
"I live in Pune and work remotely as a backend engineer. "
"I prefer calls between 9am and 11am IST, and avoid meetings after 6pm."
),
},
{
"role": "assistant",
"content": "Understood. I will schedule calls in your preferred morning window.",
},
{
"role": "user",
"content": "I also like concise weekly updates every Monday morning.",
},
]

try:
ingest_result = await client.add(
messages=messages,
session_id=session_id,
user_id=user_id,
metadata={"source": "local-demo"},
)
print("Ingestion:", ingest_result)

search_result = await client.search(
query="When should I contact this user and what communication style do they prefer?",
user_id=user_id,
depth="l1",
)

facts = search_result.get("facts", [])
episodes = search_result.get("episodes", [])
sentences = search_result.get("sentences", [])

print("\nFacts:")
for fact in facts:
print(f" - {fact.get('text')}")

print("\nEpisodes:")
for episode in episodes:
print(f" - {episode.get('text')}")

print("\nSentences:")
for sentence in sentences[:5]:
print(f" - {sentence.get('text')}")

finally:
await client.close()


if __name__ == "__main__":
asyncio.run(main())
6 changes: 3 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ dynamic = ["version"]
description = "Open-source, self-hostable memory engine for AI agents using a three-layer sentence graph architecture."
readme = "README.md"
license = { file = "LICENSE" }
authors = [
{ name = "Laxman & Manit" }
]
authors = [{ name = "Laxman & Manit" }]
requires-python = ">=3.10"
classifiers = [
"Development Status :: 3 - Alpha",
Expand All @@ -36,6 +34,7 @@ dependencies = [
postgres = ["asyncpg>=0.29", "pgvector>=0.2"]
neo4j = ["neo4j>=5.14"]
qdrant = ["qdrant-client>=1.7"]
milvus = ["pymilvus>=2.5.3"]
anthropic = ["anthropic>=0.20", "voyageai>=0.2"]
sentence-transformers = ["sentence-transformers>=2.6"]
bge = ["FlagEmbedding>=1.2"]
Expand All @@ -52,6 +51,7 @@ all = [
"pgvector>=0.2",
"neo4j>=5.14",
"qdrant-client>=1.7",
"pymilvus>=2.5.3",
"anthropic>=0.20",
"voyageai>=0.2",
"sentence-transformers>=2.6",
Expand Down
Loading
Loading