langroid 0.59.12__tar.gz → 0.59.14__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.
Files changed (146) hide show
  1. {langroid-0.59.12 → langroid-0.59.14}/PKG-INFO +1 -1
  2. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/chat_agent.py +6 -2
  3. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/doc_chat_agent.py +110 -22
  4. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/base.py +15 -0
  5. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/base.py +52 -0
  6. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/qdrantdb.py +8 -0
  7. {langroid-0.59.12 → langroid-0.59.14}/pyproject.toml +1 -1
  8. {langroid-0.59.12 → langroid-0.59.14}/.gitignore +0 -0
  9. {langroid-0.59.12 → langroid-0.59.14}/LICENSE +0 -0
  10. {langroid-0.59.12 → langroid-0.59.14}/README.md +0 -0
  11. {langroid-0.59.12 → langroid-0.59.14}/langroid/__init__.py +0 -0
  12. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/__init__.py +0 -0
  13. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/base.py +0 -0
  14. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/batch.py +0 -0
  15. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/callbacks/__init__.py +0 -0
  16. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/callbacks/chainlit.py +0 -0
  17. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/chat_document.py +0 -0
  18. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/done_sequence_parser.py +0 -0
  19. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/openai_assistant.py +0 -0
  20. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/__init__.py +0 -0
  21. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/arangodb/__init__.py +0 -0
  22. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/arangodb/arangodb_agent.py +0 -0
  23. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/arangodb/system_messages.py +0 -0
  24. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/arangodb/tools.py +0 -0
  25. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/arangodb/utils.py +0 -0
  26. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/doc_chat_task.py +0 -0
  27. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
  28. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/lance_rag/__init__.py +0 -0
  29. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
  30. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/lance_rag/lance_rag_task.py +0 -0
  31. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
  32. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/lance_tools.py +0 -0
  33. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/neo4j/__init__.py +0 -0
  34. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
  35. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
  36. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/neo4j/system_messages.py +0 -0
  37. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/neo4j/tools.py +0 -0
  38. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/relevance_extractor_agent.py +0 -0
  39. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/retriever_agent.py +0 -0
  40. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/sql/__init__.py +0 -0
  41. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
  42. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/sql/utils/__init__.py +0 -0
  43. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
  44. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
  45. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/sql/utils/system_message.py +0 -0
  46. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/sql/utils/tools.py +0 -0
  47. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/special/table_chat_agent.py +0 -0
  48. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/task.py +0 -0
  49. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tool_message.py +0 -0
  50. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/__init__.py +0 -0
  51. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
  52. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/exa_search_tool.py +0 -0
  53. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/file_tools.py +0 -0
  54. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/google_search_tool.py +0 -0
  55. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/mcp/__init__.py +0 -0
  56. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/mcp/decorators.py +0 -0
  57. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/mcp/fastmcp_client.py +0 -0
  58. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/metaphor_search_tool.py +0 -0
  59. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/orchestration.py +0 -0
  60. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/recipient_tool.py +0 -0
  61. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/retrieval_tool.py +0 -0
  62. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/rewind_tool.py +0 -0
  63. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/segment_extract_tool.py +0 -0
  64. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/task_tool.py +0 -0
  65. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/tools/tavily_search_tool.py +0 -0
  66. {langroid-0.59.12 → langroid-0.59.14}/langroid/agent/xml_tool_message.py +0 -0
  67. {langroid-0.59.12 → langroid-0.59.14}/langroid/cachedb/__init__.py +0 -0
  68. {langroid-0.59.12 → langroid-0.59.14}/langroid/cachedb/base.py +0 -0
  69. {langroid-0.59.12 → langroid-0.59.14}/langroid/cachedb/redis_cachedb.py +0 -0
  70. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/__init__.py +0 -0
  71. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/models.py +0 -0
  72. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/protoc/__init__.py +0 -0
  73. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/protoc/embeddings.proto +0 -0
  74. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
  75. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
  76. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
  77. {langroid-0.59.12 → langroid-0.59.14}/langroid/embedding_models/remote_embeds.py +0 -0
  78. {langroid-0.59.12 → langroid-0.59.14}/langroid/exceptions.py +0 -0
  79. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/__init__.py +0 -0
  80. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/azure_openai.py +0 -0
  81. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/base.py +0 -0
  82. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/client_cache.py +0 -0
  83. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/config.py +0 -0
  84. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/mock_lm.py +0 -0
  85. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/model_info.py +0 -0
  86. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/openai_gpt.py +0 -0
  87. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/prompt_formatter/__init__.py +0 -0
  88. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/prompt_formatter/base.py +0 -0
  89. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
  90. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
  91. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/provider_params.py +0 -0
  92. {langroid-0.59.12 → langroid-0.59.14}/langroid/language_models/utils.py +0 -0
  93. {langroid-0.59.12 → langroid-0.59.14}/langroid/mcp/__init__.py +0 -0
  94. {langroid-0.59.12 → langroid-0.59.14}/langroid/mcp/server/__init__.py +0 -0
  95. {langroid-0.59.12 → langroid-0.59.14}/langroid/mytypes.py +0 -0
  96. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/__init__.py +0 -0
  97. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/agent_chats.py +0 -0
  98. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/code_parser.py +0 -0
  99. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/document_parser.py +0 -0
  100. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/file_attachment.py +0 -0
  101. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/md_parser.py +0 -0
  102. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/para_sentence_split.py +0 -0
  103. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/parse_json.py +0 -0
  104. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/parser.py +0 -0
  105. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/pdf_utils.py +0 -0
  106. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/repo_loader.py +0 -0
  107. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/routing.py +0 -0
  108. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/search.py +0 -0
  109. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/spider.py +0 -0
  110. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/table_loader.py +0 -0
  111. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/url_loader.py +0 -0
  112. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/urls.py +0 -0
  113. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/utils.py +0 -0
  114. {langroid-0.59.12 → langroid-0.59.14}/langroid/parsing/web_search.py +0 -0
  115. {langroid-0.59.12 → langroid-0.59.14}/langroid/prompts/__init__.py +0 -0
  116. {langroid-0.59.12 → langroid-0.59.14}/langroid/prompts/dialog.py +0 -0
  117. {langroid-0.59.12 → langroid-0.59.14}/langroid/prompts/prompts_config.py +0 -0
  118. {langroid-0.59.12 → langroid-0.59.14}/langroid/prompts/templates.py +0 -0
  119. {langroid-0.59.12 → langroid-0.59.14}/langroid/py.typed +0 -0
  120. {langroid-0.59.12 → langroid-0.59.14}/langroid/pydantic_v1/__init__.py +0 -0
  121. {langroid-0.59.12 → langroid-0.59.14}/langroid/pydantic_v1/main.py +0 -0
  122. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/__init__.py +0 -0
  123. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/algorithms/__init__.py +0 -0
  124. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/algorithms/graph.py +0 -0
  125. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/configuration.py +0 -0
  126. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/constants.py +0 -0
  127. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/git_utils.py +0 -0
  128. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/globals.py +0 -0
  129. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/html_logger.py +0 -0
  130. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/logging.py +0 -0
  131. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/object_registry.py +0 -0
  132. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/output/__init__.py +0 -0
  133. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/output/citations.py +0 -0
  134. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/output/printing.py +0 -0
  135. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/output/status.py +0 -0
  136. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/pandas_utils.py +0 -0
  137. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/pydantic_utils.py +0 -0
  138. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/system.py +0 -0
  139. {langroid-0.59.12 → langroid-0.59.14}/langroid/utils/types.py +0 -0
  140. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/__init__.py +0 -0
  141. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/chromadb.py +0 -0
  142. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/lancedb.py +0 -0
  143. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/meilisearch.py +0 -0
  144. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/pineconedb.py +0 -0
  145. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/postgres.py +0 -0
  146. {langroid-0.59.12 → langroid-0.59.14}/langroid/vector_store/weaviatedb.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.59.12
