Metadata-Version: 2.4
Name: crewai-endee
Version: 0.1.1b7
Summary: Endee vector database integration for CrewAI agent memory
Home-page: https://endee.io/
Author: Endee Labs
Author-email: support@endee.io
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: endee>=0.1.22
Requires-Dist: endee_model
Requires-Dist: crewai_tools==1.5.0
Requires-Dist: crewai==1.5.0
Requires-Dist: fastembed
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# crewai-endee

**Endee vector database integration for CrewAI agent memory**

`crewai-endee` connects [Endee](https://github.com/endee-io/endee) to [CrewAI](https://crewai.com), giving your agents persistent memory with dense, hybrid, and filtered retrieval.

---

## Installation

Requires **Python 3.9+**.

```bash
pip install crewai-endee
```

This installs `endee`, `endee_model`, `crewai`, and `fastembed` automatically.

Add your embedding provider (only install the one you use):

```bash
pip install cohere       # Cohere
pip install openai       # OpenAI
pip install google-genai # Google Gemini
```

---

## Quick Start

```python
from crewai_endee import EndeeVectorStore

store = EndeeVectorStore(
    type="my_index",
    embedder_config={"provider": "cohere", "config": {"model_name": "embed-english-light-v3.0"}},
)

store.save(value="Go is a statically typed language by Google.", metadata={"lang": "Go"})
results = store.search("static typing", limit=3)
```

---

## Connect to Endee

First, configure your embedding provider:

```python
embedder_config = {
    "provider": "cohere",
    "config": {"model_name": "embed-english-light-v3.0", "api_key": "YOUR_COHERE_API_KEY"},
}
```

### With API token

Sign up at [endee.io](https://endee.io) and get your token. See the [Endee docs](https://docs.endee.io/quick-start) for details.

```python
from crewai_endee import EndeeVectorStore

store = EndeeVectorStore(
    type="my_index",
    embedder_config=embedder_config,
    api_token="YOUR_ENDEE_API_TOKEN",
)
```

### Without API token (local)

Run the open-source Endee server locally. See [github.com/endee-io/endee](https://github.com/endee-io/endee) for setup instructions. Then omit `api_token`:

```python
store = EndeeVectorStore(
    type="my_index",
    embedder_config=embedder_config,
)
```

---

## Dense Mode

```python
from crewai_endee import EndeeVectorStore

store = EndeeVectorStore(
    type="demo_dense",
    embedder_config=embedder_config,
    api_token=ENDEE_API_TOKEN,
    space_type="cosine",
    precision="int8",
)

store.ensure_index()
store.save(value="Python is a dynamic language.", metadata={"lang": "Python"})
results = store.search("dynamic typing", limit=3)
```

---

## Hybrid Mode

Add `sparse_model_name` to enable dense + sparse (BM25):

```python
hybrid_store = EndeeVectorStore(
    type="demo_hybrid",
    embedder_config=embedder_config,
    api_token=ENDEE_API_TOKEN,
    sparse_model_name="endee/bm25",
)

hybrid_store.ensure_index()
hybrid_store.save(value="Go has native concurrency.", metadata={"lang": "Go"})

# Hybrid search — combines dense similarity with BM25 keyword matching
results = hybrid_store.search("concurrency", limit=3)

# Tune fusion weights (hybrid only)
results = hybrid_store.search("concurrency", limit=3, dense_rrf_weight=0.8, rrf_rank_constant=60)
```

---

## Search with Filters

Endee uses MongoDB-style operator syntax for query filters:

```python
# Filter by exact match
results = store.search("web language", limit=3, filter=[{"typing": {"$eq": "dynamic"}}])

# Filter with score threshold
results = store.search("systems programming", limit=3, filter=[{"typing": {"$eq": "static"}}], score_threshold=0.2)

# Include raw vectors in results
results = store.search("interpreted language", limit=1, include_vectors=True)
```

Supported operators: `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$in`

---

## Index Operations

```python
# Describe index metadata (count, dimension, precision, etc.)
info = store.describe()

# Retrieve a single vector by ID
vec = store.get_vector("some_vector_id")

# Update filter metadata without re-embedding
store.update_filters([{"id": "some_vector_id", "filter": {"reviewed": "true"}}])

# Delete a single vector by ID
store.delete_vector("some_vector_id")

# Delete all vectors matching a filter
store.delete(filter=[{"category": {"$eq": "outdated"}}])

# Delete the entire index
store.reset()
```

---

## CrewAI Integration

`EndeeVectorStore` extends CrewAI's `RAGStorage`. Wire it into a Crew via `ShortTermMemory` and `EntityMemory`:

```python
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

# Create Endee-backed stores for crew memory
stm_store = EndeeVectorStore(
    type="crew_short_term",
    embedder_config=embedder_config,
    api_token=ENDEE_API_TOKEN,
)

entity_store = EndeeVectorStore(
    type="crew_entity",
    embedder_config=embedder_config,
    api_token=ENDEE_API_TOKEN,
)

# Wrap as CrewAI memory objects
short_term_memory = ShortTermMemory(storage=stm_store)
entity_memory = EntityMemory(storage=entity_store)

# LLM + Agents
llm = LLM(model="gemini/gemini-2.5-flash", api_key=GOOGLE_API_KEY)

analyst = Agent(
    role="Software Analyst",
    goal="Extract programming language characteristics",
    backstory="You study programming language design and typing systems.",
    llm=llm,
)

# Tasks
analysis_task = Task(
    description="Analyse key characteristics of Python, Java, Go, Rust, and C++.",
    expected_output="Structured summary of each language.",
    agent=analyst,
)

# Crew with Endee-backed memory
crew = Crew(
    agents=[analyst],
    tasks=[analysis_task],
    process=Process.sequential,
    memory=True,
    short_term_memory=short_term_memory,
    entity_memory=entity_memory,
    embedder=embedder_config,
    verbose=True,
)

result = crew.kickoff()
print(result)
```

---

## Supported Sparse Models

```python
from crewai_endee import list_supported_models

for name, config in list_supported_models().items():
    print(f"  {name} — {config['description']}")
```

| Model | Description |
|-------|-------------|
| `endee/bm25` | BM25 sparse embeddings via endee_model |
| `splade_pp` | SPLADE++ for English — semantic + keyword hybrid search |

---

## API Reference

### Constructor Parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `type` | `str` | *(required)* | Unique Endee index name |
| `embedder_config` | `dict` | `None` | Dense embedder config (`provider`, `config`) |
| `api_token` | `str` | `None` | Endee API token (omit for local mode) |
| `space_type` | `str` | `"cosine"` | Distance metric: `cosine`, `l2`, or `ip` |
| `precision` | `str` | server default | `float32`, `float16`, `int16`, `int8`, or `binary` |
| `sparse_model_name` | `str` | `None` | Set to enable hybrid mode (e.g. `"endee/bm25"`) |
| `ef_con` | `int` | `None` | HNSW ef_construction for index build quality |
| `text_key` | `str` | `"value"` | Metadata key under which document text is stored |
| `endee_index` | `Index` | `None` | Pre-existing `endee.Index` object |
| `allow_reset` | `bool` | `True` | Whether `reset()` is permitted |

### Methods

| Method | Description |
|--------|-------------|
| `save(value, metadata)` | Embed text and upsert into the index |
| `search(query, limit, filter, score_threshold, ...)` | Semantic search with optional filters |
| `ensure_index()` | Create / verify the index and print status |
| `describe()` | Return index metadata (count, dimension, precision, etc.) |
| `get_vector(id)` | Retrieve a single vector by ID |
| `delete_vector(id)` | Delete a single vector by ID |
| `delete(filter)` | Delete all vectors matching a metadata filter |
| `update_filters(updates)` | Update filter metadata without re-embedding |
| `reset()` | Delete the entire index and reset state |

### Search Parameters

| Parameter | Default | Description |
|-----------|---------|-------------|
| `query` | *(required)* | Natural-language search query |
| `limit` | `3` | Max results (max 512) |
| `filter` | `None` | MongoDB-style filters: `[{"field": {"$op": value}}]` |
| `score_threshold` | `0` | Minimum similarity score to include |
| `ef_search` | `None` | HNSW ef parameter (default 128, max 1024) |
| `include_vectors` | `False` | Include raw vector data in results |
| `dense_rrf_weight` | `None` | Dense score weight in hybrid RRF fusion (0.0–1.0) |
| `rrf_rank_constant` | `None` | RRF constant *k* (default 60) |

---

## Interactive Demo

[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/endee-io/crewai-endee/blob/main/endee_crewai_demo.ipynb)

---

Full Endee documentation: [docs.endee.io](https://docs.endee.io) | GitHub: [endee-io/endee](https://github.com/endee-io/endee) | CrewAI docs: [docs.crewai.com](https://docs.crewai.com)
