langroid 0.39.5__py3-none-any.whl → 0.41.0__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.
@@ -0,0 +1,422 @@
1
+ import hashlib
2
+ import json
3
+ import logging
4
+ import os
5
+ import uuid
6
+ from typing import Any, Dict, List, Optional, Sequence, Tuple
7
+
8
+ from langroid.embedding_models.base import (
9
+ EmbeddingModelsConfig,
10
+ )
11
+ from langroid.embedding_models.models import OpenAIEmbeddingsConfig
12
+ from langroid.exceptions import LangroidImportError
13
+ from langroid.mytypes import DocMetaData, Document
14
+ from langroid.vector_store.base import VectorStore, VectorStoreConfig
15
+
16
+ has_postgres: bool = True
17
+ try:
18
+ from sqlalchemy import (
19
+ Column,
20
+ MetaData,
21
+ String,
22
+ Table,
23
+ case,
24
+ create_engine,
25
+ inspect,
26
+ text,
27
+ )
28
+ from sqlalchemy.dialects.postgresql import JSONB
29
+ from sqlalchemy.engine import Connection, Engine
30
+ from sqlalchemy.orm import sessionmaker
31
+ from sqlalchemy.sql.expression import insert
32
+ except ImportError:
33
+ Engine = Any # type: ignore
34
+ Connection = Any # type: ignore
35
+ has_postgres = False
36
+
37
+ logger = logging.getLogger(__name__)
38
+
39
+
40
+ class PostgresDBConfig(VectorStoreConfig):
41
+ collection_name: str = "embeddings"
42
+ cloud: bool = False
43
+ docker: bool = True
44
+ host: str = "127.0.0.1"
45
+ port: int = 5432
46
+ replace_collection: bool = False
47
+ embedding: EmbeddingModelsConfig = OpenAIEmbeddingsConfig()
48
+ pool_size: int = 10
49
+ max_overflow: int = 20
50
+ hnsw_m: int = 16
51
+ hnsw_ef_construction: int = 200
52
+
53
+
54
+ class PostgresDB(VectorStore):
55
+ def __init__(self, config: PostgresDBConfig = PostgresDBConfig()):
56
+ super().__init__(config)
57
+ if not has_postgres:
58
+ raise LangroidImportError("pgvector", "postgres")
59
+ self.config: PostgresDBConfig = config
60
+ self.engine = self._create_engine()
61
+ PostgresDB._create_vector_extension(self.engine)
62
+ self.SessionLocal = sessionmaker(
63
+ autocommit=False, autoflush=False, bind=self.engine
64
+ )
65
+ self.metadata = MetaData()
66
+ self._setup_table()
67
+
68
+ def _create_engine(self) -> Engine:
69
+ """Creates a SQLAlchemy engine based on the configuration."""
70
+
71
+ connection_string: str | None = None # Ensure variable is always defined
72
+
73
+ if self.config.cloud:
74
+ connection_string = os.getenv("POSTGRES_CONNECTION_STRING")
75
+
76
+ if connection_string and connection_string.startswith("postgres://"):
77
+ connection_string = connection_string.replace(
78
+ "postgres://", "postgresql+psycopg2://", 1
79
+ )
80
+ elif not connection_string:
81
+ raise ValueError("Provide the POSTGRES_CONNECTION_STRING.")
82
+
83
+ elif self.config.docker:
84
+ username = os.getenv("POSTGRES_USER", "postgres")
85
+ password = os.getenv("POSTGRES_PASSWORD", "postgres")
86
+ database = os.getenv("POSTGRES_DB", "langroid")
87
+
88
+ if not (username and password and database):
89
+ raise ValueError(
90
+ "Provide POSTGRES_USER, POSTGRES_PASSWORD, " "POSTGRES_DB. "
91
+ )
92
+
93
+ connection_string = (
94
+ f"postgresql+psycopg2://{username}:{password}@"
95
+ f"{self.config.host}:{self.config.port}/{database}"
96
+ )
97
+ self.config.cloud = False # Ensures cloud is disabled if using Docker
98
+
99
+ else:
100
+ raise ValueError(
101
+ "Provide either Docker or Cloud config to connect to the database."
102
+ )
103
+
104
+ return create_engine(
105
+ connection_string,
106
+ pool_size=self.config.pool_size,
107
+ max_overflow=self.config.max_overflow,
108
+ )
109
+
110
+ def _setup_table(self) -> None:
111
+ try:
112
+ from pgvector.sqlalchemy import Vector
113
+ except ImportError as e:
114
+ raise LangroidImportError(extra="postgres", error=str(e))
115
+
116
+ if self.config.replace_collection:
117
+ self.delete_collection(self.config.collection_name)
118
+
119
+ self.embeddings_table = Table(
120
+ self.config.collection_name,
121
+ self.metadata,
122
+ Column("id", String, primary_key=True, nullable=False, unique=True),
123
+ Column("embedding", Vector(self.embedding_dim)),
124
+ Column("document", String),
125
+ Column("cmetadata", JSONB),
126
+ extend_existing=True,
127
+ )
128
+
129
+ self.metadata.create_all(self.engine)
130
+ self.metadata.reflect(bind=self.engine, only=[self.config.collection_name])
131
+
132
+ # Create HNSW index for embeddings column if it doesn't exist.
133
+ # This index enables efficient nearest-neighbor search using cosine similarity.
134
+ # PostgreSQL automatically builds the index after creation;
135
+ # no manual step required.
136
+ # Read more about pgvector hnsw index here:
137
+ # https://github.com/pgvector/pgvector?tab=readme-ov-file#hnsw
138
+
139
+ index_name = f"hnsw_index_{self.config.collection_name}_embedding"
140
+ with self.engine.connect() as connection:
141
+ if not self.index_exists(connection, index_name):
142
+ connection.execute(text("COMMIT"))
143
+ create_index_query = text(
144
+ f"""
145
+ CREATE INDEX CONCURRENTLY IF NOT EXISTS {index_name}
146
+ ON {self.config.collection_name}
147
+ USING hnsw (embedding vector_cosine_ops)
148
+ WITH (
149
+ m = {self.config.hnsw_m},
150
+ ef_construction = {self.config.hnsw_ef_construction}
151
+ );
152
+ """
153
+ )
154
+ connection.execute(create_index_query)
155
+
156
+ def index_exists(self, connection: Connection, index_name: str) -> bool:
157
+ """Check if an index exists."""
158
+ query = text(
159
+ "SELECT 1 FROM pg_indexes WHERE indexname = :index_name"
160
+ ).bindparams(index_name=index_name)
161
+ result = connection.execute(query).scalar()
162
+ return bool(result)
163
+
164
+ @staticmethod
165
+ def _create_vector_extension(conn: Engine) -> None:
166
+
167
+ with conn.connect() as connection:
168
+ with connection.begin():
169
+ # The number is a unique identifier used to lock a specific resource
170
+ # during transaction. Any 64-bit integer can be used for advisory locks.
171
+ # Acquire advisory lock to ensure atomic, isolated setup
172
+ # and prevent race conditions.
173
+
174
+ statement = text(
175
+ "SELECT pg_advisory_xact_lock(1573678846307946496);"
176
+ "CREATE EXTENSION IF NOT EXISTS vector;"
177
+ )
178
+ connection.execute(statement)
179
+
180
+ def set_collection(self, collection_name: str, replace: bool = False) -> None:
181
+ inspector = inspect(self.engine)
182
+ table_exists = collection_name in inspector.get_table_names()
183
+
184
+ if (
185
+ collection_name == self.config.collection_name
186
+ and table_exists
187
+ and not replace
188
+ ):
189
+ return
190
+ else:
191
+ self.config.collection_name = collection_name
192
+ self.config.replace_collection = replace
193
+ self._setup_table()
194
+
195
+ def list_collections(self, empty: bool = True) -> List[str]:
196
+ inspector = inspect(self.engine)
197
+ table_names = inspector.get_table_names()
198
+
199
+ with self.SessionLocal() as session:
200
+ collections = []
201
+ for table_name in table_names:
202
+ table = Table(table_name, self.metadata, autoload_with=self.engine)
203
+ if empty:
204
+ collections.append(table_name)
205
+ else:
206
+ # Efficiently check for non-emptiness
207
+ if session.query(table.select().limit(1).exists()).scalar():
208
+ collections.append(table_name)
209
+ return collections
210
+
211
+ def create_collection(self, collection_name: str, replace: bool = False) -> None:
212
+ self.set_collection(collection_name, replace=replace)
213
+
214
+ def delete_collection(self, collection_name: str) -> None:
215
+ """
216
+ Deletes a collection and its associated HNSW index, handling metadata
217
+ synchronization issues.
218
+ """
219
+ with self.engine.connect() as connection:
220
+ connection.execute(text("COMMIT"))
221
+ index_name = f"hnsw_index_{collection_name}_embedding"
222
+ drop_index_query = text(f"DROP INDEX CONCURRENTLY IF EXISTS {index_name}")
223
+ connection.execute(drop_index_query)
224
+
225
+ # 3. Now, drop the table using SQLAlchemy
226
+ table = Table(collection_name, self.metadata)
227
+ table.drop(self.engine, checkfirst=True)
228
+
229
+ # 4. Refresh metadata again after dropping the table
230
+ self.metadata.clear()
231
+ self.metadata.reflect(bind=self.engine)
232
+
233
+ def clear_all_collections(self, really: bool = False, prefix: str = "") -> int:
234
+ if not really:
235
+ logger.warning("Not deleting all tables, set really=True to confirm")
236
+ return 0
237
+
238
+ inspector = inspect(self.engine)
239
+ table_names = inspector.get_table_names()
240
+
241
+ with self.SessionLocal() as session:
242
+ deleted_count = 0
243
+ for table_name in table_names:
244
+ if table_name.startswith(prefix):
245
+ # Use delete_collection to handle index and table deletion
246
+ self.delete_collection(table_name)
247
+ deleted_count += 1
248
+ session.commit()
249
+ logger.warning(f"Deleted {deleted_count} tables with prefix '{prefix}'.")
250
+ return deleted_count
251
+
252
+ def clear_empty_collections(self) -> int:
253
+ inspector = inspect(self.engine)
254
+ table_names = inspector.get_table_names()
255
+
256
+ with self.SessionLocal() as session:
257
+ deleted_count = 0
258
+ for table_name in table_names:
259
+ table = Table(table_name, self.metadata, autoload_with=self.engine)
260
+
261
+ # Efficiently check for emptiness without fetching all rows
262
+ if session.query(table.select().limit(1).exists()).scalar():
263
+ continue
264
+
265
+ # Use delete_collection to handle index and table deletion
266
+ self.delete_collection(table_name)
267
+ deleted_count += 1
268
+
269
+ session.commit() # Commit is likely not needed here
270
+ logger.warning(f"Deleted {deleted_count} empty tables.")
271
+ return deleted_count
272
+
273
+ def _parse_embedding_store_record(self, res: Any) -> Dict[str, Any]:
274
+ metadata = res.cmetadata or {}
275
+ metadata["id"] = res.id
276
+ return {
277
+ "content": res.document,
278
+ "metadata": DocMetaData(**metadata),
279
+ }
280
+
281
+ def get_all_documents(self, where: str = "") -> List[Document]:
282
+ with self.SessionLocal() as session:
283
+ query = session.query(self.embeddings_table)
284
+
285
+ # Apply 'where' clause if provided
286
+ if where:
287
+ try:
288
+ where_json = json.loads(where)
289
+ query = query.filter(
290
+ self.embeddings_table.c.cmetadata.contains(where_json)
291
+ )
292
+ except json.JSONDecodeError:
293
+ logger.error(f"Invalid JSON in 'where' clause: {where}")
294
+ return [] # Return empty list or handle error as appropriate
295
+
296
+ results = query.all()
297
+ documents = [
298
+ Document(**self._parse_embedding_store_record(res)) for res in results
299
+ ]
300
+ return documents
301
+
302
+ def get_documents_by_ids(self, ids: List[str]) -> List[Document]:
303
+ with self.SessionLocal() as session:
304
+ # Add a CASE statement to preserve the order of IDs
305
+ case_stmt = case(
306
+ {id_: index for index, id_ in enumerate(ids)},
307
+ value=self.embeddings_table.c.id,
308
+ )
309
+
310
+ query = (
311
+ session.query(self.embeddings_table)
312
+ .filter(self.embeddings_table.c.id.in_(ids))
313
+ .order_by(case_stmt) # Order by the CASE statement
314
+ )
315
+ results = query.all()
316
+
317
+ documents = [
318
+ Document(**self._parse_embedding_store_record(row)) for row in results
319
+ ]
320
+ return documents
321
+
322
+ def add_documents(self, documents: Sequence[Document]) -> None:
323
+ super().maybe_add_ids(documents)
324
+ for doc in documents:
325
+ doc.metadata.id = str(PostgresDB._id_to_uuid(doc.metadata.id, doc.metadata))
326
+
327
+ embeddings = self.embedding_fn([doc.content for doc in documents])
328
+
329
+ batch_size = self.config.batch_size
330
+ with self.SessionLocal() as session:
331
+ for i in range(0, len(documents), batch_size):
332
+ batch_docs = documents[i : i + batch_size]
333
+ batch_embeddings = embeddings[i : i + batch_size]
334
+
335
+ new_records = [
336
+ {
337
+ "id": doc.metadata.id,
338
+ "embedding": embedding,
339
+ "document": doc.content,
340
+ "cmetadata": doc.metadata.dict(),
341
+ }
342
+ for doc, embedding in zip(batch_docs, batch_embeddings)
343
+ ]
344
+
345
+ if new_records:
346
+ stmt = insert(self.embeddings_table).values(new_records)
347
+ session.execute(stmt)
348
+ session.commit()
349
+
350
+ @staticmethod
351
+ def _id_to_uuid(id: str, obj: object) -> str:
352
+ try:
353
+ doc_id = str(uuid.UUID(id))
354
+ except ValueError:
355
+ obj_repr = repr(obj)
356
+
357
+ obj_hash = hashlib.sha256(obj_repr.encode()).hexdigest()
358
+
359
+ combined = f"{id}-{obj_hash}"
360
+
361
+ doc_id = str(uuid.uuid5(uuid.NAMESPACE_DNS, combined))
362
+
363
+ return doc_id
364
+
365
+ def similar_texts_with_scores(
366
+ self,
367
+ query: str,
368
+ k: int = 1,
369
+ where: Optional[str] = None,
370
+ neighbors: int = 1, # Parameter not used in this implementation
371
+ ) -> List[Tuple[Document, float]]:
372
+ embedding = self.embedding_fn([query])[0]
373
+
374
+ with self.SessionLocal() as session:
375
+ # Calculate the score (1 - cosine_distance) and label it as "score"
376
+ score = (
377
+ 1 - (self.embeddings_table.c.embedding.cosine_distance(embedding))
378
+ ).label("score")
379
+
380
+ if where is not None:
381
+ try:
382
+ json_query = json.loads(where)
383
+ except json.JSONDecodeError:
384
+ raise ValueError(f"Invalid JSON in 'where' clause: {where}")
385
+
386
+ results = (
387
+ session.query(
388
+ self.embeddings_table.c.id,
389
+ self.embeddings_table.c.document,
390
+ self.embeddings_table.c.cmetadata,
391
+ score, # Select the calculated score
392
+ )
393
+ .filter(self.embeddings_table.c.cmetadata.contains(json_query))
394
+ .order_by(score.desc()) # Order by score in descending order
395
+ .limit(k)
396
+ .all()
397
+ )
398
+ else:
399
+ results = (
400
+ session.query(
401
+ self.embeddings_table.c.id,
402
+ self.embeddings_table.c.document,
403
+ self.embeddings_table.c.cmetadata,
404
+ score, # Select the calculated score
405
+ )
406
+ .order_by(score.desc()) # Order by score in descending order
407
+ .limit(k)
408
+ .all()
409
+ )
410
+
411
+ documents_with_scores = [
412
+ (
413
+ Document(
414
+ content=result.document,
415
+ metadata=DocMetaData(**(result.cmetadata or {})),
416
+ ),
417
+ result.score, # Use the score from the query result
418
+ )
419
+ for result in results
420
+ ]
421
+
422
+ return documents_with_scores
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
  import os
