langchain-endee 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,499 @@
1
+ Metadata-Version: 2.4
2
+ Name: langchain_endee
3
+ Version: 0.1.0
4
+ Summary: High Speed Vector Database for Faster and Efficient ANN Searches with LangChain
5
+ Home-page: https://endee.io
6
+ Author: Endee Labs
7
+ Author-email: support@endee.io
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.9
12
+ Description-Content-Type: text/markdown
13
+ Requires-Dist: langchain>=0.3.25
14
+ Requires-Dist: langchain-core>=0.3.59
15
+ Requires-Dist: endee>=0.1.22
16
+ Requires-Dist: endee_model
17
+ Requires-Dist: fastembed>=0.3.0
18
+ Requires-Dist: pydantic<3,>=1.9.0
19
+ Dynamic: author
20
+ Dynamic: author-email
21
+ Dynamic: classifier
22
+ Dynamic: description
23
+ Dynamic: description-content-type
24
+ Dynamic: home-page
25
+ Dynamic: requires-dist
26
+ Dynamic: requires-python
27
+ Dynamic: summary
28
+
29
+ # Endee LangChain Integration
30
+
31
+ LangChain vector store integration for [Endee](https://github.com/endee-io/endee).
32
+
33
+ For Endee setup, features, and server docs see [docs.endee.io](https://docs.endee.io/quick-start).
34
+
35
+ **Sections:** [Setup](#1-setup) | [Dense](#2-dense-search) | [Hybrid](#3-hybrid-search) | [Filters](#4-filters) | [RAG Chain](#5-rag-chain)
36
+
37
+ ---
38
+
39
+ ## 1. Setup
40
+
41
+ ### Install
42
+
43
+ ```bash
44
+ pip install langchain-endee endee endee-model
45
+ ```
46
+
47
+ Pick an embedding model:
48
+
49
+ ```bash
50
+ # Option A: Local (no API key)
51
+ pip install langchain-huggingface sentence-transformers
52
+
53
+ # Option B: OpenAI
54
+ pip install langchain-openai
55
+ ```
56
+
57
+ For hybrid search with SPLADE (optional):
58
+
59
+ ```bash
60
+ pip install fastembed
61
+ ```
62
+
63
+ ### Endee Serverless
64
+
65
+ Create a token at [app.endee.io](https://app.endee.io). See [docs](https://docs.endee.io/quick-start) for details.
66
+
67
+ ```python
68
+ from langchain_endee import EndeeVectorStore
69
+ from langchain_core.documents import Document
70
+ from endee import Precision
71
+
72
+ from langchain_huggingface import HuggingFaceEmbeddings
73
+ embeddings = HuggingFaceEmbeddings(model_name="all-MiniLM-L6-v2")
74
+ DIMENSION = 384
75
+
76
+ # Or OpenAI:
77
+ # from langchain_openai import OpenAIEmbeddings
78
+ # embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
79
+ # DIMENSION = 1536
80
+
81
+ vector_store = EndeeVectorStore(
82
+ embedding=embeddings,
83
+ api_token="your-token", # from app.endee.io
84
+ index_name="my_index",
85
+ dimension=DIMENSION,
86
+ )
87
+ ```
88
+
89
+ ### Endee Local (Docker)
90
+
91
+ Run Endee locally — no token needed. See [GitHub](https://github.com/endee-io/endee) for setup.
92
+
93
+ ```bash
94
+ docker run -p 8000:8080 -v endee-data:/data endee-oss:latest
95
+ ```
96
+
97
+ The API is served at `/api/v1`, so pass `base_url` pointing to that path:
98
+
99
+ ```python
100
+ vector_store = EndeeVectorStore(
101
+ embedding=embeddings,
102
+ index_name="local_index",
103
+ dimension=DIMENSION,
104
+ base_url="http://localhost:8000/api/v1", # local server, no token needed
105
+ )
106
+ ```
107
+
108
+ `base_url` works with all factory methods:
109
+
110
+ ```python
111
+ # from_documents
112
+ store = EndeeVectorStore.from_documents(
113
+ documents=documents,
114
+ embedding=embeddings,
115
+ index_name="my_index",
116
+ dimension=DIMENSION,
117
+ base_url="http://localhost:8000/api/v1",
118
+ )
119
+
120
+ # from_existing_index
121
+ store = EndeeVectorStore.from_existing_index(
122
+ index_name="my_index",
123
+ embedding=embeddings,
124
+ base_url="http://localhost:8000/api/v1",
125
+ )
126
+ ```
127
+
128
+ ### Ingest Documents
129
+
130
+ Each LangChain `Document` has `page_content` (the text to embed) and `metadata` (key-value pairs for filtering).
131
+
132
+ ```python
133
+ documents = [
134
+ Document(
135
+ page_content="Python is a high-level programming language known for readability.",
136
+ metadata={"topic": "programming", "language": "python"},
137
+ ),
138
+ Document(
139
+ page_content="Rust is a systems language focused on safety and speed.",
140
+ metadata={"topic": "programming", "language": "rust"},
141
+ ),
142
+ Document(
143
+ page_content="Machine learning gives systems the ability to learn from data.",
144
+ metadata={"topic": "ai", "field": "ml"},
145
+ ),
146
+ Document(
147
+ page_content="Vector databases store embeddings for fast similarity search.",
148
+ metadata={"topic": "database", "type": "vector"},
149
+ ),
150
+ Document(
151
+ page_content="RAG enhances LLM responses by retrieving relevant documents first.",
152
+ metadata={"topic": "ai", "field": "rag"},
153
+ ),
154
+ ]
155
+ ```
156
+
157
+ There are three ways to insert:
158
+
159
+ #### `from_documents()` — create index + insert `Document` objects
160
+
161
+ ```python
162
+ vector_store = EndeeVectorStore.from_documents(
163
+ documents=documents,
164
+ embedding=embeddings,
165
+ api_token="your-token",
166
+ index_name="my_index",
167
+ dimension=DIMENSION,
168
+ space_type="cosine",
169
+ precision=Precision.INT16,
170
+ force_recreate=True,
171
+ )
172
+ ```
173
+
174
+ #### `from_texts()` — create index + insert raw strings
175
+
176
+ ```python
177
+ vector_store = EndeeVectorStore.from_texts(
178
+ texts=[
179
+ "Python is a high-level programming language.",
180
+ "Rust is a systems language focused on safety.",
181
+ ],
182
+ metadatas=[
183
+ {"topic": "programming", "language": "python"},
184
+ {"topic": "programming", "language": "rust"},
185
+ ],
186
+ embedding=embeddings,
187
+ api_token="your-token",
188
+ index_name="my_index",
189
+ dimension=DIMENSION,
190
+ )
191
+ ```
192
+
193
+ #### `add_texts()` — insert into an existing store
194
+
195
+ ```python
196
+ new_ids = vector_store.add_texts(
197
+ texts=[
198
+ "Go is designed for scalable services.",
199
+ "TypeScript adds static typing to JavaScript.",
200
+ ],
201
+ metadatas=[
202
+ {"topic": "programming", "language": "go"},
203
+ {"topic": "programming", "language": "typescript"},
204
+ ],
205
+ batch_size=1000, # vectors per upsert (max 1000)
206
+ embedding_chunk_size=100, # texts per embedding API call
207
+ )
208
+ print(f"Inserted IDs: {new_ids}")
209
+ ```
210
+
211
+ ### Reconnect to an Existing Index
212
+
213
+ Use `from_existing_index()` to reconnect without re-ingesting — ideal for production.
214
+
215
+ ```python
216
+ vector_store = EndeeVectorStore.from_existing_index(
217
+ index_name="my_index",
218
+ embedding=embeddings,
219
+ api_token="your-token",
220
+ )
221
+ ```
222
+
223
+ ---
224
+
225
+ ## 2. Dense Search
226
+
227
+ ### `similarity_search()`
228
+
229
+ ```python
230
+ results = vector_store.similarity_search(query="How does RAG work?", k=3)
231
+
232
+ for doc in results:
233
+ print(f"[{doc.metadata.get('topic')}] {doc.page_content[:70]}")
234
+ ```
235
+
236
+ ### `similarity_search_with_score()`
237
+
238
+ ```python
239
+ scored = vector_store.similarity_search_with_score(query="neural networks", k=3)
240
+
241
+ for doc, score in scored:
242
+ print(f"sim={score:.3f} {doc.page_content[:60]}")
243
+ ```
244
+
245
+ ### `similarity_search_by_vector()`
246
+
247
+ ```python
248
+ query_vec = embeddings.embed_query("programming language safety")
249
+
250
+ # Dense mode
251
+ results = vector_store.similarity_search_by_vector(embedding=query_vec, k=2)
252
+
253
+ # Hybrid mode — sparse_indices and sparse_values must be supplied
254
+ # (omitting them logs a warning and falls back to dense-only)
255
+ sparse_vec = sparse.embed_query("programming language safety")
256
+ results = hybrid_store.similarity_search_by_vector(
257
+ embedding=query_vec,
258
+ sparse_indices=sparse_vec.indices,
259
+ sparse_values=sparse_vec.values,
260
+ k=2,
261
+ )
262
+ ```
263
+
264
+ ### `similarity_search_by_vector_with_score()`
265
+
266
+ ```python
267
+ scored_by_vec = vector_store.similarity_search_by_vector_with_score(
268
+ embedding=query_vec,
269
+ k=3,
270
+ filter=[{"topic": {"$eq": "programming"}}],
271
+ )
272
+
273
+ for doc, score in scored_by_vec:
274
+ print(f"sim={score:.3f} {doc.page_content[:65]}")
275
+ ```
276
+
277
+ ### Search tuning
278
+
279
+ See [Endee docs](https://docs.endee.io) for details on `ef`, `prefilter_cardinality_threshold`, and `filter_boost_percentage`.
280
+
281
+ ```python
282
+ results = vector_store.similarity_search(
283
+ query="vector search",
284
+ k=10,
285
+ ef=256,
286
+ filter=[{"topic": {"$eq": "database"}}],
287
+ prefilter_cardinality_threshold=5_000,
288
+ filter_boost_percentage=20,
289
+ include_vectors=False,
290
+ )
291
+ ```
292
+
293
+ ### `as_retriever()`
294
+
295
+ ```python
296
+ retriever = vector_store.as_retriever(search_kwargs={"k": 3})
297
+ docs = retriever.invoke("What are vector databases used for?")
298
+ ```
299
+
300
+ ---
301
+
302
+ ## 3. Hybrid Search
303
+
304
+ Pass `retrieval_mode=RetrievalMode.HYBRID` and a `sparse_embedding` to enable hybrid search. The correct `sparse_model` is auto-detected.
305
+
306
+ ### Sparse Embedding Classes
307
+
308
+ | Class | Model | Install |
309
+ |-------|-------|---------|
310
+ | `EndeeModelSparse` | Native BM25 (recommended) | included with `endee-model` |
311
+ | `FastEmbedSparse` | SPLADE (neural) | `pip install fastembed` |
312
+
313
+ ### Create a Hybrid Store
314
+
315
+ ```python
316
+ from langchain_endee import EndeeVectorStore, EndeeModelSparse, FastEmbedSparse, RetrievalMode
317
+
318
+ # Option A: EndeeModelSparse (recommended)
319
+ sparse = EndeeModelSparse()
320
+
321
+ # Option B: FastEmbedSparse with SPLADE
322
+ # sparse = FastEmbedSparse()
323
+
324
+ hybrid_store = EndeeVectorStore.from_documents(
325
+ documents=documents,
326
+ embedding=embeddings,
327
+ api_token="your-token",
328
+ index_name="hybrid_index",
329
+ dimension=DIMENSION,
330
+ space_type="cosine",
331
+ retrieval_mode=RetrievalMode.HYBRID,
332
+ sparse_embedding=sparse,
333
+ force_recreate=True,
334
+ )
335
+ ```
336
+
337
+ All search methods automatically use both dense and sparse:
338
+
339
+ ```python
340
+ results = hybrid_store.similarity_search("vector database semantic search", k=3)
341
+ ```
342
+
343
+ ### RRF Tuning
344
+
345
+ See [Endee docs](https://docs.endee.io) for details on Reciprocal Rank Fusion.
346
+
347
+ ```python
348
+ results = hybrid_store.similarity_search_with_score(
349
+ query="vector database semantic search",
350
+ k=3,
351
+ rrf_rank_constant=60,
352
+ dense_rrf_weight=0.7,
353
+ )
354
+ ```
355
+
356
+ ---
357
+
358
+ ## 4. Filters
359
+
360
+ Pass filters as a list of dicts (AND logic). See [Endee docs](https://docs.endee.io) for filter operators (`$eq`, `$in`, `$range`).
361
+
362
+ ### Search with filters
363
+
364
+ ```python
365
+ results = vector_store.similarity_search(
366
+ query="learning from data",
367
+ k=5,
368
+ filter=[{"topic": {"$eq": "ai"}}],
369
+ )
370
+ ```
371
+
372
+ ```python
373
+ results = vector_store.similarity_search(
374
+ query="safe languages",
375
+ k=5,
376
+ filter=[
377
+ {"topic": {"$eq": "programming"}},
378
+ {"language": {"$in": ["python", "rust"]}},
379
+ ],
380
+ )
381
+ ```
382
+
383
+ ### Retriever with filters
384
+
385
+ ```python
386
+ retriever = vector_store.as_retriever(
387
+ search_type="similarity",
388
+ search_kwargs={"k": 3, "filter": [{"topic": {"$eq": "ai"}}]},
389
+ )
390
+ docs = retriever.invoke("machine learning")
391
+ ```
392
+
393
+ ### `get_by_ids()`
394
+
395
+ ```python
396
+ docs = vector_store.get_by_ids(["id1", "id2"]) # positional-only
397
+ ```
398
+
399
+ ### `update_filters()`
400
+
401
+ Update filter metadata without re-embedding.
402
+
403
+ ```python
404
+ vector_store.update_filters([
405
+ {"id": "id1", "filter": {"topic": "updated", "priority": 1}},
406
+ ])
407
+ ```
408
+
409
+ ### `delete()`
410
+
411
+ ```python
412
+ # Delete by IDs
413
+ vector_store.delete(ids=["id1", "id2"])
414
+
415
+ # Delete by filter
416
+ vector_store.delete(filter=[{"status": {"$eq": "expired"}}])
417
+ ```
418
+
419
+ ---
420
+
421
+ ## 5. RAG Chain
422
+
423
+ Wire the retriever into a LangChain chain that passes retrieved context to an LLM.
424
+
425
+ ```python
426
+ from langchain_openai import ChatOpenAI
427
+ from langchain_core.prompts import ChatPromptTemplate
428
+ from langchain_core.output_parsers import StrOutputParser
429
+ from langchain_core.runnables import RunnablePassthrough
430
+
431
+
432
+ def format_docs(docs):
433
+ return "\n\n".join(doc.page_content for doc in docs)
434
+
435
+
436
+ retriever = vector_store.as_retriever(search_kwargs={"k": 3})
437
+ llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)
438
+
439
+ prompt = ChatPromptTemplate.from_template(
440
+ "Answer the question based only on the context below.\n\n"
441
+ "Context:\n{context}\n\n"
442
+ "Question: {question}"
443
+ )
444
+
445
+ rag_chain = (
446
+ {"context": retriever | format_docs, "question": RunnablePassthrough()}
447
+ | prompt
448
+ | llm
449
+ | StrOutputParser()
450
+ )
451
+
452
+ answer = rag_chain.invoke("How does vector search work?")
453
+ print(answer)
454
+ ```
455
+
456
+ Works with any retriever — dense, hybrid, or filtered:
457
+
458
+ ```python
459
+ # Hybrid RAG
460
+ retriever = hybrid_store.as_retriever(search_kwargs={"k": 3})
461
+
462
+ # Filtered RAG
463
+ retriever = vector_store.as_retriever(
464
+ search_type="similarity",
465
+ search_kwargs={"k": 3, "filter": [{"topic": {"$eq": "ai"}}]},
466
+ )
467
+ ```
468
+
469
+ ---
470
+
471
+ ## Constructor Parameters
472
+
473
+ | Parameter | Type | Default | Description |
474
+ |-----------|------|---------|-------------|
475
+ | `embedding` | `Embeddings` | *required* | LangChain embedding function |
476
+ | `index_name` | `str` | *required* | Name of the Endee index |
477
+ | `api_token` | `str \| None` | `None` | From [app.endee.io](https://app.endee.io) (None for local) |
478
+ | `base_url` | `str \| None` | `None` | API base URL for local deployment (e.g. `http://localhost:8000/api/v1`) |
479
+ | `dimension` | `int \| None` | `None` | Vector dimension (required for new indexes) |
480
+ | `space_type` | `str` | `"cosine"` | `"cosine"`, `"l2"`, or `"ip"` |
481
+ | `precision` | `str` | `Precision.INT16` | See [Endee docs](https://docs.endee.io) |
482
+ | `M` | `int` | `16` | See [Endee docs](https://docs.endee.io) |
483
+ | `ef_con` | `int` | `128` | See [Endee docs](https://docs.endee.io) |
484
+ | `retrieval_mode` | `RetrievalMode` | `DENSE` | `DENSE` or `HYBRID` |
485
+ | `sparse_embedding` | `SparseEmbeddings \| None` | `None` | Sparse model for hybrid search |
486
+ | `max_text_length` | `int \| None` | auto-detected | Max text length in tokens |
487
+ | `force_recreate` | `bool` | `False` | Delete and recreate index if exists |
488
+ | `validate_index_config` | `bool` | `True` | Validate dimension/config on connect |
489
+
490
+ ## Links
491
+
492
+ - [Endee Documentation](https://docs.endee.io)
493
+ - [Endee GitHub](https://github.com/endee-io/endee)
494
+ - [Endee Server](https://app.endee.io)
495
+ - [Interactive Demo (Colab)](https://colab.research.google.com/github/endee-io/nD-langchain-python/blob/main/endee_rag_demo.ipynb)
496
+
497
+ ## License
498
+
499
+ MIT License