langroid 0.1.239__py3-none-any.whl → 0.1.241__py3-none-any.whl

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.
@@ -670,23 +670,26 @@ class OpenAIGPT(LanguageModel):
670
670
  sys.stdout.write(Colors().GREEN)
671
671
  sys.stdout.flush()
672
672
  has_function = False
673
- for event in response:
674
- (
675
- is_break,
676
- has_function,
677
- function_name,
678
- function_args,
679
- completion,
680
- ) = self._process_stream_event(
681
- event,
682
- chat=chat,
683
- has_function=has_function,
684
- completion=completion,
685
- function_args=function_args,
686
- function_name=function_name,
687
- )
688
- if is_break:
689
- break
673
+ try:
674
+ for event in response:
675
+ (
676
+ is_break,
677
+ has_function,
678
+ function_name,
679
+ function_args,
680
+ completion,
681
+ ) = self._process_stream_event(
682
+ event,
683
+ chat=chat,
684
+ has_function=has_function,
685
+ completion=completion,
686
+ function_args=function_args,
687
+ function_name=function_name,
688
+ )
689
+ if is_break:
690
+ break
691
+ except Exception:
692
+ pass
690
693
 
691
694
  print("")
692
695
  # TODO- get usage info in stream mode (?)
@@ -722,23 +725,26 @@ class OpenAIGPT(LanguageModel):
722
725
  sys.stdout.write(Colors().GREEN)
723
726
  sys.stdout.flush()
724
727
  has_function = False
725
- async for event in response:
726
- (
727
- is_break,
728
- has_function,
729
- function_name,
730
- function_args,
731
- completion,
732
- ) = self._process_stream_event(
733
- event,
734
- chat=chat,
735
- has_function=has_function,
736
- completion=completion,
737
- function_args=function_args,
738
- function_name=function_name,
739
- )
740
- if is_break:
741
- break
728
+ try:
729
+ async for event in response:
730
+ (
731
+ is_break,
732
+ has_function,
733
+ function_name,
734
+ function_args,
735
+ completion,
736
+ ) = self._process_stream_event(
737
+ event,
738
+ chat=chat,
739
+ has_function=has_function,
740
+ completion=completion,
741
+ function_args=function_args,
742
+ function_name=function_name,
743
+ )
744
+ if is_break:
745
+ break
746
+ except Exception:
747
+ pass
742
748
 
743
749
  print("")
744
750
  # TODO- get usage info in stream mode (?)
@@ -3,7 +3,7 @@ import json
3
3
  import logging
4
4
  import os
5
5
  import uuid
6
- from typing import List, Optional, Sequence, Tuple, TypeVar
6
+ from typing import Dict, List, Optional, Sequence, Tuple, TypeVar
7
7
 
8
8
  from dotenv import load_dotenv
9
9
  from qdrant_client import QdrantClient
@@ -13,7 +13,12 @@ from qdrant_client.http.models import (
13
13
  CollectionStatus,
14
14
  Distance,
15
15
  Filter,
16
- SearchParams,
16
+ NamedSparseVector,
17
+ NamedVector,
18
+ SearchRequest,
19
+ SparseIndexParams,
20
+ SparseVector,
21
+ SparseVectorParams,
17
22
  VectorParams,
18
23
  )
19
24
 
@@ -22,7 +27,7 @@ from langroid.embedding_models.base import (
22
27
  EmbeddingModelsConfig,
23
28
  )
24
29
  from langroid.embedding_models.models import OpenAIEmbeddingsConfig
25
- from langroid.mytypes import Document, EmbeddingFunction
30
+ from langroid.mytypes import Document, EmbeddingFunction, Embeddings
26
31
  from langroid.utils.configuration import settings
27
32
  from langroid.vector_store.base import VectorStore, VectorStoreConfig
28
33
 
@@ -62,15 +67,36 @@ class QdrantDBConfig(VectorStoreConfig):
62
67
  storage_path: str = ".qdrant/data"
63
68
  embedding: EmbeddingModelsConfig = OpenAIEmbeddingsConfig()
64
69
  distance: str = Distance.COSINE
