langroid 0.54.1__py3-none-any.whl → 0.54.2__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
@@ -251,6 +251,172 @@ class Agent(ABC):
251
251
  def clear_dialog(self) -> None:
252
252
  self.dialog = []
253
253
 
254
+ def _analyze_handler_params(
255
+ self, handler_method: Any
256
+ ) -> Tuple[bool, Optional[str], Optional[str]]:
257
+ """
258
+ Analyze parameters of a handler method to determine their types.
259
+
260
+ Returns:
261
+ Tuple of (has_annotations, agent_param_name, chat_doc_param_name)
262
+ - has_annotations: True if useful type annotations were found
263
+ - agent_param_name: Name of the agent parameter if found
264
+ - chat_doc_param_name: Name of the chat_doc parameter if found
265
+ """
266
+ sig = inspect.signature(handler_method)
267
+ params = list(sig.parameters.values())
268
+ # Remove 'self' parameter
269
+ params = [p for p in params if p.name != "self"]
270
+
271
+ agent_param = None
272
+ chat_doc_param = None
273
+ has_annotations = False
274
+
275
+ for param in params:
276
+ # First try type annotations
277
+ if param.annotation != inspect.Parameter.empty:
278
+ ann_str = str(param.annotation)
279
+ # Check for Agent-like types
280
+ if (
281
+ param.annotation == self.__class__
282
+ or "Agent" in ann_str
283
+ or (
284
+ hasattr(param.annotation, "__name__")
285
+ and "Agent" in param.annotation.__name__
286
+ )
287
+ ):
288
+ agent_param = param.name
289
+ has_annotations = True
290
+ # Check for ChatDocument-like types
291
+ elif "ChatDocument" in ann_str or "ChatDoc" in ann_str:
292
+ chat_doc_param = param.name
293
+ has_annotations = True
294
+
295
+ # Fallback to parameter names
296
+ elif param.name == "agent":
297
+ agent_param = param.name
298
+ elif param.name == "chat_doc":
299
+ chat_doc_param = param.name
300
+
301
+ return has_annotations, agent_param, chat_doc_param
302
+
303
+ @no_type_check
304
+ def _create_handler_wrapper(
305
+ self,
306
+ message_class: Type[ToolMessage],
307
+ handler_method: Any,
308
+ is_async: bool = False,
309
+ ) -> Any:
310
+ """
311
+ Create a wrapper function for a handler method based on its signature.
312
+
313
+ Args:
314
+ message_class: The ToolMessage class
315
+ handler_method: The handle/handle_async method
316
+ is_async: Whether this is for an async handler
317
+
318
+ Returns:
319
+ Appropriate wrapper function
320
+ """
321
+ sig = inspect.signature(handler_method)
322
+ params = list(sig.parameters.values())
323
+ params = [p for p in params if p.name != "self"]
324
+
325
+ has_annotations, agent_param, chat_doc_param = self._analyze_handler_params(
326
+ handler_method,
327
+ )
328
+
329
+ # Build wrapper based on found parameters
330
+ if len(params) == 0:
331
+ if is_async:
332
+
333
+ async def wrapper(obj: Any) -> Any:
334
+ return await obj.handle_async()
335
+
336
+ else:
337
+
338
+ def wrapper(obj: Any) -> Any:
339
+ return obj.handle()
340
+
341
+ elif agent_param and chat_doc_param:
342
+ # Both parameters present - build wrapper respecting their order
343
+ param_names = [p.name for p in params]
344
+ if param_names.index(agent_param) < param_names.index(chat_doc_param):
345
+ # agent is first parameter
346
+ if is_async:
347
+
348
+ async def wrapper(obj: Any, chat_doc: Any) -> Any:
349
+ return await obj.handle_async(self, chat_doc)
350
+
351
+ else:
352
+
353
+ def wrapper(obj: Any, chat_doc: Any) -> Any:
354
+ return obj.handle(self, chat_doc)
355
+
356
+ else:
357
+ # chat_doc is first parameter
358
+ if is_async:
359
+
360
+ async def wrapper(obj: Any, chat_doc: Any) -> Any:
361
+ return await obj.handle_async(chat_doc, self)
362
+
363
+ else:
364
+
365
+ def wrapper(obj: Any, chat_doc: Any) -> Any:
366
+ return obj.handle(chat_doc, self)
367
+
368
+ elif agent_param and not chat_doc_param:
369
+ # Only agent parameter
370
+ if is_async:
371
+
372
+ async def wrapper(obj: Any) -> Any:
373
+ return await obj.handle_async(self)
374
+
375
+ else:
376
+
377
+ def wrapper(obj: Any) -> Any:
378
+ return obj.handle(self)
379
+
380
+ elif chat_doc_param and not agent_param:
381
+ # Only chat_doc parameter
382
+ if is_async:
383
+
384
+ async def wrapper(obj: Any, chat_doc: Any) -> Any:
385
+ return await obj.handle_async(chat_doc)
386
+
387
+ else:
388
+
389
+ def wrapper(obj: Any, chat_doc: Any) -> Any:
390
+ return obj.handle(chat_doc)
391
+
392
+ else:
393
+ # No recognized parameters - backward compatibility
394
+ # Assume single parameter is chat_doc (legacy behavior)
395
+ if len(params) == 1:
396
+ if is_async:
397
+
398
+ async def wrapper(obj: Any, chat_doc: Any) -> Any:
399
+ return await obj.handle_async(chat_doc)
400
+
401
+ else:
402
+
403
+ def wrapper(obj: Any, chat_doc: Any) -> Any:
404
+ return obj.handle(chat_doc)
405
+
406
+ else:
407
+ # Multiple unrecognized parameters - best guess
408
+ if is_async:
409
+
410
+ async def wrapper(obj: Any, chat_doc: Any) -> Any:
411
+ return await obj.handle_async(chat_doc)
412
+
413
+ else:
414
+
415
+ def wrapper(obj: Any, chat_doc: Any) -> Any:
416
+ return obj.handle(chat_doc)
417
+
418
+ return wrapper
419
+
254
420
  def _get_tool_list(
255
421
  self, message_class: Optional[Type[ToolMessage]] = None
256
422
  ) -> List[str]:
