langroid 0.18.0__py3-none-any.whl → 0.18.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 +5 -1
- langroid/agent/callbacks/chainlit.py +4 -1
- langroid/agent/special/doc_chat_agent.py +15 -6
- langroid/agent/tools/orchestration.py +3 -2
- langroid/parsing/document_parser.py +20 -7
- langroid/utils/system.py +32 -8
- {langroid-0.18.0.dist-info → langroid-0.18.2.dist-info}/METADATA +3 -1
- {langroid-0.18.0.dist-info → langroid-0.18.2.dist-info}/RECORD +11 -11
- pyproject.toml +1 -1
- {langroid-0.18.0.dist-info → langroid-0.18.2.dist-info}/LICENSE +0 -0
- {langroid-0.18.0.dist-info → langroid-0.18.2.dist-info}/WHEEL +0 -0
langroid/agent/base.py
CHANGED
@@ -91,6 +91,8 @@ class AgentConfig(BaseSettings):
|
|
91
91
|
show_stats: bool = True # show token usage/cost stats?
|
92
92
|
add_to_registry: bool = True # register agent in ObjectRegistry?
|
93
93
|
respond_tools_only: bool = False # respond only to tool messages (not plain text)?
|
94
|
+
# allow multiple tool messages in a single response?
|
95
|
+
allow_multiple_tools: bool = True
|
94
96
|
|
95
97
|
@validator("name")
|
96
98
|
def check_name_alphanum(cls, v: str) -> str:
|
@@ -1120,6 +1122,8 @@ class Agent(ABC):
|
|
1120
1122
|
# as a response to the tool message even though the tool was not intended
|
1121
1123
|
# for this agent.
|
1122
1124
|
return None
|
1125
|
+
if len(tools) > 1 and not self.config.allow_multiple_tools:
|
1126
|
+
return self.to_ChatDocument("ERROR: Use ONE tool at a time!")
|
1123
1127
|
if len(tools) == 0:
|
1124
1128
|
fallback_result = self.handle_message_fallback(msg)
|
1125
1129
|
if fallback_result is None:
|
@@ -1316,7 +1320,7 @@ class Agent(ABC):
|
|
1316
1320
|
"""
|
1317
1321
|
Convert result of a responder (agent_response or llm_response, or task.run()),
|
1318
1322
|
or tool handler, or handle_message_fallback,
|
1319
|
-
to a ChatDocument, to
|
1323
|
+
to a ChatDocument, to enable handling by other
|
1320
1324
|
responders/tasks in a task loop possibly involving multiple agents.
|
1321
1325
|
|
1322
1326
|
Args:
|
@@ -244,8 +244,11 @@ class ChainlitAgentCallbacks:
|
|
244
244
|
agent.callbacks.show_error_message = self.show_error_message
|
245
245
|
agent.callbacks.show_start_response = self.show_start_response
|
246
246
|
self.config = config
|
247
|
-
|
248
247
|
self.agent: lr.Agent = agent
|
248
|
+
if self.agent.llm is not None:
|
249
|
+
# We don't want to suppress LLM output in async + streaming,
|
250
|
+
# since we often use chainlit async callbacks to display LLM output
|
251
|
+
self.agent.llm.config.async_stream_quiet = False
|
249
252
|
if msg is not None:
|
250
253
|
self.show_first_user_message(msg)
|
251
254
|
|
@@ -248,12 +248,6 @@ class DocChatAgent(ChatAgent):
|
|
248
248
|
def ingest(self) -> None:
|
249
249
|
"""
|
250
250
|
Chunk + embed + store docs specified by self.config.doc_paths
|
251
|
-
|
252
|
-
Returns:
|
253
|
-
dict with keys:
|
254
|
-
n_splits: number of splits
|
255
|
-
urls: list of urls
|
256
|
-
paths: list of file paths
|
257
251
|
"""
|
258
252
|
if len(self.config.doc_paths) == 0:
|
259
253
|
# we must be using a previously defined collection
|
@@ -1110,6 +1104,14 @@ class DocChatAgent(ChatAgent):
|
|
1110
1104
|
Returns:
|
1111
1105
|
|
1112
1106
|
"""
|
1107
|
+
|
1108
|
+
if (
|
1109
|
+
self.vecdb is None
|
1110
|
+
or self.vecdb.config.collection_name
|
1111
|
+
not in self.vecdb.list_collections(empty=False)
|
1112
|
+
):
|
1113
|
+
return []
|
1114
|
+
|
1113
1115
|
# if we are using cross-encoder reranking or reciprocal rank fusion (RRF),
|
1114
1116
|
# we can retrieve more docs during retrieval, and leave it to the cross-encoder
|
1115
1117
|
# or RRF reranking to whittle down to self.config.parsing.n_similar_docs
|
@@ -1275,6 +1277,13 @@ class DocChatAgent(ChatAgent):
|
|
1275
1277
|
List[Document]: list of relevant extracts
|
1276
1278
|
|
1277
1279
|
"""
|
1280
|
+
if (
|
1281
|
+
self.vecdb is None
|
1282
|
+
or self.vecdb.config.collection_name
|
1283
|
+
not in self.vecdb.list_collections(empty=False)
|
1284
|
+
):
|
1285
|
+
return query, []
|
1286
|
+
|
1278
1287
|
if len(self.dialog) > 0 and not self.config.assistant_mode:
|
1279
1288
|
# Regardless of whether we are in conversation mode or not,
|
1280
1289
|
# for relevant doc/chunk extraction, we must convert the query
|
@@ -200,12 +200,13 @@ class DonePassTool(PassTool):
|
|
200
200
|
|
201
201
|
|
202
202
|
class ForwardTool(PassTool):
|
203
|
-
"""Tool for forwarding the received msg (ChatDocument) to another agent.
|
203
|
+
"""Tool for forwarding the received msg (ChatDocument) to another agent or entity.
|
204
204
|
Similar to PassTool, but with a specified recipient agent.
|
205
205
|
"""
|
206
206
|
|
207
207
|
purpose: str = """
|
208
|
-
To forward the current message to an <agent
|
208
|
+
To forward the current message to an <agent>, where <agent>
|
209
|
+
could be the name of an agent, or an entity such as "user", "llm".
|
209
210
|
"""
|
210
211
|
request: str = "forward_tool"
|
211
212
|
agent: str
|
@@ -81,13 +81,25 @@ def is_plain_text(path_or_bytes: str | bytes) -> bool:
|
|
81
81
|
else:
|
82
82
|
content = path_or_bytes[:1024]
|
83
83
|
try:
|
84
|
+
# Use magic to detect the MIME type
|
85
|
+
import magic
|
86
|
+
|
87
|
+
mime_type = magic.from_buffer(content, mime=True)
|
88
|
+
|
89
|
+
# Check if the MIME type is not a text type
|
90
|
+
if not mime_type.startswith("text/"):
|
91
|
+
return False
|
92
|
+
|
84
93
|
# Attempt to decode the content as UTF-8
|
85
94
|
content = content[: find_last_full_char(content)]
|
86
95
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
96
|
+
try:
|
97
|
+
_ = content.decode("utf-8")
|
98
|
+
# Additional checks can go here, e.g., to verify that the content
|
99
|
+
# doesn't contain too many unusual characters for it to be considered text
|
100
|
+
return True
|
101
|
+
except UnicodeDecodeError:
|
102
|
+
return False
|
91
103
|
except UnicodeDecodeError:
|
92
104
|
# If decoding fails, it's likely not plain text (or not encoded in UTF-8)
|
93
105
|
return False
|
@@ -123,7 +135,8 @@ class DocumentParser(Parser):
|
|
123
135
|
Returns:
|
124
136
|
DocumentParser: An instance of a DocumentParser subclass.
|
125
137
|
"""
|
126
|
-
|
138
|
+
inferred_doc_type = DocumentParser._document_type(source, doc_type)
|
139
|
+
if inferred_doc_type == DocumentType.PDF:
|
127
140
|
if config.pdf.library == "fitz":
|
128
141
|
return FitzPDFParser(source, config)
|
129
142
|
elif config.pdf.library == "pypdf":
|
@@ -138,7 +151,7 @@ class DocumentParser(Parser):
|
|
138
151
|
raise ValueError(
|
139
152
|
f"Unsupported PDF library specified: {config.pdf.library}"
|
140
153
|
)
|
141
|
-
elif
|
154
|
+
elif inferred_doc_type == DocumentType.DOCX:
|
142
155
|
if config.docx.library == "unstructured":
|
143
156
|
return UnstructuredDocxParser(source, config)
|
144
157
|
elif config.docx.library == "python-docx":
|
@@ -147,7 +160,7 @@ class DocumentParser(Parser):
|
|
147
160
|
raise ValueError(
|
148
161
|
f"Unsupported DOCX library specified: {config.docx.library}"
|
149
162
|
)
|
150
|
-
elif
|
163
|
+
elif inferred_doc_type == DocumentType.DOC:
|
151
164
|
return UnstructuredDocParser(source, config)
|
152
165
|
else:
|
153
166
|
source_name = source if isinstance(source, str) else "bytes"
|
langroid/utils/system.py
CHANGED
@@ -10,7 +10,7 @@ import socket
|
|
10
10
|
import traceback
|
11
11
|
import uuid
|
12
12
|
from pathlib import Path
|
13
|
-
from typing import Any
|
13
|
+
from typing import Any, Literal
|
14
14
|
|
15
15
|
logger = logging.getLogger(__name__)
|
16
16
|
|
@@ -186,24 +186,48 @@ def generate_unique_id() -> str:
|
|
186
186
|
return str(uuid.uuid4())
|
187
187
|
|
188
188
|
|
189
|
-
def create_file(
|
189
|
+
def create_file(
|
190
|
+
filepath: str | Path,
|
191
|
+
content: str = "",
|
192
|
+
if_exists: Literal["overwrite", "skip", "error", "append"] = "overwrite",
|
193
|
+
) -> None:
|
190
194
|
"""
|
191
|
-
Create a file with the given content
|
195
|
+
Create, overwrite or append to a file, with the given content
|
196
|
+
at the specified filepath.
|
192
197
|
If content is empty, it will simply touch to create an empty file.
|
193
198
|
|
194
199
|
Args:
|
195
200
|
filepath (str|Path): The relative path of the file to be created
|
196
201
|
content (str): The content to be written to the file
|
202
|
+
if_exists (Literal["overwrite", "skip", "error", "append"]):
|
203
|
+
Action to take if file exists
|
197
204
|
"""
|
198
|
-
Path(filepath)
|
199
|
-
|
200
|
-
|
205
|
+
filepath = Path(filepath)
|
206
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
207
|
+
|
208
|
+
if filepath.exists():
|
209
|
+
if if_exists == "skip":
|
210
|
+
logger.warning(f"File already exists, skipping: {filepath}")
|
211
|
+
return
|
212
|
+
elif if_exists == "error":
|
213
|
+
raise FileExistsError(f"File already exists: {filepath}")
|
214
|
+
elif if_exists == "append":
|
215
|
+
mode = "a"
|
216
|
+
else: # overwrite
|
217
|
+
mode = "w"
|
218
|
+
else:
|
219
|
+
mode = "w"
|
220
|
+
|
221
|
+
if content == "" and mode in ["a", "w"]:
|
222
|
+
filepath.touch()
|
223
|
+
logger.warning(f"Empty file created: {filepath}")
|
201
224
|
else:
|
202
225
|
# the newline = '\n` argument is used to ensure that
|
203
226
|
# newlines in the content are written as actual line breaks
|
204
|
-
with open(filepath,
|
227
|
+
with open(filepath, mode, newline="\n") as f:
|
205
228
|
f.write(content)
|
206
|
-
|
229
|
+
action = "appended to" if mode == "a" else "created/updated in"
|
230
|
+
logger.warning(f"Content {action}: {filepath}")
|
207
231
|
|
208
232
|
|
209
233
|
def read_file(path: str, line_numbers: bool = False) -> str:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.18.
|
3
|
+
Version: 0.18.2
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
License: MIT
|
6
6
|
Author: Prasad Chalasani
|
@@ -244,6 +244,8 @@ teacher_task.run()
|
|
244
244
|
<summary> <b>Click to expand</b></summary>
|
245
245
|
|
246
246
|
- **Oct 2024:**
|
247
|
+
- **[0.18.0]** [LLMConfig.async_stream_quiet](https://langroid.github.io/langroid/notes/async-streaming/) flag to
|
248
|
+
turn off LLM output in async + stream mode.
|
247
249
|
- **[0.17.0]** XML-based tools, see [docs](https://langroid.github.io/langroid/notes/xml-tools/).
|
248
250
|
- **Sep 2024:**
|
249
251
|
- **[0.16.0](https://github.com/langroid/langroid/releases/tag/0.16.0)** Support for OpenAI `o1-mini` and `o1-preview` models.
|
@@ -1,16 +1,16 @@
|
|
1
1
|
langroid/__init__.py,sha256=z_fCOLQJPOw3LLRPBlFB5-2HyCjpPgQa4m4iY5Fvb8Y,1800
|
2
2
|
langroid/agent/__init__.py,sha256=ll0Cubd2DZ-fsCMl7e10hf9ZjFGKzphfBco396IKITY,786
|
3
|
-
langroid/agent/base.py,sha256=
|
3
|
+
langroid/agent/base.py,sha256=tJFil49Us2wRv2oKbikqrmcBv6KAeM-UdZ7Psrr-jjE,64282
|
4
4
|
langroid/agent/batch.py,sha256=QZdlt1563hx4l3AXrCaGovE-PNG93M3DsvQAbDzdiS8,13705
|
5
5
|
langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
langroid/agent/callbacks/chainlit.py,sha256=
|
6
|
+
langroid/agent/callbacks/chainlit.py,sha256=oiZYfPyfkUfFqhVFBiobN3hAJEaYn0FBSglvNE7t17Q,22302
|
7
7
|
langroid/agent/chat_agent.py,sha256=m1fs-XsBzusRicwBETId3dpTQG8qEaS0yym_o4mfAI0,49931
|
8
8
|
langroid/agent/chat_document.py,sha256=FZ_PkeKU5OVp1IUlMvspfqxIXzlyd7J_F32DSYrxQ7E,17651
|
9
9
|
langroid/agent/helpers.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
10
10
|
langroid/agent/junk,sha256=LxfuuW7Cijsg0szAzT81OjWWv1PMNI-6w_-DspVIO2s,339
|
11
11
|
langroid/agent/openai_assistant.py,sha256=2rjCZw45ysNBEGNzQM4uf0bTC4KkatGYAWcVcW4xcek,34337
|
12
12
|
langroid/agent/special/__init__.py,sha256=gik_Xtm_zV7U9s30Mn8UX3Gyuy4jTjQe9zjiE3HWmEo,1273
|
13
|
-
langroid/agent/special/doc_chat_agent.py,sha256=
|
13
|
+
langroid/agent/special/doc_chat_agent.py,sha256=xIqBOyLax_jMU0UevxqXf_aQUrRkW6MQUKpKnKvaqkQ,59281
|
14
14
|
langroid/agent/special/lance_doc_chat_agent.py,sha256=s8xoRs0gGaFtDYFUSIRchsgDVbS5Q3C2b2mr3V1Fd-Q,10419
|
15
15
|
langroid/agent/special/lance_rag/__init__.py,sha256=QTbs0IVE2ZgDg8JJy1zN97rUUg4uEPH7SLGctFNumk4,174
|
16
16
|
langroid/agent/special/lance_rag/critic_agent.py,sha256=OtFuHthKQLkdVkvuZ2m0GNq1qOYLqHkm1pfLRFnSg5c,9548
|
@@ -40,7 +40,7 @@ langroid/agent/tools/duckduckgo_search_tool.py,sha256=NhsCaGZkdv28nja7yveAhSK_w6
|
|
40
40
|
langroid/agent/tools/file_tools.py,sha256=GjPB5YDILucYapElnvvoYpGJuZQ25ecLs2REv7edPEo,7292
|
41
41
|
langroid/agent/tools/google_search_tool.py,sha256=y7b-3FtgXf0lfF4AYxrZ3K5pH2dhidvibUOAGBE--WI,1456
|
42
42
|
langroid/agent/tools/metaphor_search_tool.py,sha256=qj4gt453cLEX3EGW7nVzVu6X7LCdrwjSlcNY0qJW104,2489
|
43
|
-
langroid/agent/tools/orchestration.py,sha256=
|
43
|
+
langroid/agent/tools/orchestration.py,sha256=u_iQDwD5L9JgE39pCemmIRG3MxyrBsw6KpQYf7pH-Ek,10881
|
44
44
|
langroid/agent/tools/recipient_tool.py,sha256=0m2kQhYKTeGujAxhSPqH5z6hSAhVB_Dqour6uul2U30,9427
|
45
45
|
langroid/agent/tools/retrieval_tool.py,sha256=2q2pfoYbZNfbWQ0McxrtmfF0ekGglIgRl-6uF26pa-E,871
|
46
46
|
langroid/agent/tools/rewind_tool.py,sha256=XAXL3BpNhCmBGYq_qi_sZfHJuIw7NY2jp4wnojJ7WRs,5606
|
@@ -82,7 +82,7 @@ langroid/parsing/agent_chats.py,sha256=sbZRV9ujdM5QXvvuHVjIi2ysYSYlap-uqfMMUKulr
|
|
82
82
|
langroid/parsing/code-parsing.md,sha256=--cyyNiSZSDlIwcjAV4-shKrSiRe2ytF3AdSoS_hD2g,3294
|
83
83
|
langroid/parsing/code_parser.py,sha256=AOxb3xbYpTBPP3goOm5dKfJdh5hS_2BhLVCEkifWZN8,3796
|
84
84
|
langroid/parsing/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
85
|
-
langroid/parsing/document_parser.py,sha256=
|
85
|
+
langroid/parsing/document_parser.py,sha256=ZGxgG4ytnCIah4HWk3ZrK3EGAMXDjAzI9KaPEXCbMm0,24589
|
86
86
|
langroid/parsing/image_text.py,sha256=sbLIQ5nHe2UnYUksBaQsmZGaX-X0qgEpPd7CEzi_z5M,910
|
87
87
|
langroid/parsing/para_sentence_split.py,sha256=AJBzZojP3zpB-_IMiiHismhqcvkrVBQ3ZINoQyx_bE4,2000
|
88
88
|
langroid/parsing/parse_json.py,sha256=aADo38bAHQhC8on4aWZZzVzSDy-dK35vRLZsFI2ewh8,4756
|
@@ -125,7 +125,7 @@ langroid/utils/output/printing.py,sha256=yzPJZN-8_jyOJmI9N_oLwEDfjMwVgk3IDiwnZ4e
|
|
125
125
|
langroid/utils/output/status.py,sha256=rzbE7mDJcgNNvdtylCseQcPGCGghtJvVq3lB-OPJ49E,1049
|
126
126
|
langroid/utils/pandas_utils.py,sha256=UctS986Jtl_MvU5rA7-GfrjEHXP7MNu8ePhepv0bTn0,755
|
127
127
|
langroid/utils/pydantic_utils.py,sha256=iRy7uQhHhQmIDZTTPNX5jXb6fqefMe9N67p3fPfOmTI,20624
|
128
|
-
langroid/utils/system.py,sha256=
|
128
|
+
langroid/utils/system.py,sha256=AiEehQy0K9c9qHdKsZRCscRrazDzuh5Tv3GRQsA0Cxg,8455
|
129
129
|
langroid/utils/types.py,sha256=4GrOnU3HLWh-UwaUPp7LlB3V413q3K5OSzc0ggDoQ6A,2510
|
130
130
|
langroid/utils/web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
131
131
|
langroid/utils/web/login.py,sha256=1iz9eUAHa87vpKIkzwkmFa00avwFWivDSAr7QUhK7U0,2528
|
@@ -137,8 +137,8 @@ langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3Hmh
|
|
137
137
|
langroid/vector_store/momento.py,sha256=qR-zBF1RKVHQZPZQYW_7g-XpTwr46p8HJuYPCkfJbM4,10534
|
138
138
|
langroid/vector_store/qdrant_cloud.py,sha256=3im4Mip0QXLkR6wiqVsjV1QvhSElfxdFSuDKddBDQ-4,188
|
139
139
|
langroid/vector_store/qdrantdb.py,sha256=v88lqFkepADvlN6lByUj9I4NEKa9X9lWH16uTPPbYrE,17457
|
140
|
-
pyproject.toml,sha256=
|
141
|
-
langroid-0.18.
|
142
|
-
langroid-0.18.
|
143
|
-
langroid-0.18.
|
144
|
-
langroid-0.18.
|
140
|
+
pyproject.toml,sha256=Vh1LYkE26eWAeHJABpWjRbXvTclFi7B6Lrh8LfuhJjU,7179
|
141
|
+
langroid-0.18.2.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
142
|
+
langroid-0.18.2.dist-info/METADATA,sha256=NFKC5gncBypWwo7Y0ErHW93oGtrNvaa41VNj_bZW2Cs,56486
|
143
|
+
langroid-0.18.2.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
144
|
+
langroid-0.18.2.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|