3
3
  import re
4
- from typing import Any, List, Optional, Sequence, Tuple
4
+ from typing import TYPE_CHECKING, Any, List, Optional, Sequence, Tuple
5
5
 
6
6
  from dotenv import load_dotenv
7
7
 
@@ -15,6 +15,7 @@ from langroid.utils.configuration import settings
15
15
  from langroid.vector_store.base import VectorStore, VectorStoreConfig
16
16
 
17
17
  logger = logging.getLogger(__name__)
18
+ has_weaviate: bool = True
18
19
  try:
19
20
  import weaviate
20
21
  from weaviate.classes.config import (
@@ -25,7 +26,18 @@ try:
25
26
  from weaviate.classes.query import Filter, MetadataQuery
26
27
  from weaviate.util import generate_uuid5, get_valid_uuid
27
28
  except ImportError:
28
- raise LangroidImportError("weaviate", "weaviate")
29
+ has_weaviate = False
30
+
31
+ if not TYPE_CHECKING:
32
+
33
+ class VectorDistances:
34
+ """
35
+ Fallback class when weaviate is not installed, to avoid import errors.
36
+ """
37
+
38
+ COSINE: str = "cosine"
39
+ DOTPRODUCT: str = "dot"
40
+ L2: str = "l2"
29
41
 
30
42
 
31
43
  class WeaviateDBConfig(VectorStoreConfig):
@@ -39,6 +51,8 @@ class WeaviateDBConfig(VectorStoreConfig):
39
51
  class WeaviateDB(VectorStore):
40
52
  def __init__(self, config: WeaviateDBConfig = WeaviateDBConfig()):
41
53
  super().__init__(config)
54
+ if not has_weaviate:
55
+ raise LangroidImportError("weaviate", "weaviate")
42
56
  self.config: WeaviateDBConfig = config
43
57
  load_dotenv()
44
58
  if not self.config.cloud:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.39.5
3
+ Version: 0.41.0
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -15,6 +15,7 @@ Requires-Dist: colorlog<7.0.0,>=6.7.0
15
15
  Requires-Dist: docling<3.0.0,>=2.16.0
16
16
  Requires-Dist: docstring-parser<1.0,>=0.16
17
17
  Requires-Dist: duckduckgo-search<7.0.0,>=6.0.0
18
+ Requires-Dist: exa-py>=1.8.7
18
19
  Requires-Dist: faker<19.0.0,>=18.9.0
19
20
  Requires-Dist: fakeredis<3.0.0,>=2.12.1
20
21
  Requires-Dist: fire<1.0.0,>=0.5.0
@@ -48,6 +49,7 @@ Requires-Dist: redis<6.0.0,>=5.0.1
48
49
  Requires-Dist: requests-oauthlib<2.0.0,>=1.3.1
49
50
  Requires-Dist: requests<3.0.0,>=2.31.0
50
51
  Requires-Dist: rich<14.0.0,>=13.3.4
52
+ Requires-Dist: tavily-python>=0.5.0
51
53
  Requires-Dist: thefuzz<1.0.0,>=0.20.0
52
54
  Requires-Dist: tiktoken<1.0.0,>=0.7.0
53
55
  Requires-Dist: trafilatura<2.0.0,>=1.5.0
@@ -64,6 +66,8 @@ Requires-Dist: litellm<2.0.0,>=1.30.1; extra == 'all'
64
66
  Requires-Dist: metaphor-python<0.2.0,>=0.1.23; extra == 'all'
65
67
  Requires-Dist: neo4j<6.0.0,>=5.14.1; extra == 'all'
66
68
  Requires-Dist: pdf2image<2.0.0,>=1.17.0; extra == 'all'
69
+ Requires-Dist: pgvector>=0.3.6; extra == 'all'
70
+ Requires-Dist: psycopg2-binary>=2.9.10; extra == 'all'
67
71
  Requires-Dist: psycopg2<3.0.0,>=2.9.7; extra == 'all'
68
72
  Requires-Dist: pymupdf4llm<0.1.0,>=0.0.17; extra == 'all'
69
73
  Requires-Dist: pymupdf<2.0.0,>=1.23.3; extra == 'all'
@@ -104,6 +108,8 @@ Provides-Extra: docling
104
108
  Requires-Dist: docling<3.0.0,>=2.16.0; extra == 'docling'
105
109
  Provides-Extra: docx
106
110
  Requires-Dist: python-docx<2.0.0,>=1.1.0; extra == 'docx'
111
+ Provides-Extra: exa
112
+ Requires-Dist: exa-py>=1.8.7; extra == 'exa'
107
113
  Provides-Extra: fastembed
108
114
  Requires-Dist: fastembed<0.4.0,>=0.3.1; extra == 'fastembed'
109
115
  Provides-Extra: google-generativeai
@@ -139,8 +145,13 @@ Requires-Dist: pymupdf4llm<0.1.0,>=0.0.17; extra == 'pdf-parsers'
139
145
  Requires-Dist: pymupdf<2.0.0,>=1.23.3; extra == 'pdf-parsers'
140
146
  Requires-Dist: pypdf>=5.1.0; extra == 'pdf-parsers'
141
147
  Requires-Dist: pytesseract<0.4.0,>=0.3.10; extra == 'pdf-parsers'
148
+ Provides-Extra: pinecone
149
+ Requires-Dist: pinecone-client>=5.0.1; extra == 'pinecone'
142
150
  Provides-Extra: postgres
151
+ Requires-Dist: pgvector>=0.3.6; extra == 'postgres'
152
+ Requires-Dist: psycopg2-binary>=2.9.10; extra == 'postgres'
143
153
  Requires-Dist: psycopg2<3.0.0,>=2.9.7; extra == 'postgres'
154
+ Requires-Dist: sqlalchemy<3.0.0,>=2.0.19; extra == 'postgres'
144
155
  Provides-Extra: pymupdf4llm
145
156
  Requires-Dist: pymupdf4llm<0.1.0,>=0.0.17; extra == 'pymupdf4llm'
146
157
  Provides-Extra: scrapy
@@ -149,6 +160,8 @@ Provides-Extra: sql
149
160
  Requires-Dist: psycopg2<3.0.0,>=2.9.7; extra == 'sql'
150
161
  Requires-Dist: pymysql<2.0.0,>=1.1.0; extra == 'sql'
151
162
  Requires-Dist: sqlalchemy<3.0.0,>=2.0.19; extra == 'sql'
163
+ Provides-Extra: tavily
164
+ Requires-Dist: tavily-python>=0.5.0; extra == 'tavily'
152
165
  Provides-Extra: transformers
153
166
  Requires-Dist: huggingface-hub<1.0.0,>=0.21.2; extra == 'transformers'
154
167
  Requires-Dist: torch<3.0.0,>=2.0.0; extra == 'transformers'
@@ -158,6 +171,7 @@ Requires-Dist: unstructured[docx,pdf,pptx]<1.0.0,>=0.16.15; extra == 'unstructur
158
171
  Provides-Extra: vecdbs
159
172
  Requires-Dist: chromadb<=0.4.23,>=0.4.21; extra == 'vecdbs'
160
173
  Requires-Dist: lancedb<0.9.0,>=0.8.2; extra == 'vecdbs'
174
+ Requires-Dist: pinecone-client>=5.0.1; extra == 'vecdbs'
161
175
  Requires-Dist: pyarrow<16.0.0,>=15.0.0; extra == 'vecdbs'
162
176
  Requires-Dist: tantivy<0.22.0,>=0.21.0; extra == 'vecdbs'
163
177
  Requires-Dist: weaviate-client>=4.9.6; extra == 'vecdbs'
@@ -43,6 +43,7 @@ langroid/agent/special/sql/utils/system_message.py,sha256=qKLHkvQWRQodTtPLPxr1GS
43
43
  langroid/agent/special/sql/utils/tools.py,sha256=ovCePzq5cmbqw0vsVPBzxdZpUcSUIfTiDSMGXustZW8,1749
44
44
  langroid/agent/tools/__init__.py,sha256=IMgCte-_ZIvCkozGQmvMqxIw7_nKLKzD78ccJL1bnQU,804
45
45
  langroid/agent/tools/duckduckgo_search_tool.py,sha256=NhsCaGZkdv28nja7yveAhSK_w6l_Ftym8agbrdzqgfo,1935
46
+ langroid/agent/tools/exa_search_tool.py,sha256=qxDs6vIiUtFyfX6gmS-PxoCXes-55in3ef5AkUQhiM0,2469
46
47
  langroid/agent/tools/file_tools.py,sha256=GjPB5YDILucYapElnvvoYpGJuZQ25ecLs2REv7edPEo,7292
47
48
  langroid/agent/tools/google_search_tool.py,sha256=y7b-3FtgXf0lfF4AYxrZ3K5pH2dhidvibUOAGBE--WI,1456
48
49
  langroid/agent/tools/metaphor_search_tool.py,sha256=ccyEhkShH5MxW6-sx1n0BLpD_GForQddS_nNvBZ67Ik,2561
@@ -51,6 +52,7 @@ langroid/agent/tools/recipient_tool.py,sha256=dr0yTxgNEIoxUYxH6TtaExC4G_8WdJ0xGo
51
52
  langroid/agent/tools/retrieval_tool.py,sha256=zcAV20PP_6VzSd-UE-IJcabaBseFL_QNz59Bnig8-lE,946
52
53
  langroid/agent/tools/rewind_tool.py,sha256=XAXL3BpNhCmBGYq_qi_sZfHJuIw7NY2jp4wnojJ7WRs,5606
53
54
  langroid/agent/tools/segment_extract_tool.py,sha256=__srZ_VGYLVOdPrITUM8S0HpmX4q7r5FHWMDdHdEv8w,1440
55
+ langroid/agent/tools/tavily_search_tool.py,sha256=soI-j0HdgVQLf09wRQScaEK4b5RpAX9C4cwOivRFWWI,1903
54
56
  langroid/cachedb/__init__.py,sha256=icAT2s7Vhf-ZGUeqpDQGNU6ob6o0aFEyjwcxxUGRFjg,225
55
57
  langroid/cachedb/base.py,sha256=ztVjB1DtN6pLCujCWnR6xruHxwVj3XkYniRTYAKKqk0,1354
56
58
  langroid/cachedb/momento_cachedb.py,sha256=YEOJ62hEcV6iIeMr5aGgRYgWQqFYaej9gEDEcY0sm7M,3172
@@ -86,13 +88,13 @@ langroid/parsing/parser.py,sha256=pPzM3zXQvFtwTyQPtDha15oZhu1O3OKDLECnkB8waxg,12
86
88
  langroid/parsing/pdf_utils.py,sha256=rmNJ9UzuBgXTAYwj1TtRJcD8h53x7cizhgyYHKO88I4,1513
87
89
  langroid/parsing/repo_loader.py,sha256=3GjvPJS6Vf5L6gV2zOU8s-Tf1oq_fZm-IB_RL_7CTsY,29373
88
90
  langroid/parsing/routing.py,sha256=-FcnlqldzL4ZoxuDwXjQPNHgBe9F9-F4R6q7b_z9CvI,1232
89
- langroid/parsing/search.py,sha256=M1swZfZEMEsalmTwVCkql3DzBNBemky7pWt0PrcwGdQ,9779
91
+ langroid/parsing/search.py,sha256=YPCwezM0c4PWbNUMEmQ5RrJBtvX4aWZ1CMCJFs4sqFo,9806
90
92
  langroid/parsing/spider.py,sha256=hAVM6wxh1pQ0EN4tI5wMBtAjIk0T-xnpi-ZUzWybhos,3258
91
93
  langroid/parsing/table_loader.py,sha256=qNM4obT_0Y4tjrxNBCNUYjKQ9oETCZ7FbolKBTcz-GM,3410
92
94
  langroid/parsing/url_loader.py,sha256=JK48KktLRDBfjrt4nsUfy92M6yGdEeicAqOum2MdULM,4656
93
95
  langroid/parsing/urls.py,sha256=86omykgxo4hg2jyF10Ef-FJa9n6MgXdSXy2mImqgo5c,8076
94
- langroid/parsing/utils.py,sha256=YrV2GNL4EOBGknA4AClPGdJ4S5B31radrt-Ou8OAKoU,12749
95
- langroid/parsing/web_search.py,sha256=8rW8EI3tyHITaB2l9MT_6yLMeQfo8y-Ih-8N2v2uMpk,4931
96
+ langroid/parsing/utils.py,sha256=ZWMS7oG04GUY9EAIwnFN6KKo_ePCKhqk_H8jW6TDT0s,12805
97
+ langroid/parsing/web_search.py,sha256=wWSmV0METFTGPhHJIs-M4tog2Aur_75Pxr4a49cKDkU,7042
96
98
  langroid/prompts/__init__.py,sha256=RW11vK6jiLPuaUh4GpeFvstti73gkm8_rDMtrbo2YsU,142
97
99
  langroid/prompts/dialog.py,sha256=SpfiSyofSgy2pwD1YboHR_yHO3LEEMbv6j2sm874jKo,331
98
100
  langroid/prompts/prompts_config.py,sha256=p_lp9nbMuQwhhMwAZsOxveRw9C0ZFZvql7pdIfgVZYo,143
@@ -116,15 +118,17 @@ langroid/utils/output/__init__.py,sha256=7P0f--4IZneNsTxXY5fd6d6iW-CeVe-KSsl-87s
116
118
  langroid/utils/output/citations.py,sha256=9T69O_N6mxPQjQ-qC1vKS8_kyg1z5hDQXMhBsA45xkk,3147
117
119
  langroid/utils/output/printing.py,sha256=yzPJZN-8_jyOJmI9N_oLwEDfjMwVgk3IDiwnZ4eK_AE,2962
118
120
  langroid/utils/output/status.py,sha256=rzbE7mDJcgNNvdtylCseQcPGCGghtJvVq3lB-OPJ49E,1049
119
- langroid/vector_store/__init__.py,sha256=BcoOm1tG3y0EqjkIGmMOHkY9iTUhDHgyruknWDKgqIg,1214
120
- langroid/vector_store/base.py,sha256=69keYWkUD0fcGXC0STcdO1-jn8H4Ez-L_fnxmRvUoNw,14412
121
+ langroid/vector_store/__init__.py,sha256=8ktJUVsVUoc7FMmkUFpFBZu7VMWUqQY9zpm4kEJ8yTs,1537
122
+ langroid/vector_store/base.py,sha256=BgQzTScKNzKr3F3o9jrQNG-b3Dv16wKEGSM9jg-W03Y,14752
121
123
  langroid/vector_store/chromadb.py,sha256=p9mEqJwO2BrL2jSSXfa23kCPlPOwWpF3xJYd5zoWw_c,8661
122
124
  langroid/vector_store/lancedb.py,sha256=Qd20gKjWozPWfW5-D66J6U8dSrJo1yl-maj6s1lbf1c,14688
123
125
  langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3HmhHQICXLs,11663
124
126
  langroid/vector_store/momento.py,sha256=xOaU7Hlyyn_5ihb0ARS5JHtmrKrTCt2IdRA-ioMM5ek,10307
127
+ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZXpEY9M,14994
128
+ langroid/vector_store/postgres.py,sha256=DQHd6dt-OcV_QVNm-ymn28rlTfhI6hqgcpLTPCsm0jI,15990
125
129
  langroid/vector_store/qdrantdb.py,sha256=v7TAsIoj_vxeKDYS9tpwJLBZA8fuTweTYxHo0X_uawM,17949
126
- langroid/vector_store/weaviatedb.py,sha256=cMg9kqJXlD1WURs6QivHvwausCyLYGr4mOK2v9uYkhw,11105
127
- langroid-0.39.5.dist-info/METADATA,sha256=BPira_zYZOFY685gg9eWMVM7q-x8o710qWCNBRKJAMw,60634
128
- langroid-0.39.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
129
- langroid-0.39.5.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
130
- langroid-0.39.5.dist-info/RECORD,,
130
+ langroid/vector_store/weaviatedb.py,sha256=ONEr2iGS0Ii73oMe7tRk6bB-BEXQUa70fYSrdI8d3yo,11481
131
+ langroid-0.41.0.dist-info/METADATA,sha256=jCbP1nZgmhcN4XJE7eh8SBMbFEwNVV8yVoZqrmV8pCQ,61259
132
+ langroid-0.41.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
133
+ langroid-0.41.0.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
134
+ langroid-0.41.0.dist-info/RECORD,,