langroid 0.53.13__py3-none-any.whl → 0.53.15__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.
langroid/agent/base.py CHANGED
@@ -503,6 +503,10 @@ class Agent(ABC):
503
503
  self.callbacks.show_agent_response(
504
504
  content=results_str,
505
505
  language="json" if maybe_json else "text",
506
+ is_tool=(
507
+ isinstance(results, ChatDocument)
508
+ and self.has_tool_message_attempt(results)
509
+ ),
506
510
  )
507
511
  if isinstance(results, ChatDocument):
508
512
  # Preserve trail of tool_ids for OpenAI Assistant fn-calls
langroid/agent/batch.py CHANGED
@@ -334,6 +334,21 @@ def run_batch_task_gen(
334
334
  task_i.kill()
335
335
  # exception will be handled by the caller
336
336
  raise e
337
+ # ----------------------------------------
338
+ # Propagate any exception stored on the task that may have been
339
+ # swallowed inside `Task.run_async`, so that the upper-level
340
+ # exception-handling logic works as expected.
341
+ for attr in ("_exception", "last_exception", "exception"):
342
+ exc = getattr(task_i, attr, None)
343
+ if isinstance(exc, BaseException):
344
+ raise exc
345
+ # Fallback: treat a KILL-status result as an error
346
+ if (
347
+ isinstance(result, ChatDocument)
348
+ and getattr(result, "status", None) is not None
349
+ and str(getattr(result, "status")) == "StatusCode.KILL"
350
+ ):
351
+ raise RuntimeError(str(result.content))
337
352
  return result
338
353
 
339
354
  return run_batched_tasks(
@@ -410,7 +410,12 @@ class ChainlitAgentCallbacks:
410
410
  self.last_step = step
411
411
  run_sync(step.send())
412
412
 
413
- def show_agent_response(self, content: str, language="text") -> None:
413
+ def show_agent_response(
414
+ self,
415
+ content: str,
416
+ language="text",
417
+ is_tool: bool = False,
418
+ ) -> None:
414
419
  """Show message from agent (typically tool handler)."""
415
420
  if language == "text":
416
421
  content = wrap_text_preserving_structure(content, width=90)
@@ -7,6 +7,13 @@ expression (involving a dataframe `df`) to answer the query.
7
7
  The expression is passed via the `pandas_eval` tool/function-call,
8
8
  which is handled by the Agent's `pandas_eval` method. This method evaluates
9
9
  the expression and returns the result as a string.
10
+
11
+ WARNING: This Agent should be used only with trusted input, as it can execute system
12
+ commands.
13
+
14
+ The `full_eval` flag is false by default, which means that the input is sanitized
15
+ against most common code injection attack vectors. `full_eval` may be set to True to
16
+ disable sanitization at all. Both cases should be used with caution.
10
17
  """
11
18
 
12
19
  import io
@@ -26,6 +33,7 @@ from langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig
26
33
  from langroid.parsing.table_loader import read_tabular_data
27
34
  from langroid.prompts.prompts_config import PromptsConfig
28
35
  from langroid.utils.constants import DONE, PASS
36
+ from langroid.utils.pandas_utils import sanitize_command
29
37
  from langroid.vector_store.base import VectorStoreConfig
30
38
 
31
39
  logger = logging.getLogger(__name__)
@@ -113,6 +121,9 @@ class TableChatAgentConfig(ChatAgentConfig):
113
121
  cache: bool = True # cache results
114
122
  debug: bool = False
115
123
  stream: bool = True # allow streaming where needed
124
+ full_eval: bool = (
125
+ False # runs eval without sanitization. Use only on trusted input!
126
+ )
116
127
  data: str | pd.DataFrame # data file, URL, or DataFrame
117
128
  separator: None | str = None # separator for data file
118
129
  vecdb: None | VectorStoreConfig = None
@@ -204,7 +215,7 @@ class TableChatAgent(ChatAgent):
204
215
  """
205
216
  self.sent_expression = True
206
217
  exprn = msg.expression
207
- local_vars = {"df": self.df}
218
+ vars = {"df": self.df}
208
219
  # Create a string-based I/O stream
209
220
  code_out = io.StringIO()
210
221
 
@@ -212,10 +223,13 @@ class TableChatAgent(ChatAgent):
212
223
  sys.stdout = code_out
213
224
 
214
225
  # Evaluate the last line and get the result;
215
- # SECURITY: eval only with empty globals and {"df": df} in locals to
216
- # prevent arbitrary Python code execution.
226
+ # SECURITY MITIGATION: Eval input is sanitized by default to prevent most
227
+ # common code injection attack vectors.
217
228
  try:
218
- eval_result = eval(exprn, {}, local_vars)
229
+ if not self.config.full_eval:
230
+ exprn = sanitize_command(exprn)
231
+ code = compile(exprn, "<calc>", "eval")
232
+ eval_result = eval(code, vars, {})
219
233
  except Exception as e:
220
234
  eval_result = f"ERROR: {type(e)}: {e}"
221
235
 
@@ -226,7 +240,7 @@ class TableChatAgent(ChatAgent):
226
240
  sys.stdout = sys.__stdout__
227
241
 
228
242
  # If df has been modified in-place, save the changes back to self.df
229
- self.df = local_vars["df"]
243
+ self.df = vars["df"]
230
244
 
231
245
  # Get the resulting string from the I/O stream
232
246
  print_result = code_out.getvalue() or ""
langroid/utils/logging.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import os
3
3
  import os.path
4
+ import sys
4
5
  import threading
5
6
  from typing import ClassVar, Dict, no_type_check
6
7
 
@@ -163,7 +164,16 @@ class RichFileLogger:
163
164
 
164
165
  os.makedirs(os.path.dirname(log_file), exist_ok=True)
165
166
  mode = "a" if append else "w"
166
- self.file = open(log_file, mode, buffering=1, encoding="utf-8")
167
+ self._owns_file: bool = True
168
+ try:
169
+ self.file = open(log_file, mode, buffering=1, encoding="utf-8")
170
+ except OSError as exc: # EMFILE: too many open files
171
+ if exc.errno == 24:
172
+ # Fallback: reuse an already-open stream to avoid creating a new FD
173
+ self.file = sys.stderr
174
+ self._owns_file = False
175
+ else:
176
+ raise
167
177
  self.log_file: str = log_file
168
178
  self.color: bool = color
169
179
  self.console: Console | None = (
@@ -195,7 +205,7 @@ class RichFileLogger:
195
205
  self._ref_counts.pop(self.log_file, None)
196
206
  self._instances.pop(self.log_file, None)
197
207
  with self._write_lock:
198
- if not self.file.closed:
208
+ if self._owns_file and not self.file.closed:
199
209
  self.file.close()
200
210
  else:
201
211
  self._ref_counts[self.log_file] = count
@@ -1,7 +1,287 @@
1
+ import ast
1
2
  from typing import Any
2
3
 
3
4
  import pandas as pd
4
5
 
6
+ COMMON_USE_DF_METHODS = {
7
+ "T",
8
+ "abs",
9
+ "add",
10
+ "add_prefix",
11
+ "add_suffix",
12
+ "agg",
13
+ "aggregate",
14
+ "align",
15
+ "all",
16
+ "any",
17
+ "apply",
18
+ "applymap",
19
+ "at",
20
+ "at_time",
21
+ "between_time",
22
+ "bfill",
23
+ "clip",
24
+ "combine",
25
+ "combine_first",
26
+ "convert_dtypes",
27
+ "corr",
28
+ "corrwith",
29
+ "count",
30
+ "cov",
31
+ "cummax",
32
+ "cummin",
33
+ "cumprod",
34
+ "cumsum",
35
+ "describe",
36
+ "diff",
37
+ "dot",
38
+ "drop_duplicates",
39
+ "duplicated",
40
+ "eq",
41
+ "eval",
42
+ "ewm",
43
+ "expanding",
44
+ "explode",
45
+ "filter",
46
+ "first",
47
+ "groupby",
48
+ "head",
49
+ "idxmax",
50
+ "idxmin",
51
+ "infer_objects",
52
+ "interpolate",
53
+ "isin",
54
+ "kurt",
55
+ "kurtosis",
56
+ "last",
57
+ "le",
58
+ "loc",
59
+ "lt",
60
+ "gt",
61
+ "ge",
62
+ "iloc",
63
+ "mask",
64
+ "max",
65
+ "mean",
66
+ "median",
67
+ "melt",
68
+ "min",
69
+ "mode",
70
+ "mul",
71
+ "nlargest",
72
+ "nsmallest",
73
+ "notna",
74
+ "notnull",
75
+ "nunique",
76
+ "pct_change",
77
+ "pipe",
78
+ "pivot",
79
+ "pivot_table",
80
+ "prod",
81
+ "product",
82
+ "quantile",
83
+ "query",
84
+ "rank",
85
+ "replace",
86
+ "resample",
87
+ "rolling",
88
+ "round",
89
+ "sample",
90
+ "select_dtypes",
91
+ "sem",
92
+ "shift",
93
+ "skew",
94
+ "sort_index",
95
+ "sort_values",
96
+ "squeeze",
97
+ "stack",
98
+ "std",
99
+ "sum",
100
+ "tail",
101
+ "transform",
102
+ "transpose",
103
+ "unstack",
104
+ "value_counts",
105
+ "var",
106
+ "where",
107
+ "xs",
108
+ }
109
+
110
+ POTENTIALLY_DANGEROUS_DF_METHODS = {
111
+ "eval",
112
+ "query",
113
+ "apply",
114
+ "applymap",
115
+ "pipe",
116
+ "agg",
117
+ "aggregate",
118
+ "transform",
119
+ "rolling",
120
+ "expanding",
121
+ "resample",
122
+ }
123
+
124
+ WHITELISTED_DF_METHODS = COMMON_USE_DF_METHODS - POTENTIALLY_DANGEROUS_DF_METHODS
125
+
126
+
127
+ BLOCKED_KW = {
128
+ "engine",
129
+ "parser",
130
+ "inplace",
131
+ "regex",
132
+ "dtype",
133
+ "converters",
134
+ "eval",
135
+ }
136
+ MAX_CHAIN = 6
137
+ MAX_DEPTH = 25
138
+ NUMERIC_LIMIT = 1_000_000_000
139
+
140
+
141
+ class UnsafeCommandError(ValueError):
142
+ """Raised when a command string violates security policy."""
143
+
144
+ pass
145
+
146
+
147
+ def _literal_ok(node: ast.AST) -> bool:
148
+ """Return True if *node* is a safe literal (and within numeric limit)."""
149
+ if isinstance(node, ast.Constant):
150
+ if (
151
+ isinstance(node.value, (int, float, complex))
152
+ and abs(node.value) > NUMERIC_LIMIT
153
+ ):
154
+ raise UnsafeCommandError("numeric constant exceeds limit")
155
+ return True
156
+ if isinstance(node, (ast.Tuple, ast.List)):
157
+ return all(_literal_ok(elt) for elt in node.elts)
158
+ if isinstance(node, ast.Slice):
159
+ return all(
160
+ sub is None or _literal_ok(sub)
161
+ for sub in (node.lower, node.upper, node.step)
162
+ )
163
+ return False
164
+
165
+
166
+ class CommandValidator(ast.NodeVisitor):
167
+ """AST walker that enforces the security policy."""
168
+
169
+ # Comparison operators we allow
170
+ ALLOWED_CMPOP = (ast.Gt, ast.GtE, ast.Lt, ast.LtE, ast.Eq, ast.NotEq)
171
+
172
+ # Arithmetic operators we allow (power ** intentionally omitted)
173
+ ALLOWED_BINOP = (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod)
174
+ ALLOWED_UNARY = (ast.UAdd, ast.USub)
175
+
176
+ # Node whitelist
177
+ ALLOWED_NODES = (
178
+ ast.Expression,
179
+ ast.Attribute,
180
+ ast.Name,
181
+ ast.Load,
182
+ ast.Call,
183
+ ast.Subscript,
184
+ ast.Constant,
185
+ ast.Tuple,
186
+ ast.List,
187
+ ast.Slice,
188
+ ast.keyword,
189
+ ast.BinOp,
190
+ ast.UnaryOp,
191
+ ast.Compare,
192
+ *ALLOWED_BINOP,
193
+ *ALLOWED_UNARY,
194
+ *ALLOWED_CMPOP,
195
+ )
196
+
197
+ def __init__(self, df_name: str = "df"):
198
+ self.df_name = df_name
199
+ self.depth = 0
200
+ self.chain = 0
201
+
202
+ # Depth guard
203
+ def generic_visit(self, node: ast.AST) -> None:
204
+ self.depth += 1
205
+ if self.depth > MAX_DEPTH:
206
+ raise UnsafeCommandError("AST nesting too deep")
207
+ super().generic_visit(node)
208
+ self.depth -= 1
209
+
210
+ # Literal validation
211
+ def visit_Constant(self, node: ast.Constant) -> None:
212
+ _literal_ok(node)
213
+
214
+ # Arithmetic
215
+ def visit_BinOp(self, node: ast.BinOp) -> None:
216
+ if not isinstance(node.op, self.ALLOWED_BINOP):
217
+ raise UnsafeCommandError("operator not allowed")
218
+ self.generic_visit(node)
219
+
220
+ def visit_UnaryOp(self, node: ast.UnaryOp) -> None:
221
+ if not isinstance(node.op, self.ALLOWED_UNARY):
222
+ raise UnsafeCommandError("unary operator not allowed")
223
+ self.generic_visit(node)
224
+
225
+ # Comparisons
226
+ def visit_Compare(self, node: ast.Compare) -> None:
227
+ if not all(isinstance(op, self.ALLOWED_CMPOP) for op in node.ops):
228
+ raise UnsafeCommandError("comparison operator not allowed")
229
+ for comp in node.comparators:
230
+ _literal_ok(comp)
231
+ self.generic_visit(node)
232
+
233
+ # Subscripts
234
+ def visit_Subscript(self, node: ast.Subscript) -> None:
235
+ if not _literal_ok(node.slice):
236
+ raise UnsafeCommandError("subscript must be literal")
237
+ self.generic_visit(node)
238
+
239
+ # Method calls
240
+ def visit_Call(self, node: ast.Call) -> None:
241
+ if not isinstance(node.func, ast.Attribute):
242
+ raise UnsafeCommandError("only DataFrame method calls allowed")
243
+
244
+ method = node.func.attr
245
+ self.chain += 1
246
+ if self.chain > MAX_CHAIN:
247
+ raise UnsafeCommandError("method-chain too long")
248
+ if method not in WHITELISTED_DF_METHODS:
249
+ raise UnsafeCommandError(f"method '{method}' not permitted")
250
+
251
+ # kwarg / arg checks
252
+ for kw in node.keywords:
253
+ if kw.arg in BLOCKED_KW:
254
+ raise UnsafeCommandError(f"kwarg '{kw.arg}' is blocked")
255
+ _literal_ok(kw.value)
256
+ for arg in node.args:
257
+ _literal_ok(arg)
258
+
259
+ try:
260
+ self.generic_visit(node)
261
+ finally:
262
+ self.chain -= 1
263
+
264
+ # Names
265
+ def visit_Name(self, node: ast.Name) -> None:
266
+ if node.id != self.df_name:
267
+ raise UnsafeCommandError(f"unexpected variable '{node.id}'")
268
+
269
+ # Top-level gate
270
+ def visit(self, node: ast.AST) -> None:
271
+ if not isinstance(node, self.ALLOWED_NODES):
272
+ raise UnsafeCommandError(f"disallowed node {type(node).__name__}")
273
+ super().visit(node)
274
+
275
+
276
+ def sanitize_command(expr: str, df_name: str = "df") -> str:
277
+ """
278
+ Validate *expr*; return it unchanged if it passes all rules,
279
+ else raise UnsafeCommandError with the first violation encountered.
280
+ """
281
+ tree = ast.parse(expr, mode="eval")
282
+ CommandValidator(df_name).visit(tree)
283
+ return expr
284
+
5
285
 
6
286
  def stringify(x: Any) -> str:
7
287
  # Convert x to DataFrame if it is not one already
@@ -14,7 +14,7 @@ from langroid.utils.algorithms.graph import components, topological_sort
14
14
  from langroid.utils.configuration import settings
15
15
  from langroid.utils.object_registry import ObjectRegistry
16
16
  from langroid.utils.output.printing import print_long_text
17
- from langroid.utils.pandas_utils import stringify
17
+ from langroid.utils.pandas_utils import sanitize_command, stringify
18
18
  from langroid.utils.pydantic_utils import flatten_dict
19
19
 
20
20
  logger = logging.getLogger(__name__)
@@ -159,11 +159,12 @@ class VectorStore(ABC):
159
159
  df = pd.DataFrame(dicts)
160
160
 
161
161
  try:
162
- # SECURITY: Use Python's eval() with NO globals and only {"df": df}
163
- # in locals. This allows pandas operations on `df` while preventing
164
- # access to builtins or other potentially harmful global functions,
165
- # mitigating risks associated with executing untrusted `calc` strings.
166
- result = eval(calc, {}, {"df": df}) # type: ignore
162
+ # SECURITY MITIGATION: Eval input is sanitized to prevent most common
163
+ # code injection attack vectors.
164
+ vars = {"df": df}
165
+ calc = sanitize_command(calc)
166
+ code = compile(calc, "<calc>", "eval")
167
+ result = eval(code, vars, {})
167
168
  except Exception as e:
168
169
  # return error message so LLM can fix the calc string if needed
169
170
  err = f"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.53.13
3
+ Version: 0.53.15
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -3,8 +3,8 @@ langroid/exceptions.py,sha256=OPjece_8cwg94DLPcOGA1ddzy5bGh65pxzcHMnssTz8,2995
3
3
  langroid/mytypes.py,sha256=HIcYAqGeA9OK0Hlscym2FI5Oax9QFljDZoVgRlomhRk,4014
4
4
  langroid/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  langroid/agent/__init__.py,sha256=ll0Cubd2DZ-fsCMl7e10hf9ZjFGKzphfBco396IKITY,786
6
- langroid/agent/base.py,sha256=zHwhNU403H-ZvogH4QhKTzaZn5_jt0ZdPHzSEmycDoc,80035
7
- langroid/agent/batch.py,sha256=vi1r5i1-vN80WfqHDSwjEym_KfGsqPGUtwktmiK1nuk,20635
6
+ langroid/agent/base.py,sha256=9ZA2PhluFXReeqtGr7mdOUZZ7AXX6Eg_YymzdniUB-E,80181
7
+ langroid/agent/batch.py,sha256=wpE9RqCNDVDhAXkCB7wEqfCIEAi6qKcrhaZ-Zr9T4C0,21375
8
8
  langroid/agent/chat_agent.py,sha256=2HIYzYxkrGkRIS97ioKfIqjaW3RbX89M39LjzBobBEY,88381
9
9
  langroid/agent/chat_document.py,sha256=6O20Fp4QrquykaF2jFtwNHkvcoDte1LLwVZNk9mVH9c,18057
10
10
  langroid/agent/openai_assistant.py,sha256=JkAcs02bIrgPNVvUWVR06VCthc5-ulla2QMBzux_q6o,34340
@@ -12,7 +12,7 @@ langroid/agent/task.py,sha256=dJ2nRKc9-ulZyzNaABMKb9PKJzWRdZTonH9TPswpdbc,90801
12
12
  langroid/agent/tool_message.py,sha256=BhjP-_TfQ2tgxuY4Yo_JHLOwwt0mJ4BwjPnREvEY4vk,14744
13
13
  langroid/agent/xml_tool_message.py,sha256=oeBKnJNoGaKdtz39XoWGMTNlVyXew2MWH5lgtYeh8wQ,15496
14
14
  langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- langroid/agent/callbacks/chainlit.py,sha256=UHB6P_J40vsVnssosqkpkOVWRf9NK4TOY0_G2g_Arsg,20900
15
+ langroid/agent/callbacks/chainlit.py,sha256=4rJw07NIIVTIVvksVY08h5PdLE_kRoJItjbQM0UjRn0,20962
16
16
  langroid/agent/special/__init__.py,sha256=gik_Xtm_zV7U9s30Mn8UX3Gyuy4jTjQe9zjiE3HWmEo,1273
17
17
  langroid/agent/special/doc_chat_agent.py,sha256=7PvVKHrXHw2LoSgU2-3hE7mz46r5oKB3o_bFhWmfT_I,65642
18
18
  langroid/agent/special/doc_chat_task.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -20,7 +20,7 @@ langroid/agent/special/lance_doc_chat_agent.py,sha256=s8xoRs0gGaFtDYFUSIRchsgDVb
20
20
  langroid/agent/special/lance_tools.py,sha256=qS8x4wi8mrqfbYV2ztFzrcxyhHQ0ZWOc-zkYiH7awj0,2105
21
21
  langroid/agent/special/relevance_extractor_agent.py,sha256=zIx8GUdVo1aGW6ASla0NPQjYYIpmriK_TYMijqAx3F8,4796
22
22
  langroid/agent/special/retriever_agent.py,sha256=o2UfqiCGME0t85SZ6qjK041_WZYqXSuV1SeH_3KtVuc,1931
23
- langroid/agent/special/table_chat_agent.py,sha256=wC4DWRVMAGfVY33JqP8cNR4kO0dqD8ryjWzAkMFT5j0,9780
23
+ langroid/agent/special/table_chat_agent.py,sha256=WS-E3QqI6Wm67-GNAcfIIl_TW7C-kYzlpd461hwLz6Y,10403
24
24
  langroid/agent/special/arangodb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  langroid/agent/special/arangodb/arangodb_agent.py,sha256=12Y54c84c9qXV-YXRBcI5HaqyiY75JR4TmqlURYKJAM,25851
26
26
  langroid/agent/special/arangodb/system_messages.py,sha256=udwfLleTdyz_DuxHuoiv2wHEZoAPBPbwdF_ivjIfP5c,6867
@@ -112,9 +112,9 @@ langroid/utils/configuration.py,sha256=ZkHHkEeWuS-o3_S4g0SE0wz-UK_of23NOWve1kpQi
112
112
  langroid/utils/constants.py,sha256=CK09kda9bNDEhnwClq7ZTWZOh38guJlfcZ5hKUS1Ijo,1075
113
113
  langroid/utils/git_utils.py,sha256=WnflJ3R3owhlD0LNdSJakcKhExcEehE1UW5jYVQl8JY,7955
114
114
  langroid/utils/globals.py,sha256=Az9dOFqR6n9CoTYSqa2kLikQWS0oCQ9DFQIQAnG-2q8,1355
115
- langroid/utils/logging.py,sha256=zTcsd0Gsmar-BMUCoVyRJUOvKE6Nb54PRFu5coqCAmA,6844
115
+ langroid/utils/logging.py,sha256=kmdpj1ozH1apf3o00Zgz-ZT-S4BHqUfF81GkY0Gf578,7262
116
116
  langroid/utils/object_registry.py,sha256=iPz9GHzvmCeVoidB3JdAMEKcxJEqTdUr0otQEexDZ5s,2100
117
- langroid/utils/pandas_utils.py,sha256=UctS986Jtl_MvU5rA7-GfrjEHXP7MNu8ePhepv0bTn0,755
117
+ langroid/utils/pandas_utils.py,sha256=Zz_-dKogZR2Ijw5QNTHC2EcEmzgODPU-2_MVgS3274c,7266
118
118
  langroid/utils/pydantic_utils.py,sha256=R7Ps8VP56-eSo-LYHWllFo-SJ2zDmdItuuYpUq2gGJ8,20854
119
119
  langroid/utils/system.py,sha256=q3QJtTSapIwNe8MMhGEM03wgxPLmZiD47_sF1pKx53I,8472
120
120
  langroid/utils/types.py,sha256=-BvyIf_LmAJ5jR9NC7S4CSVNEr3XayAaxJ5o0TiIej0,2992
@@ -125,7 +125,7 @@ langroid/utils/output/citations.py,sha256=9W0slQQgzRGLS7hU51mm5UWao5cS_xr8AVosVe
125
125
  langroid/utils/output/printing.py,sha256=yzPJZN-8_jyOJmI9N_oLwEDfjMwVgk3IDiwnZ4eK_AE,2962
126
126
  langroid/utils/output/status.py,sha256=rzbE7mDJcgNNvdtylCseQcPGCGghtJvVq3lB-OPJ49E,1049
127
127
  langroid/vector_store/__init__.py,sha256=8ktJUVsVUoc7FMmkUFpFBZu7VMWUqQY9zpm4kEJ8yTs,1537
128
- langroid/vector_store/base.py,sha256=HDgY2uMwOnoyuySDCXdRK_USPWaFRLhti94B2OP1B_w,14752
128
+ langroid/vector_store/base.py,sha256=uIRz3ZVmqxzuq2V71Kpys_6-j460gGjHXQIAJWJLI78,14675
129
129
  langroid/vector_store/chromadb.py,sha256=p9mEqJwO2BrL2jSSXfa23kCPlPOwWpF3xJYd5zoWw_c,8661
130
130
  langroid/vector_store/lancedb.py,sha256=Qd20gKjWozPWfW5-D66J6U8dSrJo1yl-maj6s1lbf1c,14688
131
131
  langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3HmhHQICXLs,11663
@@ -133,7 +133,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
133
133
  langroid/vector_store/postgres.py,sha256=wHPtIi2qM4fhO4pMQr95pz1ZCe7dTb2hxl4VYspGZoA,16104
134
134
  langroid/vector_store/qdrantdb.py,sha256=O6dSBoDZ0jzfeVBd7LLvsXu083xs2fxXtPa9gGX3JX4,18443
135
135
  langroid/vector_store/weaviatedb.py,sha256=Yn8pg139gOy3zkaPfoTbMXEEBCiLiYa1MU5d_3UA1K4,11847
136
- langroid-0.53.13.dist-info/METADATA,sha256=MxvVyK7eqNOrqoRg3LEZ2s9v9kIgQZcTfg1BCErytmA,64946
137
- langroid-0.53.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
138
- langroid-0.53.13.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
139
- langroid-0.53.13.dist-info/RECORD,,
136
+ langroid-0.53.15.dist-info/METADATA,sha256=893X5dUY-L85Q0rKwS2Ex6fIH3L_W3TZ0NFqCeMbz4Q,64946
137
+ langroid-0.53.15.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
138
+ langroid-0.53.15.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
139
+ langroid-0.53.15.dist-info/RECORD,,