@@ -304,13 +470,12 @@ class Agent(ABC):
304
470
  in one place, i.e. in the message class.
305
471
  See `tests/main/test_stateless_tool_messages.py` for an example.
306
472
  """
307
- has_chat_doc_arg = (
308
- len(inspect.signature(message_class.handle).parameters) > 1
473
+ wrapper = self._create_handler_wrapper(
474
+ message_class,
475
+ message_class.handle,
476
+ is_async=False,
309
477
  )
310
- if has_chat_doc_arg:
311
- setattr(self, handler, lambda obj, chat_doc: obj.handle(chat_doc))
312
- else:
313
- setattr(self, handler, lambda obj: obj.handle())
478
+ setattr(self, handler, wrapper)
314
479
  elif (
315
480
  hasattr(message_class, "response")
316
481
  and inspect.isfunction(message_class.response)
@@ -320,11 +485,17 @@ class Agent(ABC):
320
485
  len(inspect.signature(message_class.response).parameters) > 2
321
486
  )
322
487
  if has_chat_doc_arg:
323
- setattr(
324
- self, handler, lambda obj, chat_doc: obj.response(self, chat_doc)
325
- )
488
+
489
+ def response_wrapper_with_chat_doc(obj: Any, chat_doc: Any) -> Any:
490
+ return obj.response(self, chat_doc)
491
+
492
+ setattr(self, handler, response_wrapper_with_chat_doc)
326
493
  else:
327
- setattr(self, handler, lambda obj: obj.response(self))
494
+
495
+ def response_wrapper_no_chat_doc(obj: Any) -> Any:
496
+ return obj.response(self)
497
+
498
+ setattr(self, handler, response_wrapper_no_chat_doc)
328
499
 
329
500
  if hasattr(message_class, "handle_message_fallback") and (
330
501
  inspect.isfunction(message_class.handle_message_fallback)
@@ -334,10 +505,13 @@ class Agent(ABC):
334
505
  # `handle_message_fallback` method (which does nothing).
335
506
  # It's possible multiple tool messages have a `handle_message_fallback`,
336
507
  # in which case, the last one inserted will be used.
508
+ def fallback_wrapper(msg: Any) -> Any:
509
+ return message_class.handle_message_fallback(self, msg)
510
+
337
511
  setattr(
338
512
  self,
339
513
  "handle_message_fallback",
340
- lambda msg: message_class.handle_message_fallback(self, msg),
514
+ fallback_wrapper,
341
515
  )
342
516
 
343
517
  async_handler_name = f"{handler}_async"
@@ -346,23 +520,12 @@ class Agent(ABC):
346
520
  and inspect.isfunction(message_class.handle_async)
347
521
  and not hasattr(self, async_handler_name)
348
522
  ):
349
- has_chat_doc_arg = (
350
- len(inspect.signature(message_class.handle_async).parameters) > 1
523
+ wrapper = self._create_handler_wrapper(
524
+ message_class,
525
+ message_class.handle_async,
526
+ is_async=True,
351
527
  )
352
-
353
- if has_chat_doc_arg:
354
-
355
- @no_type_check
356
- async def handler(obj, chat_doc):
357
- return await obj.handle_async(chat_doc)
358
-
359
- else:
360
-
361
- @no_type_check
362
- async def handler(obj):
363
- return await obj.handle_async()
364
-
365
- setattr(self, async_handler_name, handler)
528
+ setattr(self, async_handler_name, wrapper)
366
529
  elif (
367
530
  hasattr(message_class, "response_async")
368
531
  and inspect.isfunction(message_class.response_async)
@@ -1,6 +1,8 @@
1
1
  import asyncio
2
2
  import datetime
3
3
  import logging
4
+ from base64 import b64decode
5
+ from io import BytesIO
4
6
  from typing import Any, Dict, List, Optional, Tuple, Type, TypeAlias, cast
5
7
 
6
8
  from dotenv import load_dotenv
@@ -16,9 +18,20 @@ from mcp.client.session import (
16
18
  LoggingFnT,
17
19
  MessageHandlerFnT,
18
20
  )
19
- from mcp.types import CallToolResult, TextContent, Tool
21
+ from mcp.types import (
22
+ BlobResourceContents,
23
+ CallToolResult,
24
+ EmbeddedResource,
25
+ ImageContent,
26
+ TextContent,
27
+ TextResourceContents,
28
+ Tool,
29
+ )
20
30
 
31
+ from langroid.agent.base import Agent
32
+ from langroid.agent.chat_document import ChatDocument
21
33
  from langroid.agent.tool_message import ToolMessage
34
+ from langroid.parsing.file_attachment import FileAttachment
22
35
  from langroid.pydantic_v1 import AnyUrl, BaseModel, Field, create_model
23
36
 
24
37
  load_dotenv() # load environment variables from .env
@@ -39,6 +52,10 @@ class FastMCPClient:
39
52
  def __init__(
40
53
  self,
41
54
  server: FastMCPServerSpec,
55
+ persist_connection: bool = False,
56
+ forward_images: bool = True,
57
+ forward_text_resources: bool = False,
58
+ forward_blob_resources: bool = False,
42
59
  sampling_handler: SamplingHandler | None = None, # type: ignore
43
60
  roots: RootsList | RootsHandler | None = None, # type: ignore
44
61
  log_handler: LoggingFnT | None = None,
@@ -58,6 +75,10 @@ class FastMCPClient:
58
75
  self.log_handler = log_handler
59
76
  self.message_handler = message_handler
60
77
  self.read_timeout_seconds = read_timeout_seconds
78
+ self.persist_connection = persist_connection
79
+ self.forward_text_resources = forward_text_resources
80
+ self.forward_blob_resources = forward_blob_resources
81
+ self.forward_images = forward_images
61
82
 
62
83
  async def __aenter__(self) -> "FastMCPClient":
63
84
  """Enter the async context manager and connect inner client."""
@@ -96,6 +117,19 @@ class FastMCPClient:
96
117
  self.client = None
97
118
  self._cm = None
98
119
 
120
+ def __del__(self) -> None:
121
+ """Warn about unclosed persistent connections."""
122
+ if self.client is not None and self.persist_connection:
123
+ import warnings
124
+
125
+ warnings.warn(
126
+ f"FastMCPClient with persist_connection=True was not properly closed. "
127
+ f"Connection to {self.server} may leak resources. "
128
+ f"Use 'async with' or call await client.close()",
129
+ ResourceWarning,
130
+ stacklevel=2,
131
+ )
132
+
99
133
  def _schema_to_field(
100
134
  self, name: str, schema: Dict[str, Any], prefix: str
101
135
  ) -> Tuple[Any, Any]:
@@ -151,7 +185,13 @@ class FastMCPClient:
151
185
  with the given `tool_name`.
152
186
  """