70
+ use_sparse_embeddings: bool = False
71
+ sparse_embedding_model: str = ""
72
+ sparse_limit: int = 3
65
73
 
66
74
 
67
75
  class QdrantDB(VectorStore):
68
76
  def __init__(self, config: QdrantDBConfig = QdrantDBConfig()):
69
77
  super().__init__(config)
70
- self.config = config
78
+ self.config: QdrantDBConfig = config
71
79
  emb_model = EmbeddingModel.create(config.embedding)
72
80
  self.embedding_fn: EmbeddingFunction = emb_model.embedding_fn()
73
81
  self.embedding_dim = emb_model.embedding_dims
82
+ if self.config.use_sparse_embeddings:
83
+ try:
84
+ from transformers import AutoModelForMaskedLM, AutoTokenizer
85
+ except ImportError:
86
+ raise ImportError(
87
+ """
88
+ To use sparse embeddings,
89
+ you must install langroid with the [transformers] extra, e.g.:
90
+ pip install "langroid[transformers]"
91
+ """
92
+ )
93
+
94
+ self.sparse_tokenizer = AutoTokenizer.from_pretrained(
95
+ self.config.sparse_embedding_model
96
+ )
97
+ self.sparse_model = AutoModelForMaskedLM.from_pretrained(
98
+ self.config.sparse_embedding_model
99
+ )
74
100
  self.host = config.host
75
101
  self.port = config.port
76
102
  load_dotenv()
@@ -205,12 +231,22 @@ class QdrantDB(VectorStore):
205
231
  else:
206
232
  logger.warning("Recreating fresh collection")
207
233
  self.client.delete_collection(collection_name=collection_name)