3
+ Version: 0.59.14
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -279,13 +279,17 @@ class ChatAgent(Agent):
279
279
  new_agent.llm_functions_handled = self.llm_functions_handled
280
280
  new_agent.llm_functions_usable = self.llm_functions_usable
281
281
  new_agent.llm_function_force = self.llm_function_force
282
- # Caution - we are copying the vector-db, maybe we don't always want this?
283
- new_agent.vecdb = self.vecdb
282
+ # Ensure each clone gets its own vecdb client when supported.
283
+ new_agent.vecdb = None if self.vecdb is None else self.vecdb.clone()
284
+ self._clone_extra_state(new_agent)
284
285
  new_agent.id = ObjectRegistry.new_id()
285
286
  if self.config.add_to_registry:
286
287
  ObjectRegistry.register_object(new_agent)
287
288
  return new_agent
288
289
 
290
+ def _clone_extra_state(self, new_agent: "ChatAgent") -> None:
291
+ """Hook for subclasses to copy additional state into clones."""
292
+
289
293
  def _strict_mode_for_tool(self, tool: str | type[ToolMessage]) -> bool:
290
294
  """Should we enable strict mode for a given tool?"""
291
295
  if isinstance(tool, str):
@@ -15,11 +15,24 @@ pip install "langroid[hf-embeddings]"
15
15
  """
16
16
 
17
17
  import asyncio
18
+ import copy
18
19
  import importlib
19
20
  import logging
21
+ import threading
20
22
  from collections import OrderedDict
23
+ from dataclasses import dataclass
21
24
  from functools import cache
22
- from typing import Any, Callable, Dict, List, Optional, Set, Tuple, no_type_check
25
+ from typing import (
26
+ TYPE_CHECKING,
27
+ Any,
28
+ Callable,
29
+ Dict,
30
+ List,
31
+ Optional,
32
+ Set,
33
+ Tuple,
34
+ no_type_check,
35
+ )
23
36
 
24
37
  import nest_asyncio
25
38
  import numpy as np
@@ -39,7 +52,7 @@ from langroid.embedding_models.models import (
39
52
  OpenAIEmbeddingsConfig,
40
53
  SentenceTransformerEmbeddingsConfig,
41
54
  )
42
- from langroid.language_models.base import StreamingIfAllowed
55
+ from langroid.language_models.base import LLMConfig, StreamingIfAllowed
43
56
  from langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig
44
57
  from langroid.mytypes import DocMetaData, Document, Entity
45
58
  from langroid.parsing.document_parser import DocumentType
@@ -66,6 +79,9 @@ from langroid.utils.pydantic_utils import dataframe_to_documents, extract_fields
66
79
  from langroid.vector_store.base import VectorStore, VectorStoreConfig
67
80
  from langroid.vector_store.qdrantdb import QdrantDBConfig
68
81
 
82
+ if TYPE_CHECKING:
83
+ from sentence_transformers import CrossEncoder
84
+
69
85
 
70
86
  @cache
71
87
  def apply_nest_asyncio() -> None:
@@ -75,6 +91,60 @@ def apply_nest_asyncio() -> None:
75
91
  logger = logging.getLogger(__name__)
76
92
 
77
93
 
94
+ @dataclass
95
+ class _CrossEncoderCacheEntry:
96
+ model: "CrossEncoder"
97
+ lock: threading.RLock
98
+
99
+
100
+ _CROSS_ENCODER_CACHE: Dict[str, _CrossEncoderCacheEntry] = {}
101
+ _CROSS_ENCODER_CACHE_LOCK = threading.Lock()
102
+
103
+
104
+ def _auto_cross_encoder_device() -> str:
105
+ try:
106
+ import torch
107
+
108
+ if torch.cuda.is_available():
109
+ return "cuda"
110
+ mps = getattr(torch.backends, "mps", None)
111
+ if mps is not None and mps.is_available():
112
+ return "mps"
113
+ except Exception:
114
+ pass
115
+ return "cpu"
116
+
117
+
118
+ def _get_cross_encoder_entry(
119
+ model_name: str, device: str | None
120
+ ) -> _CrossEncoderCacheEntry:
121
+ actual_device = device or _auto_cross_encoder_device()
122
+ cache_key = f"{model_name}::{actual_device}"
123
+ entry = _CROSS_ENCODER_CACHE.get(cache_key)
124
+ if entry is not None:
125
+ return entry
126
+
127
+ with _CROSS_ENCODER_CACHE_LOCK:
128
+ entry = _CROSS_ENCODER_CACHE.get(cache_key)
129
+ if entry is not None:
130
+ return entry
131
+ try:
132
+ from sentence_transformers import CrossEncoder
133
+ except ImportError as exc:
134
+ raise ImportError(
135
+ """
136
+ To use cross-encoder re-ranking, you must install
137
+ langroid with the [hf-embeddings] extra, e.g.:
138
+ pip install "langroid[hf-embeddings]"
139
+ """
140
+ ) from exc
141
+
142
+ model = CrossEncoder(model_name, device=actual_device)
143
+ entry = _CrossEncoderCacheEntry(model=model, lock=threading.RLock())
144
+ _CROSS_ENCODER_CACHE[cache_key] = entry
145
+ return entry
146
+
147
+
78
148
  DEFAULT_DOC_CHAT_SYSTEM_MESSAGE = """