153
187
  if not self.client:
154
- raise RuntimeError("Client not initialized. Use async with FastMCPClient.")
188
+ if self.persist_connection:
189
+ await self.connect()
190
+ assert self.client
191
+ else:
192
+ raise RuntimeError(
193
+ "Client not initialized. Use async with FastMCPClient."
194
+ )
155
195
  target = await self.get_mcp_tool_async(tool_name)
156
196
  if target is None:
157
197
  raise ValueError(f"No tool named {tool_name}")
@@ -209,36 +249,68 @@ class FastMCPClient:
209
249
  tool_model._renamed_fields = renamed # type: ignore[attr-defined]
210
250
 
211
251
  # 2) define an arg-free call_tool_async()
212
- async def call_tool_async(self: ToolMessage) -> Any:
252
+ async def call_tool_async(itself: ToolMessage) -> Any:
213
253
  from langroid.agent.tools.mcp.fastmcp_client import FastMCPClient
214
254
 
215
255
  # pack up the payload
216
- payload = self.dict(
217
- exclude=self.Config.schema_extra["exclude"].union(
256
+ payload = itself.dict(
257
+ exclude=itself.Config.schema_extra["exclude"].union(
218
258
  ["request", "purpose"]
219
259
  ),
220
260
  )
221
261
 
222
262
  # restore any renamed fields
223
- for orig, new in self.__class__._renamed_fields.items(): # type: ignore
263
+ for orig, new in itself.__class__._renamed_fields.items(): # type: ignore
224
264
  if new in payload:
225
265
  payload[orig] = payload.pop(new)
226
266
 
227
- client_cfg = getattr(self.__class__, "_client_config", None) # type: ignore
267
+ client_cfg = getattr(itself.__class__, "_client_config", None) # type: ignore
228
268
  if not client_cfg:
229
269
  # Fallback or error - ideally _client_config should always exist
230
- raise RuntimeError(f"Client config missing on {self.__class__}")
270
+ raise RuntimeError(f"Client config missing on {itself.__class__}")
271
+
272
+ # Connect the client if not yet connected and keep the connection open
273
+ if self.persist_connection:
274
+ if not self.client:
275
+ await self.connect()
276
+
277
+ return await self.call_mcp_tool(itself.request, payload)
278
+
231
279
  # open a fresh client, call the tool, then close
232
280
  async with FastMCPClient(**client_cfg) as client: # type: ignore
233
- return await client.call_mcp_tool(self.request, payload)
281
+ return await client.call_mcp_tool(itself.request, payload)
234
282
 
235
283
  tool_model.call_tool_async = call_tool_async # type: ignore
236
284
 
237
285
  if not hasattr(tool_model, "handle_async"):
238
- # 3) define an arg-free handle_async() method
239
- # if the tool model doesn't already have one
240
- async def handle_async(self: ToolMessage) -> Any:
241
- return await self.call_tool_async() # type: ignore[attr-defined]
286
+ # 3) define handle_async() method with optional agent parameter
287
+ from typing import Union
288
+
289
+ async def handle_async(
290
+ self: ToolMessage, agent: Optional[Agent] = None
291
+ ) -> Union[str, Optional[ChatDocument]]:
292
+ """
293
+ Auto-generated handler for MCP tool. Returns ChatDocument with files
294
+ if files are present and agent is provided, otherwise returns text.
295
+
296
+ To override: define your own handle_async method with matching signature
297
+ if you need file handling, or simpler signature if you only need text.
298
+ """
299
+ response = await self.call_tool_async() # type: ignore[attr-defined]
300
+ if response is None:
301
+ return None
302
+
303
+ content, files = response
304
+
305
+ # If we have files and an agent is provided, return a ChatDocument
306
+ if files and agent is not None:
307
+ return agent.create_agent_response(
308
+ content=content,
309
+ files=files,
310
+ )
311
+ else:
312
+ # Otherwise, just return the text content
313
+ return str(content) if content is not None else None
242
314
 
243
315
  # add the handle_async() method to the tool model
244
316
  tool_model.handle_async = handle_async # type: ignore
@@ -251,7 +323,13 @@ class FastMCPClient:
251
323
  handling nested schemas, with `handle_async` methods
252
324
  """
253
325
  if not self.client:
254
- raise RuntimeError("Client not initialized. Use async with FastMCPClient.")
326
+ if self.persist_connection:
327
+ await self.connect()
328
+ assert self.client
329
+ else:
330
+ raise RuntimeError(
331
+ "Client not initialized. Use async with FastMCPClient."
332
+ )
255
333
  resp = await self.client.list_tools()
256
334
  return [await self.get_tool_async(t.name) for t in resp]
257
335
 
@@ -267,7 +345,13 @@ class FastMCPClient:
267
345
  The raw Tool object from the server, or None.
268
346
  """
269
347
  if not self.client:
270
- raise RuntimeError("Client not initialized. Use async with FastMCPClient.")
348
+ if self.persist_connection:
349
+ await self.connect()
350
+ assert self.client
351
+ else:
352
+ raise RuntimeError(
353
+ "Client not initialized. Use async with FastMCPClient."
354
+ )
271
355
  resp: List[Tool] = await self.client.list_tools()
272
356
  return next((t for t in resp if t.name == name), None)
273
357
 
@@ -275,7 +359,7 @@ class FastMCPClient:
275
359
  self,
276
360
  tool_name: str,
277
361
  result: CallToolResult,
278
- ) -> List[str] | str | None:
362
+ ) -> Optional[str | tuple[str, list[FileAttachment]]]:
279
363
  if result.isError:
