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 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 = -1
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 = -1 # last step where the best explicit response was N/A
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 = -1 # last step where the best explicit response was N/A
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 Non-NO_ANSWER response
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
- and result.content.strip() != NO_ANSWER
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 != e # case insensitive
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
  )
@@ -54,7 +54,7 @@ class RedisCache(CacheDB):
54
54
  host=redis_host,
55
55
  port=redis_port,
56
56
  password=redis_password,
57
- max_connections=50,
57
+ max_connections=500,
58
58
  socket_timeout=5,
59
59
  socket_keepalive=True,
60
60
  retry_on_timeout=True,
@@ -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
- # escape special characters in addressing prefix for regex use
9
- addressing_escaped = re.escape(addressing)
10
- pattern = rf"{addressing_escaped}(\w+)[,:\s]?"
11
- # Regular expression to find a username prefixed by addressing character or string
12
- match = re.findall(pattern, content)
13
-
14
- addressee = None
15
- if match:
16
- # select the last match as the addressee
17
- addressee = match[-1]
18
-
19
- # Remove the last occurrence of the addressing prefix followed by the
20
- # username and optional punctuation or whitespace
21
- # To remove only the last occurrence, we'll construct a new pattern that
22
- # specifically matches the last addressee
23
- last_occurrence_pattern = rf"{addressing_escaped}{addressee}[,:\\s]?"
24
- # Replace the last occurrence found in the content
25
- content = re.sub(last_occurrence_pattern, "", content, count=1).strip()
26
-
27
- return addressee, content
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
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=ALqGmqzqUNaZB3u8jReBcsC8rRti3WqsN82EMm4CFAo,72189
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=h12NxUeaCcQB06NQwmjm_NU-hc5HQw0fGg3f_MHAzcE,5140
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=_NFPe7wLjd5B6s47w3M8-5vldL8e2Sz51Gb5bwF5ooY,1072
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=MX6Rg0hLOiPlYpb9q4G0H0jLdqAjNWSI_tnKXB8mkuQ,6963
132
- langroid-0.2.3.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
133
- langroid-0.2.3.dist-info/METADATA,sha256=1GD93aMG3wEtL7pE3NBXM1gxjyjEO0Og5XdtNGb3UGA,53146
134
- langroid-0.2.3.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
135
- langroid-0.2.3.dist-info/RECORD,,
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"
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
+