79
149
  You are a helpful assistant, helping me understand a collection of documents.
80
150
 
@@ -154,6 +224,7 @@ class DocChatAgentConfig(ChatAgentConfig):
154
224
  cross_encoder_reranking_model: str = ( # ignored if use_reciprocal_rank_fusion=True
155
225
  "cross-encoder/ms-marco-MiniLM-L-6-v2" if has_sentence_transformers else ""
156
226
  )
227
+ cross_encoder_device: Optional[str] = None # default to CPU when None
157
228
  rerank_diversity: bool = True # rerank to maximize diversity?
158
229
  rerank_periphery: bool = True # rerank to avoid Lost In the Middle effect?
159
230
  rerank_after_adding_context: bool = True # rerank after adding context window?
@@ -209,7 +280,7 @@ class DocChatAgentConfig(ChatAgentConfig):
209
280
  embedding=hf_embed_config if has_sentence_transformers else oai_embed_config,
210
281
  )
211
282
 
212
- llm: OpenAIGPTConfig = OpenAIGPTConfig(
283
+ llm: LLMConfig = OpenAIGPTConfig(
213
284
  type="openai",
214
285
  chat_model=OpenAIChatModel.GPT4o,
215
286
  completion_model=OpenAIChatModel.GPT4o,
@@ -299,6 +370,19 @@ class DocChatAgent(ChatAgent):
299
370
 
300
371
  self.ingest()
301
372
 
373
+ def _clone_extra_state(self, new_agent: "ChatAgent") -> None:
374
+ super()._clone_extra_state(new_agent)
375
+ for attr in [
376
+ "chunked_docs",
377
+ "chunked_docs_clean",
378
+ "original_docs",
379
+ "original_docs_length",
380
+ "from_dataframe",
381
+ "df_description",
382
+ ]:
383
+ if hasattr(self, attr):
384
+ setattr(new_agent, attr, copy.deepcopy(getattr(self, attr)))
385
+
302
386
  def clear(self) -> None:
303
387
  """Clear the document collection and the specific collection in vecdb"""
304
388
  self.original_docs = []
@@ -1100,19 +1184,13 @@ class DocChatAgent(ChatAgent):
1100
1184
  self, query: str, passages: List[Document]
1101
1185
  ) -> List[Document]:
1102
1186
  with status("[cyan]Re-ranking retrieved chunks using cross-encoder..."):
1103
- try:
1104
- from sentence_transformers import CrossEncoder
1105
- except ImportError:
1106
- raise ImportError(
1107
- """
1108
- To use cross-encoder re-ranking, you must install
1109
- langroid with the [hf-embeddings] extra, e.g.:
1110
- pip install "langroid[hf-embeddings]"
1111
- """
1112
- )
1113
-
1114
- model = CrossEncoder(self.config.cross_encoder_reranking_model)
1115
- scores = model.predict([(query, p.content) for p in passages])
1187
+ device = self.config.cross_encoder_device
1188
+ entry = _get_cross_encoder_entry(
1189
+ self.config.cross_encoder_reranking_model, device
1190
+ )
1191
+ pair_inputs = [(query, p.content) for p in passages]
1192
+ with entry.lock:
1193
+ scores = entry.model.predict(pair_inputs, show_progress_bar=False)
1116
1194
  # Convert to [0,1] so we might could use a cutoff later.
1117
1195
  scores = 1.0 / (1 + np.exp(-np.array(scores)))
1118
1196
  # get top k scoring passages
@@ -1414,6 +1492,7 @@ class DocChatAgent(ChatAgent):
1414
1492
  )