280
364
  # Log more detailed error information
281
365
  error_content = None
@@ -293,26 +377,41 @@ class FastMCPClient:
293
377
  )
294
378
  return f"ERROR: Tool call failed - {error_content}"
295
379
 
296
- has_nontext_results = any(
297
- not isinstance(item, TextContent) for item in result.content
298
- )
299
- if has_nontext_results:
300
- self.logger.warning(
301
- f"""
302
- MCP Tool {tool_name} returned non-text results,
303
- which will be skipped.
304
- """,
305
- )
306
- results = [
380
+ results_text = [
307
381
  item.text for item in result.content if isinstance(item, TextContent)
308
382
  ]
309
- if len(results) == 1:
310
- return results[0]
311
- return results
383
+ results_file = []
384
+
385
+ for item in result.content:
386
+ if isinstance(item, ImageContent) and self.forward_images:
387
+ results_file.append(
388
+ FileAttachment.from_bytes(
389
+ b64decode(item.data),
390
+ mime_type=item.mimeType,
391
+ )
392
+ )
393
+ elif isinstance(item, EmbeddedResource):
394
+ if (
395
+ isinstance(item.resource, TextResourceContents)
396
+ and self.forward_text_resources
397
+ ):
398
+ results_text.append(item.resource.text)
399
+ elif (
400
+ isinstance(item.resource, BlobResourceContents)
401
+ and self.forward_blob_resources
402
+ ):
403
+ results_file.append(
404
+ FileAttachment.from_io(
405
+ BytesIO(b64decode(item.resource.blob)),
406
+ mime_type=item.resource.mimeType,
407
+ )
408
+ )
409
+
410
+ return "\n".join(results_text), results_file
312
411
 
313
412
  async def call_mcp_tool(
314
413
  self, tool_name: str, arguments: Dict[str, Any]
315
- ) -> str | List[str] | None:
414
+ ) -> Optional[tuple[str, list[FileAttachment]]]:
316
415
  """Call an MCP tool with the given arguments.
317
416
 
318
417
  Args:
@@ -323,12 +422,23 @@ class FastMCPClient:
323
422
  The result of the tool call.
324
423
  """
