langroid 0.2.3__py3-none-any.whl → 0.2.4__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/task.py +12 -11
- langroid/cachedb/redis_cachedb.py +1 -1
- langroid/parsing/routing.py +29 -20
- {langroid-0.2.3.dist-info → langroid-0.2.4.dist-info}/METADATA +7 -1
- {langroid-0.2.3.dist-info → langroid-0.2.4.dist-info}/RECORD +8 -8
- pyproject.toml +2 -1
- {langroid-0.2.3.dist-info → langroid-0.2.4.dist-info}/LICENSE +0 -0
- {langroid-0.2.3.dist-info → langroid-0.2.4.dist-info}/WHEEL +0 -0
langroid/agent/task.py
CHANGED
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|
3
3
|
import asyncio
|
4
4
|
import copy
|
5
5
|
import logging
|
6
|
+
import re
|
6
7
|
import threading
|
7
8
|
from collections import Counter, deque
|
8
9
|
from pathlib import Path
|
@@ -256,7 +257,7 @@ class Task:
|
|
256
257
|
# how many 2-step-apart alternations of no_answer step-result have we had,
|
257
258
|
# i.e. x1, N/A, x2, N/A, x3, N/A ...
|
258
259
|
self.n_no_answer_alternations = 0
|
259
|
-
self._no_answer_step: int = -
|
260
|
+
self._no_answer_step: int = -5
|
260
261
|
self._step_idx = -1 # current step index
|
261
262
|
self.max_stalled_steps = max_stalled_steps
|
262
263
|
self.done_if_response = [r.value for r in done_if_response]
|
@@ -579,7 +580,7 @@ class Task:
|
|
579
580
|
self.reset_all_sub_tasks()
|
580
581
|
|
581
582
|
self.n_stalled_steps = 0
|
582
|
-
self._no_answer_step = -
|
583
|
+
self._no_answer_step = -5 # last step where the best explicit response was N/A
|
583
584
|
# how many N/A alternations have we had so far? (for Inf loop detection)
|
584
585
|
self.n_no_answer_alternations = 0
|
585
586
|
self.max_cost = max_cost
|
@@ -639,6 +640,7 @@ class Task:
|
|
639
640
|
self.config.inf_loop_cycle_len > 0
|
640
641
|
and i % self.config.inf_loop_cycle_len == 0
|
641
642
|
and self._maybe_infinite_loop()
|
643
|
+
or self.n_no_answer_alternations > self.config.inf_loop_wait_factor
|
642
644
|
):
|
643
645
|
raise InfiniteLoopException(
|
644
646
|
"""Possible infinite loop detected!
|
@@ -704,7 +706,7 @@ class Task:
|
|
704
706
|
self.reset_all_sub_tasks()
|
705
707
|
|
706
708
|
self.n_stalled_steps = 0
|
707
|
-
self._no_answer_step = -
|
709
|
+
self._no_answer_step = -5 # last step where the best explicit response was N/A
|
708
710
|
# how many N/A alternations have we had so far? (for Inf loop detection)
|
709
711
|
self.n_no_answer_alternations = 0
|
710
712
|
self.max_cost = max_cost
|
@@ -761,6 +763,7 @@ class Task:
|
|
761
763
|
self.config.inf_loop_cycle_len > 0
|
762
764
|
and i % self.config.inf_loop_cycle_len == 0
|
763
765
|
and self._maybe_infinite_loop()
|
766
|
+
or self.n_no_answer_alternations > self.config.inf_loop_wait_factor
|
764
767
|
):
|
765
768
|
raise InfiniteLoopException(
|
766
769
|
"""Possible infinite loop detected!
|
@@ -880,7 +883,6 @@ class Task:
|
|
880
883
|
|
881
884
|
if (
|
882
885
|
Entity.USER in self.responders
|
883
|
-
and self.interactive
|
884
886
|
and not self.human_tried
|
885
887
|
and not self.agent.has_tool_message_attempt(self.pending_message)
|
886
888
|
):
|
@@ -932,7 +934,7 @@ class Task:
|
|
932
934
|
if self.is_done:
|
933
935
|
# skip trying other responders in this step
|
934
936
|
break
|
935
|
-
if not found_response: # did not find a
|
937
|
+
if not found_response: # did not find a valid response
|
936
938
|
if no_answer_response:
|
937
939
|
# even though there was no valid response from anyone in this step,
|
938
940
|
# if there was at least one who EXPLICITLY said NO_ANSWER, then
|
@@ -988,7 +990,6 @@ class Task:
|
|
988
990
|
|
989
991
|
if (
|
990
992
|
Entity.USER in self.responders
|
991
|
-
and self.interactive
|
992
993
|
and not self.human_tried
|
993
994
|
and not self.agent.has_tool_message_attempt(self.pending_message)
|
994
995
|
):
|
@@ -1385,8 +1386,6 @@ class Task:
|
|
1385
1386
|
If the set of last (W * m) messages are the same as the
|
1386
1387
|
set of m dominant messages, then we are likely in a loop.
|
1387
1388
|
"""
|
1388
|
-
if self.n_no_answer_alternations > self.config.inf_loop_wait_factor:
|
1389
|
-
return True
|
1390
1389
|
|
1391
1390
|
max_cycle_len = self.config.inf_loop_cycle_len
|
1392
1391
|
if max_cycle_len <= 0:
|
@@ -1453,7 +1452,7 @@ class Task:
|
|
1453
1452
|
result = result or self.pending_message
|
1454
1453
|
user_quit = (
|
1455
1454
|
result is not None
|
1456
|
-
and result.content in USER_QUIT_STRINGS
|
1455
|
+
and (result.content in USER_QUIT_STRINGS or DONE in result.content)
|
1457
1456
|
and result.metadata.sender == Entity.USER
|
1458
1457
|
)
|
1459
1458
|
if self._level == 0 and self.interactive and self.only_user_quits_root:
|
@@ -1524,7 +1523,9 @@ class Task:
|
|
1524
1523
|
return (
|
1525
1524
|
result is not None
|
1526
1525
|
and not self._is_empty_message(result)
|
1527
|
-
|
1526
|
+
# some weaker LLMs, including even GPT-4o, may say "DO-NOT-KNOW."
|
1527
|
+
# (with a punctuation at the end), so need to strip out punctuation
|
1528
|
+
and re.sub(r"[,.!?:]", "", result.content.strip()) != NO_ANSWER
|
1528
1529
|
)
|
1529
1530
|
|
1530
1531
|
def log_message(
|
@@ -1605,7 +1606,7 @@ class Task:
|
|
1605
1606
|
return (
|
1606
1607
|
self.pending_message is not None
|
1607
1608
|
and (recipient := self.pending_message.metadata.recipient) != ""
|
1608
|
-
and recipient
|
1609
|
+
and not (recipient == e) # case insensitive for entities
|
1609
1610
|
and recipient != e.name
|
1610
1611
|
and recipient != self.name # case sensitive
|
1611
1612
|
)
|
langroid/parsing/routing.py
CHANGED
@@ -5,23 +5,32 @@ from typing import Optional, Tuple
|
|
5
5
|
def parse_addressed_message(
|
6
6
|
content: str, addressing: str = "@"
|
7
7
|
) -> Tuple[Optional[str], str]:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
8
|
+
"""In a message-string containing possibly multiple @<recipient> occurrences,
|
9
|
+
find the last addressee and extract their name,
|
10
|
+
and the message content following it.
|
11
|
+
|
12
|
+
E.g. "thank you @bob, now I will ask @alice again. @alice, where is the mirror?" =>
|
13
|
+
("alice", "where is the mirror?")
|
14
|
+
|
15
|
+
Args:
|
16
|
+
content (str): The message content.
|
17
|
+
addressing (str, optional): The addressing character. Defaults to "@".
|
18
|
+
|
19
|
+
Returns:
|
20
|
+
Tuple[Optional[str], str]:
|
21
|
+
A tuple containing the last addressee and the subsequent message content.
|
22
|
+
"""
|
23
|
+
# Regex to find all occurrences of the pattern
|
24
|
+
pattern = re.compile(rf"{re.escape(addressing)}(\w+)[^\w]")
|
25
|
+
matches = list(pattern.finditer(content))
|
26
|
+
|
27
|
+
if not matches:
|
28
|
+
return None, content # No addressee found, return None and original content
|
29
|
+
|
30
|
+
# Get the last match
|
31
|
+
last_match = matches[-1]
|
32
|
+
last_addressee = last_match.group(1)
|
33
|
+
# Extract content after the last addressee
|
34
|
+
content_after = content[last_match.end() :].strip()
|
35
|
+
|
36
|
+
return last_addressee, content_after
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.4
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
License: MIT
|
6
6
|
Author: Prasad Chalasani
|
@@ -149,6 +149,12 @@ This Multi-Agent paradigm is inspired by the
|
|
149
149
|
`Langroid` is a fresh take on LLM app-development, where considerable thought has gone
|
150
150
|
into simplifying the developer experience; it does not use `Langchain`.
|
151
151
|
|
152
|
+
Companies are using/adapting Langroid in production. Here is a quote from one of them:
|
153
|
+
|
154
|
+
>[Nullify](https://www.nullify.ai) uses AI Agents for secure software development.
|
155
|
+
> It finds, prioritizes and fixes vulnerabilities. We have internally adapted Langroid's multi-agent orchestration framework in production, after evaluating CrewAI, Autogen, LangChain, Langflow, etc. We found Langroid to be far superior to those frameworks in terms of ease of setup and flexibility. Langroid's Agent and Task abstractions are intuitive, well thought out, and provide a great developer experience. We wanted the quickest way to get something in production. With other frameworks it would have taken us weeks, but with Langroid we got to good results in minutes. Highly recommended! <br> -- Jacky Wong, Head of AI at Nullify.
|
156
|
+
|
157
|
+
|
152
158
|
:fire: See this [Intro to Langroid](https://lancedb.substack.com/p/langoid-multi-agent-programming-framework)
|
153
159
|
blog post from the LanceDB team
|
154
160
|
|
@@ -32,7 +32,7 @@ langroid/agent/special/sql/utils/populate_metadata.py,sha256=1J22UsyEPKzwK0XlJZt
|
|
32
32
|
langroid/agent/special/sql/utils/system_message.py,sha256=qKLHkvQWRQodTtPLPxr1GSLUYUFASZU8x-ybV67cB68,1885
|
33
33
|
langroid/agent/special/sql/utils/tools.py,sha256=vFYysk6Vi7HJjII8B4RitA3pt_z3gkSglDNdhNVMiFc,1332
|
34
34
|
langroid/agent/special/table_chat_agent.py,sha256=d9v2wsblaRx7oMnKhLV7uO_ujvk9gh59pSGvBXyeyNc,9659
|
35
|
-
langroid/agent/task.py,sha256=
|
35
|
+
langroid/agent/task.py,sha256=SDDIGE4heOq5v-FeFXnRwQZg2kmayz1xxkSVHbVYWXE,72421
|
36
36
|
langroid/agent/tool_message.py,sha256=wIyZnUcZpxkiRPvM9O3MO3b5BBAdLEEan9kqPbvtApc,9743
|
37
37
|
langroid/agent/tools/__init__.py,sha256=e-63cfwQNk_ftRKQwgDAJQK16QLbRVWDBILeXIc7wLk,402
|
38
38
|
langroid/agent/tools/duckduckgo_search_tool.py,sha256=NhsCaGZkdv28nja7yveAhSK_w6l_Ftym8agbrdzqgfo,1935
|
@@ -49,7 +49,7 @@ langroid/agent_config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
49
|
langroid/cachedb/__init__.py,sha256=icAT2s7Vhf-ZGUeqpDQGNU6ob6o0aFEyjwcxxUGRFjg,225
|
50
50
|
langroid/cachedb/base.py,sha256=ztVjB1DtN6pLCujCWnR6xruHxwVj3XkYniRTYAKKqk0,1354
|
51
51
|
langroid/cachedb/momento_cachedb.py,sha256=YEOJ62hEcV6iIeMr5aGgRYgWQqFYaej9gEDEcY0sm7M,3172
|
52
|
-
langroid/cachedb/redis_cachedb.py,sha256=
|
52
|
+
langroid/cachedb/redis_cachedb.py,sha256=7kgnbf4b5CKsCrlL97mHWKvdvlLt8zgn7lc528jEpiE,5141
|
53
53
|
langroid/embedding_models/__init__.py,sha256=lsu8qxCjfGujXGueJWU-VI3LMZYGjLSYgqUKDd4F3Qo,715
|
54
54
|
langroid/embedding_models/base.py,sha256=MSjaTkFcfoMGY6SHPOqAsbZbKctj8-1N6zgaFYmOFTg,1830
|
55
55
|
langroid/embedding_models/clustering.py,sha256=tZWElUqXl9Etqla0FAa7og96iDKgjqWjucZR_Egtp-A,6684
|
@@ -84,7 +84,7 @@ langroid/parsing/para_sentence_split.py,sha256=AJBzZojP3zpB-_IMiiHismhqcvkrVBQ3Z
|
|
84
84
|
langroid/parsing/parse_json.py,sha256=tgB_oatcrgt6L9ZplC-xBBXjLzL1gjSQf1L2_W5kwFA,4230
|
85
85
|
langroid/parsing/parser.py,sha256=AgtmlVUvrkSG1l7-YZPX8rlldgXjh_HqXAMqpXkBxUo,11746
|
86
86
|
langroid/parsing/repo_loader.py,sha256=3GjvPJS6Vf5L6gV2zOU8s-Tf1oq_fZm-IB_RL_7CTsY,29373
|
87
|
-
langroid/parsing/routing.py,sha256
|
87
|
+
langroid/parsing/routing.py,sha256=-FcnlqldzL4ZoxuDwXjQPNHgBe9F9-F4R6q7b_z9CvI,1232
|
88
88
|
langroid/parsing/search.py,sha256=plQtjarB9afGfJLB0CyPXPq3mM4m7kRsfd0_4brziEI,8846
|
89
89
|
langroid/parsing/spider.py,sha256=Y6y7b86Y2k770LdhxgjVlImBxuuy1V9n8-XQ3QPaG5s,3199
|
90
90
|
langroid/parsing/table_loader.py,sha256=qNM4obT_0Y4tjrxNBCNUYjKQ9oETCZ7FbolKBTcz-GM,3410
|
@@ -128,8 +128,8 @@ langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3Hmh
|
|
128
128
|
langroid/vector_store/momento.py,sha256=QaPzUnTwlswoawGB-paLtUPyLRvckFXLfLDfvbTzjNQ,10505
|
129
129
|
langroid/vector_store/qdrant_cloud.py,sha256=3im4Mip0QXLkR6wiqVsjV1QvhSElfxdFSuDKddBDQ-4,188
|
130
130
|
langroid/vector_store/qdrantdb.py,sha256=wYOuu5c2vIKn9ZgvTXcAiZXMpV8AOXEWFAzI8S8UP-0,16828
|
131
|
-
pyproject.toml,sha256=
|
132
|
-
langroid-0.2.
|
133
|
-
langroid-0.2.
|
134
|
-
langroid-0.2.
|
135
|
-
langroid-0.2.
|
131
|
+
pyproject.toml,sha256=Ajah39xCTJlCOESIJfnsB4XVpaH35tu5WfRjBaeQrfg,6964
|
132
|
+
langroid-0.2.4.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
133
|
+
langroid-0.2.4.dist-info/METADATA,sha256=QtGvmN78Kc33QlxCTO_d-IscvyqRGds7Wel_YK8J6Ok,53962
|
134
|
+
langroid-0.2.4.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
|
135
|
+
langroid-0.2.4.dist-info/RECORD,,
|
pyproject.toml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "langroid"
|
3
|
-
version = "0.2.
|
3
|
+
version = "0.2.4"
|
4
4
|
description = "Harness LLMs with Multi-Agent Programming"
|
5
5
|
authors = ["Prasad Chalasani <pchalasani@gmail.com>"]
|
6
6
|
readme = "README.md"
|
@@ -231,3 +231,4 @@ lint.extend-ignore = ["F821"]
|
|
231
231
|
|
232
232
|
[tool.pytest.ini_options]
|
233
233
|
filterwarnings = ["ignore::DeprecationWarning"]
|
234
|
+
|
File without changes
|
File without changes
|