208
- self.client.create_collection(
209
- collection_name=collection_name,
210
- vectors_config=VectorParams(
234
+
235
+ vectors_config = {
236
+ "": VectorParams(
211
237
  size=self.embedding_dim,
212
238
  distance=Distance.COSINE,
213
- ),
239
+ )
240
+ }
241
+ sparse_vectors_config = None
242
+ if self.config.use_sparse_embeddings:
243
+ sparse_vectors_config = {
244
+ "text-sparse": SparseVectorParams(index=SparseIndexParams())
245
+ }
246
+ self.client.create_collection(
247
+ collection_name=collection_name,
248
+ vectors_config=vectors_config,
249
+ sparse_vectors_config=sparse_vectors_config,
214
250
  )
215
251
  collection_info = self.client.get_collection(collection_name=collection_name)
216
252
  assert collection_info.status == CollectionStatus.GREEN
@@ -221,6 +257,32 @@ class QdrantDB(VectorStore):
221
257
  logger.info(collection_info)
222
258
  logger.setLevel(level)
223
259
 
260
+ def get_sparse_embeddings(self, inputs: List[str]) -> List[SparseVector]:
261
+ if not self.config.use_sparse_embeddings:
262
+ return []
263
+ import torch
264
+
265
+ tokens = self.sparse_tokenizer(
266
+ inputs, return_tensors="pt", truncation=True, padding=True
267
+ )
268
+ output = self.sparse_model(**tokens)
269
+ vectors = torch.max(
270
+ torch.log(torch.relu(output.logits) + torch.tensor(1.0))
271
+ * tokens.attention_mask.unsqueeze(-1),
272
+ dim=1,
273
+ )[0].squeeze(dim=1)
274
+ sparse_embeddings = []
275
+ for vec in vectors:
276
+ cols = vec.nonzero().squeeze().cpu().tolist()
277
+ weights = vec[cols].cpu().tolist()
278
+ sparse_embeddings.append(
279
+ SparseVector(
280
+ indices=cols,
281
+ values=weights,
282
+ )
283
+ )
284
+ return sparse_embeddings
285
+
224
286
  def add_documents(self, documents: Sequence[Document]) -> None:
225
287
  # Add id to metadata if not already present
226
288
  super().maybe_add_ids(documents)
@@ -232,6 +294,9 @@ class QdrantDB(VectorStore):
232
294
  return
233
295
  document_dicts = [doc.dict() for doc in documents]
234
296
  embedding_vecs = self.embedding_fn([doc.content for doc in documents])
297
+ sparse_embedding_vecs = self.get_sparse_embeddings(
298
+ [doc.content for doc in documents]
299
+ )
235
300
  if self.config.collection_name is None:
236
301
  raise ValueError("No collection name set, cannot ingest docs")
237
302
  if self.config.collection_name not in colls:
@@ -241,11 +306,16 @@ class QdrantDB(VectorStore):
241
306
  # else we get an API error
242
307
  b = self.config.batch_size
243
308
  for i in range(0, len(ids), b):
309
+ vectors: Dict[str, Embeddings | List[SparseVector]] = {
310
+ "": embedding_vecs[i : i + b]
311
+ }
312
+ if self.config.use_sparse_embeddings:
313
+ vectors["text-sparse"] = sparse_embedding_vecs[i : i + b]
244
314
  self.client.upsert(
245
315
  collection_name=self.config.collection_name,
246
316
  points=Batch(
247
317
  ids=ids[i : i + b],
248
- vectors=embedding_vecs[i : i + b],
318
+ vectors=vectors,
249
319
  payloads=document_dicts[i : i + b],
250
320
  ),
251
321
  )
@@ -332,18 +402,39 @@ class QdrantDB(VectorStore):
332
402
  filter = Filter()
333
403
  else:
334
404
  filter = Filter.parse_obj(json.loads(where))
405
+ requests = [
406
+ SearchRequest(
407
+ vector=NamedVector(
408
+ name="",
409
+ vector=embedding,
410
+ ),
411
+ limit=k,
412
+ with_payload=True,
413
+ filter=filter,
414
+ )
415
+ ]
416
+ if self.config.use_sparse_embeddings:
417
+ sparse_embedding = self.get_sparse_embeddings([text])[0]
418
+ requests.append(
419
+ SearchRequest(
420
+ vector=NamedSparseVector(
421
+ name="text-sparse",
422
+ vector=sparse_embedding,
423
+ ),
424
+ limit=self.config.sparse_limit,
425
+ with_payload=True,
426
+ filter=filter,
427
+ )
428
+ )
335
429
  if self.config.collection_name is None:
336
430
  raise ValueError("No collection name set, cannot search")
337
- search_result: List[ScoredPoint] = self.client.search(
338
- collection_name=self.config.collection_name,
339
- query_vector=embedding,
340
- query_filter=filter,
341
- limit=k,
342
- search_params=SearchParams(
343
- hnsw_ef=128,
344
- exact=False, # use Apx NN, not exact NN
345
- ),
431
+ search_result_lists: List[List[ScoredPoint]] = self.client.search_batch(
432
+ collection_name=self.config.collection_name, requests=requests
346
433
  )
434
+
435
+ search_result = [
436
+ match for result in search_result_lists for match in result
437
+ ] # 2D list -> 1D list
347
438
  scores = [match.score for match in search_result if match is not None]
348
439
  docs = [
349
440
  Document(**(match.payload)) # type: ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langroid
3
- Version: 0.1.239
3
+ Version: 0.1.241
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  License: MIT
6
6
  Author: Prasad Chalasani
@@ -36,6 +36,7 @@ Requires-Dist: fakeredis (>=2.12.1,<3.0.0)
36
36
  Requires-Dist: fire (>=0.5.0,<0.6.0)
37
37
  Requires-Dist: flake8 (>=6.0.0,<7.0.0)
38
38
  Requires-Dist: google-api-python-client (>=2.95.0,<3.0.0)
39
+ Requires-Dist: google-generativeai (>=0.5.2,<0.6.0)
39
40
  Requires-Dist: groq (>=0.5.0,<0.6.0)
40
41
  Requires-Dist: grpcio (>=1.62.1,<2.0.0)
41
42
  Requires-Dist: halo (>=0.0.31,<0.0.32)
@@ -99,8 +100,9 @@ Requires-Dist: sqlalchemy (>=2.0.19,<3.0.0)
99
100
  Requires-Dist: tantivy (>=0.21.0,<0.22.0)
100
101
  Requires-Dist: thefuzz (>=0.20.0,<0.21.0)
101
102
  Requires-Dist: tiktoken (>=0.5.1,<0.6.0)
102
- Requires-Dist: torch (==2.0.0) ; extra == "hf-embeddings"
103
+ Requires-Dist: torch (==2.0.0) ; extra == "hf-embeddings" or extra == "transformers"
103
104
  Requires-Dist: trafilatura (>=1.5.0,<2.0.0)
105
+ Requires-Dist: transformers (>=4.40.1,<5.0.0) ; extra == "transformers"
104
106
  Requires-Dist: typer (>=0.9.0,<0.10.0)
105
107
  Requires-Dist: types-pillow (>=10.2.0.20240406,<11.0.0.0)
106
108
  Requires-Dist: types-pyyaml (>=6.0.12.20240311,<7.0.0.0)
@@ -63,7 +63,7 @@ langroid/language_models/azure_openai.py,sha256=ncRCbKooqLVOY-PWQUIo9C3yTuKEFbAw
63
63
  langroid/language_models/base.py,sha256=B6dX43ZR65mIvjD95W4RcfpT-WpmiuEcstR3eMrr56Y,21029
64
64
  langroid/language_models/config.py,sha256=5UF3DzO1a-Dfsc3vghE0XGq7g9t_xDsRCsuRiU4dgBg,366
65
65
  langroid/language_models/openai_assistants.py,sha256=9K-DEAL2aSWHeXj2hwCo2RAlK9_1oCPtqX2u1wISCj8,36
66
- langroid/language_models/openai_gpt.py,sha256=BOZt2lOFViN3ct-jvfELRKeUkUaBOGhGxO7F6JQNCNY,50257
66
+ langroid/language_models/openai_gpt.py,sha256=ueB9MJzxGBIjN_i22v2ZqoWo_twnQan_s4p0dzf4zzI,50505
67
67
  langroid/language_models/prompt_formatter/__init__.py,sha256=9JXFF22QNMmbQV1q4nrIeQVTtA3Tx8tEZABLtLBdFyc,352
68
68
  langroid/language_models/prompt_formatter/base.py,sha256=eDS1sgRNZVnoajwV_ZIha6cba5Dt8xjgzdRbPITwx3Q,1221
69
69
  langroid/language_models/prompt_formatter/hf_formatter.py,sha256=TFL6ppmeQWnzr6CKQzRZFYY810zE1mr8DZnhw6i85ok,5217
@@ -120,8 +120,8 @@ langroid/vector_store/lancedb.py,sha256=lbl8wZuV6GNw0LnIwOSriSNwoMEba90umQTcQHtM
120
120
  langroid/vector_store/meilisearch.py,sha256=d2huA9P-NoYRuAQ9ZeXJmMKr7ry8u90RUSR28k2ecQg,11340
121
121
  langroid/vector_store/momento.py,sha256=9cui31TTrILid2KIzUpBkN2Ey3g_CZWOQVdaFsA4Ors,10045
122
122
  langroid/vector_store/qdrant_cloud.py,sha256=3im4Mip0QXLkR6wiqVsjV1QvhSElfxdFSuDKddBDQ-4,188
123
- langroid/vector_store/qdrantdb.py,sha256=foKRxRv0BBony6S4Vt0Vav9Rn9HMxZvcIh1cE7nosFE,13524
124
- langroid-0.1.239.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
125
- langroid-0.1.239.dist-info/METADATA,sha256=OqbY4y93jSmtPY7XjpgI_VLm3G6tromEe-MmtKMsDVE,49012
126
- langroid-0.1.239.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
127
- langroid-0.1.239.dist-info/RECORD,,
123
+ langroid/vector_store/qdrantdb.py,sha256=sk5Qb2ZNbooi0rorsMuqIMokF7WADw6PJ0D6goM2XBw,16802
124
+ langroid-0.1.241.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
125
+ langroid-0.1.241.dist-info/METADATA,sha256=3-gQbFV94rqEec_esydP028p-Ol3w2J9mbrcupeq0Xg,49163
126
+ langroid-0.1.241.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
127
+ langroid-0.1.241.dist-info/RECORD,,