325
424
  if not self.client:
326
- raise RuntimeError("Client not initialized. Use async with FastMCPClient.")
425
+ if self.persist_connection:
426
+ await self.connect()
427
+ assert self.client
428
+ else:
429
+ raise RuntimeError(
430
+ "Client not initialized. Use async with FastMCPClient."
431
+ )
327
432
  result: CallToolResult = await self.client.session.call_tool(
328
433
  tool_name,
329
434
  arguments,
330
435
  )
331
- return self._convert_tool_result(tool_name, result)
436
+ results = self._convert_tool_result(tool_name, result)
437
+
438
+ if isinstance(results, str):
439
+ return results, []
440
+
441
+ return results
332
442
 
333
443
 
334
444
  # ==============================================================================
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.54.1
3
+ Version: 0.54.2
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -3,7 +3,7 @@ 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=9ZA2PhluFXReeqtGr7mdOUZZ7AXX6Eg_YymzdniUB-E,80181
6
+ langroid/agent/base.py,sha256=a45iqoWet2W60h4wUIOyHDYlotgS0asqIjfbONT4fZQ,85706
7
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
@@ -56,7 +56,7 @@ langroid/agent/tools/segment_extract_tool.py,sha256=__srZ_VGYLVOdPrITUM8S0HpmX4q
56
56
  langroid/agent/tools/tavily_search_tool.py,sha256=soI-j0HdgVQLf09wRQScaEK4b5RpAX9C4cwOivRFWWI,1903
