crewai-endee
Endee vector database integration for CrewAI agent memory
crewai-endee connects Endee to CrewAI, giving your agents persistent memory with dense, hybrid, and multi-field retrieval.
Uses the same fields= configuration as the Endee Python client's create_collection().
Installation
Requires Python 3.10–3.13.
pip install crewai-endee
This installs endee, endee_model, and crewai automatically.
Quick Start
from crewai_endee import EndeeVectorStore
store = EndeeVectorStore(
type="my_collection",
embedder_config={
"provider": "sentence-transformer",
"config": {"model_name": "all-MiniLM-L6-v2"},
},
fields=[
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine", "precision": "int8"}},
],
)
store.save("Go is a statically typed language by Google.", {"lang": "Go"})
results = store.search("static typing", limit=3)
Connect to Endee
With API token
Sign up at endee.io and get your token. See the Endee docs for details.
store = EndeeVectorStore(
type="my_collection",
embedder_config=embedder_config,
api_token="YOUR_ENDEE_API_TOKEN",
fields=[
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine", "precision": "int8"}},
],
)
Without API token (local)
Run the open-source Endee server locally. See github.com/endee-io/endee for setup. Omit api_token:
store = EndeeVectorStore(
type="my_collection",
embedder_config=embedder_config,
fields=[
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine", "precision": "int8"}},
],
)
Dense Mode
from crewai_endee import EndeeVectorStore
store = EndeeVectorStore(
type="demo_dense",
embedder_config=embedder_config,
fields=[
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine", "precision": "int8"}},
],
force_recreate=True,
)
store.save("Python is a dynamic language.", {"lang": "Python"})
results = store.search("dynamic typing", limit=3)
Hybrid Mode (endee_bm25 — auto-encoded)
Add a sparse field with "sparse_model": "endee_bm25". The BM25 sparse encoder is created automatically — no extra setup needed:
store = EndeeVectorStore(
type="demo_hybrid",
embedder_config=embedder_config,
fields=[
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine", "precision": "int8"}},
{"name": "sparse", "type": "sparse",
"sparse_model": "endee_bm25"},
],
force_recreate=True,
)
store.save("Go has native concurrency.", {"lang": "Go"})
# Hybrid search — dense similarity + BM25 keyword matching, fused via RRF
results = store.search("concurrency", limit=3)
# Tune fusion weights
results = store.search(
"concurrency", limit=3,
field_weights={"dense": 0.3, "sparse": 0.7},
rrf_k=30,
)
Hybrid Mode (default sparse — user-provided vectors)
Use "sparse_model": "default" and provide your own sparse vectors via add_objects() and multi_field_search():
import uuid
from endee import rerank
store = EndeeVectorStore(
type="demo_hybrid_default",
embedder_config=embedder_config,
fields=[
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine", "precision": "int8"}},
{"name": "sparse", "type": "sparse", "sparse_model": "default"},
],
force_recreate=True,
)
# Upsert with user-provided sparse vectors
store.add_objects([{
"id": uuid.uuid4().hex,
"meta": {"text": "Python ML libraries", "metadata": {"lang": "Python"}},
"filter": {"lang": "Python"},
"fields": {
"dense": store.embedder(["Python ML libraries"])[0].tolist(),
"sparse": {"indices": [10, 42, 99], "values": [0.9, 0.4, 0.7]},
},
}])
# Search both fields with user-provided sparse query
raw = store.multi_field_search(fields={
"dense": {"query": store.embedder(["ML"])[0].tolist(), "limit": 3},
"sparse": {"query": {"indices": [10, 42], "values": [0.8, 0.5]}, "limit": 3},
})
fused = rerank(raw, limit=3, field_weights={"dense": 0.5, "sparse": 0.5})
Multi-Vector Mode
Add a multi_vector field for per-chunk or ColBERT-style embeddings:
store = EndeeVectorStore(
type="demo_multi_vector",
embedder_config=embedder_config,
fields=[
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine", "precision": "int8"}},
{"name": "chunks", "type": "multi_vector",
"params": {"dimension": 384, "space_type": "cosine",
"precision": "int8", "pooling": "mean"}},
],
force_recreate=True,
)
# Upsert with multi-vector data via add_objects
store.add_objects([{
"id": uuid.uuid4().hex,
"meta": {"text": "Long article about distributed systems.", "metadata": {}},
"filter": {},
"fields": {
"dense": store.embedder(["Long article about distributed systems."])[0].tolist(),
"chunks": [[0.1, ...], [0.3, ...], [0.5, ...]], # pre-computed chunk embeddings
},
}])
# Dense-only search still works
results = store.search("distributed systems", limit=3)
# Multi-field search with rerank
raw = store.multi_field_search(fields={
"dense": {"query": store.embedder(["consensus"])[0].tolist(), "limit": 3},
"chunks": {"query": [[0.1, ...], [0.3, ...]], "limit": 3},
})
fused = rerank(raw, limit=3, field_weights={"dense": 0.6, "chunks": 0.4})
Search with Filters
Endee uses MongoDB-style operator syntax:
# Filter by exact match
results = store.search("web language", limit=3, filter=[{"lang": {"$eq": "Python"}}])
# Filter with score threshold
results = store.search("systems", limit=3, score_threshold=0.3)
# Include raw vectors in results
results = store.search("Python", limit=1, include_vectors=True)
# HNSW tuning
results = store.search("query", limit=3, ef_search=256)
# Filtered search tuning
results = store.search(
"query", limit=3,
filter=[{"category": {"$eq": "systems"}}],
prefilter_cardinality_threshold=5000,
filter_boost_percentage=50,
)
Supported operators: $eq, $ne, $gt, $gte, $lt, $lte, $in
Collection Operations
# Describe collection metadata
info = store.describe()
# Retrieve objects by ID
objects = store.get_objects(["id1", "id2"])
# Retrieve a single object by ID
obj = store.get_vector("some_id")
# Update filter metadata without re-embedding
store.update_filters([{"id": "some_id", "filter": {"reviewed": "true"}}])
# Delete a single object by ID
store.delete_vector("some_id")
# Delete all objects matching a filter
store.delete(filter=[{"category": {"$eq": "outdated"}}])
# Delete the entire collection
store.reset()
# Close the connection
store.close()
CrewAI Integration
EndeeVectorStore extends CrewAI's BaseRAGStorage. Wire it into a Crew via ShortTermMemory and EntityMemory:
from crewai import LLM, Agent, Crew, Process, Task
from crewai.memory.short_term.short_term_memory import ShortTermMemory
from crewai.memory.entity.entity_memory import EntityMemory
from crewai_endee import EndeeVectorStore
embedder_config = {
"provider": "sentence-transformer",
"config": {"model_name": "all-MiniLM-L6-v2"},
}
fields = [
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine", "precision": "int8"}},
]
stm_store = EndeeVectorStore(
type="crew_short_term",
embedder_config=embedder_config,
fields=fields,
)
entity_store = EndeeVectorStore(
type="crew_entity",
embedder_config=embedder_config,
fields=fields,
)
short_term_memory = ShortTermMemory(storage=stm_store)
entity_memory = EntityMemory(storage=entity_store)
llm = LLM(model="gemini/gemini-2.5-flash", api_key=GOOGLE_API_KEY)
agent = Agent(
role="Software Analyst",
goal="Extract programming language characteristics",
backstory="You study programming language design.",
llm=llm,
)
task = Task(
description="Analyse key characteristics of Python, Java, and Go.",
expected_output="Structured summary of each language.",
agent=agent,
)
crew = Crew(
agents=[agent],
tasks=[task],
process=Process.sequential,
memory=True,
short_term_memory=short_term_memory,
entity_memory=entity_memory,
embedder=embedder_config,
verbose=True,
)
result = crew.kickoff()
API Reference
Constructor
EndeeVectorStore(
type: str, # Collection name (required)
embedder_config: dict, # Dense embedder config (required)
fields: list[dict], # Field definitions (required)
api_token: str = None, # Endee API token
base_url: str = None, # Custom API URL
endee_client: EndeeClient = None, # Pre-existing client
sparse_embedding: SparseEmbeddings = None, # Custom sparse model
content_payload_key: str = "text",
metadata_payload_key: str = "metadata",
force_recreate: bool = False, # Delete and recreate if exists
)
Field Types
# Dense vector
{"name": "dense", "type": "vector",
"params": {"dimension": 384, "space_type": "cosine",
"precision": "int8", "M": 16, "ef_con": 128}}
# Sparse (endee_bm25 — auto-encoded)
{"name": "sparse", "type": "sparse", "sparse_model": "endee_bm25"}
# Sparse (default — user provides vectors)
{"name": "sparse", "type": "sparse", "sparse_model": "default"}
# Multi-vector
{"name": "chunks", "type": "multi_vector",
"params": {"dimension": 128, "space_type": "cosine",
"precision": "float16", "pooling": "mean"}}
Methods
| Method | Description |
|---|---|
save(value, metadata) |
Embed text and upsert (dense + auto sparse) |
search(query, limit, filter, ...) |
Search with optional filters and RRF fusion |
add_objects(objects) |
Upsert arbitrary per-field data |
multi_field_search(fields, filter) |
Search multiple fields, raw per-field results |
ensure_collection() |
Verify collection exists |
describe() |
Collection metadata |
get_objects(ids) |
Retrieve objects by ID list |
get_vector(id) |
Retrieve single object by ID |
update_filters(updates) |
Update filter metadata without re-embedding |
delete_vector(id) |
Delete single object by ID |
delete(filter) |
Delete by metadata filter |
reset() |
Delete entire collection |
close() |
Close HTTP connection |
Search Parameters
| Parameter | Default | Description |
|---|---|---|
query |
(required) | Natural-language search query |
limit |
3 |
Max results |
filter |
None |
[{"field": {"$op": value}}] |
score_threshold |
0 |
Minimum similarity score |
ef_search |
None |
HNSW ef (default 128, max 1024) |
include_vectors |
False |
Fetch raw vector data |
field_weights |
None |
Per-field RRF weights (sum to 1.0) |
rrf_k |
60 |
RRF rank constant |
prefilter_cardinality_threshold |
None |
Brute-force pre-filter threshold |
filter_boost_percentage |
None |
Candidate pool expansion (0-100) |
Exports
from crewai_endee import (
EndeeVectorStore,
EndeeModelSparse,
SparseEmbeddings,
SparseVector,
Precision,
rerank,
)
Full Endee documentation: docs.endee.io | GitHub: endee-io/endee | CrewAI docs: docs.crewai.com
Metadata
Release files for crewai-endee 1.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| crewai_endee-1.1.0.tar.gz | 16.7 kB | Without provenance | Details for crewai_endee-1.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| crewai_endee-1.1.0-py3-none-any.whl | Python 3 | none | any | Details for crewai_endee-1.1.0-py3-none-any.whl |
Total release size: 29.9 kB
Release files / crewai_endee-1.1.0.tar.gz
| Download URL | crewai_endee-1.1.0.tar.gz |
|---|---|
| Size | 16.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
10799ad5acac08f54c475313f0c1f3bfc092122f60cd056780e1fcf2c63966a8
|
|
BLAKE2b-256 checksum How to use checksums |
ed083195047eea5f089b494934059e6e4eb6562c549c6935b537f03d195b5fbf
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.9
|
Release files / crewai_endee-1.1.0-py3-none-any.whl
| Download URL | crewai_endee-1.1.0-py3-none-any.whl |
|---|---|
| Size | 13.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2f70df93079e40103cf4cc2634e4a22544033e9a375fdc6480cb87d7e3eb3d7e
|
|
BLAKE2b-256 checksum How to use checksums |
b05b2bb7f5fcbef3c302b3ba4ce9d93c7dd57cdef43c529c74cf20ad88098327
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.9
|
Security for crewai-endee 1.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.