1415
1493
 
1416
1494
  if len(passages) == 0:
1495
+ logger.debug("No passages retrieved for query '%s'", query)
1417
1496
  return []
1418
1497
 
1419
1498
  if self.config.rerank_after_adding_context:
@@ -1467,11 +1546,17 @@ class DocChatAgent(ChatAgent):
1467
1546
  List[Document]: list of relevant extracts
1468
1547
 
1469
1548
  """
1470
- if (
1471
- self.vecdb is None
1472
- or self.vecdb.config.collection_name
1473
- not in self.vecdb.list_collections(empty=False)
1474
- ):
1549
+ collection_name = (
1550
+ None if self.vecdb is None else self.vecdb.config.collection_name
1551
+ )
1552
+ has_vecdb_collection = (
1553
+ collection_name is not None
1554
+ and collection_name in self.vecdb.list_collections(empty=False)
1555
+ if self.vecdb is not None
1556
+ else False
1557
+ )
1558
+
1559
+ if not has_vecdb_collection and len(self.chunked_docs) == 0:
1475
1560
  return query, []
1476
1561
 
1477
1562
  if len(self.dialog) > 0 and not self.config.assistant_mode:
@@ -1491,7 +1576,10 @@ class DocChatAgent(ChatAgent):
1491
1576
  if self.config.n_query_rephrases > 0:
1492
1577
  rephrases = self.llm_rephrase_query(query)
1493
1578
  proxies += rephrases
1494
- passages = self.get_relevant_chunks(query, proxies) # no LLM involved
1579
+ if has_vecdb_collection:
1580
+ passages = self.get_relevant_chunks(query, proxies) # no LLM involved
1581
+ else:
1582
+ passages = self.chunked_docs
1495
1583
 
1496
1584
  if len(passages) == 0:
1497
1585
  return query, []
@@ -21,6 +21,21 @@ class EmbeddingModel(ABC):
21
21
  Abstract base class for an embedding model.
22
22
  """