57
57
  langroid/agent/tools/mcp/__init__.py,sha256=DJNM0VeFnFS3pJKCyFGggT8JVjVu0rBzrGzasT1HaSM,387
58
58
  langroid/agent/tools/mcp/decorators.py,sha256=h7dterhsmvWJ8q4mp_OopmuG2DF71ty8cZwOyzdDZuk,1127
59
- langroid/agent/tools/mcp/fastmcp_client.py,sha256=WF3MhksDH2MzwXZF8cilMhux0hUmj6Z0dDdBYQMZwRs,18008
59
+ langroid/agent/tools/mcp/fastmcp_client.py,sha256=rxdNRinJoxFLbuTEAy7gVocC0jFRwcwDcoz9KAJN7sg,22068
60
60
  langroid/cachedb/__init__.py,sha256=G2KyNnk3Qkhv7OKyxTOnpsxfDycx3NY0O_wXkJlalNY,96
61
61
  langroid/cachedb/base.py,sha256=ztVjB1DtN6pLCujCWnR6xruHxwVj3XkYniRTYAKKqk0,1354
62
62
  langroid/cachedb/redis_cachedb.py,sha256=7kgnbf4b5CKsCrlL97mHWKvdvlLt8zgn7lc528jEpiE,5141
@@ -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.54.1.dist-info/METADATA,sha256=uamTXuB82_ONKDncZCXu8Lz_Xu1VpL2DT56kaaFLbDY,65395
137
- langroid-0.54.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
138
- langroid-0.54.1.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
139
- langroid-0.54.1.dist-info/RECORD,,
136
+ langroid-0.54.2.dist-info/METADATA,sha256=MyoCOXBAWkcArSbJp4sYz9ORHKlYwpcyo2zOAUxe5TQ,65395
137
+ langroid-0.54.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
138
+ langroid-0.54.2.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
139
+ langroid-0.54.2.dist-info/RECORD,,