23
23
 
24
+ def clone(self) -> "EmbeddingModel":
25
+ """
26
+ Return a copy of this embedding model suitable for use in cloned agents.
27
+ Default behaviour attempts to deep-copy the model configuration and
28
+ instantiate a fresh model of the same type; if that is not possible,
29
+ the original instance is reused.
30
+ """
31
+ config = getattr(self, "config", None)
32
+ if config is not None and hasattr(config, "model_copy"):
33
+ try:
34
+ return type(self)(config.model_copy(deep=True)) # type: ignore[call-arg]
35
+ except Exception:
36
+ pass
37
+ return self
38
+
24
39
  @classmethod
25
40
  def create(cls, config: EmbeddingModelsConfig) -> "EmbeddingModel":
26
41
  from langroid.embedding_models.models import (
@@ -52,6 +52,8 @@ class VectorStore(ABC):
52
52
  self.embedding_model = EmbeddingModel.create(config.embedding)
53
53
  else:
54
54
  self.embedding_model = config.embedding_model
55
+ if hasattr(self.config, "embedding_model"):
56
+ self.config.embedding_model = None
55
57
  self.embedding_fn: EmbeddingFunction = self.embedding_model.embedding_fn()
56
58
 
57
59
  @staticmethod
@@ -95,6 +97,56 @@ class VectorStore(ABC):
95
97
  def embedding_dim(self) -> int:
96
98
  return len(self.embedding_fn(["test"])[0])
97
99
 
100
+ def clone(self) -> "VectorStore":
101
+ """Return a vector-store clone suitable for agent cloning.
102
+
103
+ The default implementation deep-copies the configuration, reuses any
104
+ existing embedding model, and instantiates a fresh store of the same
105
+ type. Subclasses can override when sharing the instance is required
106
+ (e.g., embedded/local stores that rely on file locks).
107
+ """
108
+
109
+ config_class = self.config.__class__
110
+ config_data = self.config.model_dump(mode="python")
111
+ config_data["embedding_model"] = None
112
+ config_copy = config_class.model_validate(config_data)
113
+ logger.debug(
114
+ "Cloning VectorStore %s: original collection=%s, copied collection=%s",
115
+ type(self).__name__,
116
+ getattr(self.config, "collection_name", None),
117
+ getattr(config_copy, "collection_name", None),
118
+ )
119
+ # Preserve the calculated collection contents without forcing replaces
120
+ if hasattr(config_copy, "replace_collection"):
121
+ config_copy.replace_collection = False # type: ignore[attr-defined]
122
+ cloned_embedding: Optional[EmbeddingModel] = None
123
+ if (
124
+ hasattr(self, "embedding_model")
125
+ and getattr(self, "embedding_model") is not None
126
+ ):
127
+ cloned_embedding = self.embedding_model.clone() # type: ignore[attr-defined]
128
+ if hasattr(config_copy, "embedding_model"):
129
+ config_copy.embedding_model = cloned_embedding
130
+
131
+ cloned_store = type(self)(config_copy) # type: ignore[call-arg]
132
+ if hasattr(cloned_store.config, "embedding_model"):
133
+ cloned_store.config.embedding_model = None
134
+ logger.debug(
135
+ "Cloned VectorStore %s: cloned collection=%s",
136
+ type(self).__name__,
137
+ getattr(cloned_store.config, "collection_name", None),
138
+ )
139
+ if hasattr(cloned_store.config, "replace_collection"):
140
+ cloned_store.config.replace_collection = False
141
+ # Some stores might not honour replace_collection; ensure same collection
142
+ if getattr(self.config, "collection_name", None) is not None:
143
+ setattr(
144
+ cloned_store.config,
145
+ "collection_name",
146
+ getattr(self.config, "collection_name", None),
147
+ )
148
+ return cloned_store
149
+
98
150
  @abstractmethod
99
151
  def clear_empty_collections(self) -> int:
100
152
  """Clear all empty collections in the vector store.
@@ -143,6 +143,14 @@ class QdrantDB(VectorStore):
143
143
  config.collection_name, replace=config.replace_collection
144
144
  )
145
145
 
146
+ def clone(self) -> "QdrantDB":
147
+ """Create an independent Qdrant client when running against Qdrant Cloud."""
148
+ if not self.config.cloud:
149
+ return self
150
+ cloned = super().clone()
151
+ assert isinstance(cloned, QdrantDB)
152
+ return cloned
153
+
146
154
  def close(self) -> None:
147
155
  """
148
156
  Close the QdrantDB client and release any resources (e.g., file locks).
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "langroid"
3
- version = "0.59.12"
3
+ version = "0.59.14"
4
4
  authors = [
5
5
  {name = "Prasad Chalasani", email = "pchalasani@gmail.com"},
6
6
  ]
File without changes
File without changes
File without changes
File without changes