langroid 0.2.2__tar.gz → 0.2.4__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.
- {langroid-0.2.2 → langroid-0.2.4}/PKG-INFO +7 -1
- {langroid-0.2.2 → langroid-0.2.4}/README.md +6 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/task.py +42 -26
- {langroid-0.2.2 → langroid-0.2.4}/langroid/cachedb/redis_cachedb.py +1 -1
- langroid-0.2.4/langroid/parsing/routing.py +36 -0
- {langroid-0.2.2 → langroid-0.2.4}/pyproject.toml +2 -1
- langroid-0.2.2/langroid/parsing/routing.py +0 -27
- {langroid-0.2.2 → langroid-0.2.4}/LICENSE +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/base.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/batch.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/callbacks/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/callbacks/chainlit.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/chat_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/chat_document.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/helpers.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/junk +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/openai_assistant.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/doc_chat_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/lance_rag/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/lance_rag/lance_rag_task.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/lance_tools.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/neo4j/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/neo4j/utils/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/neo4j/utils/system_message.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/relevance_extractor_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/retriever_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/sql/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/sql/utils/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/sql/utils/system_message.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/sql/utils/tools.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/table_chat_agent.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tool_message.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/extract_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/generator_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/google_search_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/metaphor_search_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/recipient_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/retrieval_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/rewind_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/run_python_code.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent/tools/segment_extract_tool.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/agent_config.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/cachedb/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/cachedb/base.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/cachedb/momento_cachedb.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/base.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/clustering.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/models.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/protoc/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/protoc/embeddings.proto +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/embedding_models/remote_embeds.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/exceptions.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/azure_openai.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/base.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/config.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/mock_lm.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/openai_gpt.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/prompt_formatter/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/prompt_formatter/base.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/utils.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/mytypes.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/agent_chats.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/code-parsing.md +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/code_parser.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/config.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/document_parser.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/image_text.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/para_sentence_split.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/parse_json.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/parser.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/repo_loader.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/search.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/spider.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/table_loader.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/url_loader.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/url_loader_cookies.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/urls.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/utils.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/parsing/web_search.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/prompts/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/prompts/chat-gpt4-system-prompt.md +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/prompts/dialog.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/prompts/prompts_config.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/prompts/templates.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/pydantic_v1/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/pydantic_v1/main.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/algorithms/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/algorithms/graph.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/configuration.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/constants.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/docker.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/globals.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/llms/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/llms/strings.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/logging.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/object_registry.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/output/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/output/citations.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/output/printing.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/output/status.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/pandas_utils.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/pydantic_utils.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/system.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/web/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/utils/web/login.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/vector_store/__init__.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/vector_store/base.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/vector_store/chromadb.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/vector_store/lancedb.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/vector_store/meilisearch.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/vector_store/momento.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/vector_store/qdrant_cloud.py +0 -0
- {langroid-0.2.2 → langroid-0.2.4}/langroid/vector_store/qdrantdb.py +0 -0
@@ -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
|
|
@@ -46,6 +46,12 @@ This Multi-Agent paradigm is inspired by the
|
|
46
46
|
`Langroid` is a fresh take on LLM app-development, where considerable thought has gone
|
47
47
|
into simplifying the developer experience; it does not use `Langchain`.
|
48
48
|
|
49
|
+
Companies are using/adapting Langroid in production. Here is a quote from one of them:
|
50
|
+
|
51
|
+
>[Nullify](https://www.nullify.ai) uses AI Agents for secure software development.
|
52
|
+
> 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.
|
53
|
+
|
54
|
+
|
49
55
|
:fire: See this [Intro to Langroid](https://lancedb.substack.com/p/langoid-multi-agent-programming-framework)
|
50
56
|
blog post from the LanceDB team
|
51
57
|
|
@@ -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
|
@@ -184,8 +185,8 @@ class Task:
|
|
184
185
|
Note: erasing can reduce prompt sizes, but results in repetitive
|
185
186
|
sub-task delegation.
|
186
187
|
allow_null_result (bool):
|
187
|
-
If true,
|
188
|
-
|
188
|
+
If true, create dummy NO_ANSWER response when no valid response is found
|
189
|
+
in a step.
|
189
190
|
Optional, default is False.
|
190
191
|
*Note:* In non-interactive mode, when this is set to True,
|
191
192
|
you can have a situation where an LLM generates (non-tool) text,
|
@@ -193,7 +194,9 @@ class Task:
|
|
193
194
|
is inserted as a dummy response from the User entity, so the LLM
|
194
195
|
will now respond to this Null result, and this will continue
|
195
196
|
until the LLM emits a DONE signal (if instructed to do so),
|
196
|
-
otherwise
|
197
|
+
otherwise langroid detects a potential infinite loop after
|
198
|
+
a certain number of such steps (= `TaskConfig.inf_loop_wait_factor`)
|
199
|
+
and will raise an InfiniteLoopException.
|
197
200
|
max_stalled_steps (int): task considered done after this many consecutive
|
198
201
|
steps with no progress. Default is 3.
|
199
202
|
done_if_no_response (List[Responder]): consider task done if NULL
|
@@ -254,7 +257,7 @@ class Task:
|
|
254
257
|
# how many 2-step-apart alternations of no_answer step-result have we had,
|
255
258
|
# i.e. x1, N/A, x2, N/A, x3, N/A ...
|
256
259
|
self.n_no_answer_alternations = 0
|
257
|
-
self._no_answer_step: int = -
|
260
|
+
self._no_answer_step: int = -5
|
258
261
|
self._step_idx = -1 # current step index
|
259
262
|
self.max_stalled_steps = max_stalled_steps
|
260
263
|
self.done_if_response = [r.value for r in done_if_response]
|
@@ -577,7 +580,7 @@ class Task:
|
|
577
580
|
self.reset_all_sub_tasks()
|
578
581
|
|
579
582
|
self.n_stalled_steps = 0
|
580
|
-
self._no_answer_step = -
|
583
|
+
self._no_answer_step = -5 # last step where the best explicit response was N/A
|
581
584
|
# how many N/A alternations have we had so far? (for Inf loop detection)
|
582
585
|
self.n_no_answer_alternations = 0
|
583
586
|
self.max_cost = max_cost
|
@@ -637,6 +640,7 @@ class Task:
|
|
637
640
|
self.config.inf_loop_cycle_len > 0
|
638
641
|
and i % self.config.inf_loop_cycle_len == 0
|
639
642
|
and self._maybe_infinite_loop()
|
643
|
+
or self.n_no_answer_alternations > self.config.inf_loop_wait_factor
|
640
644
|
):
|
641
645
|
raise InfiniteLoopException(
|
642
646
|
"""Possible infinite loop detected!
|
@@ -702,7 +706,7 @@ class Task:
|
|
702
706
|
self.reset_all_sub_tasks()
|
703
707
|
|
704
708
|
self.n_stalled_steps = 0
|
705
|
-
self._no_answer_step = -
|
709
|
+
self._no_answer_step = -5 # last step where the best explicit response was N/A
|
706
710
|
# how many N/A alternations have we had so far? (for Inf loop detection)
|
707
711
|
self.n_no_answer_alternations = 0
|
708
712
|
self.max_cost = max_cost
|
@@ -759,6 +763,7 @@ class Task:
|
|
759
763
|
self.config.inf_loop_cycle_len > 0
|
760
764
|
and i % self.config.inf_loop_cycle_len == 0
|
761
765
|
and self._maybe_infinite_loop()
|
766
|
+
or self.n_no_answer_alternations > self.config.inf_loop_wait_factor
|
762
767
|
):
|
763
768
|
raise InfiniteLoopException(
|
764
769
|
"""Possible infinite loop detected!
|
@@ -878,7 +883,6 @@ class Task:
|
|
878
883
|
|
879
884
|
if (
|
880
885
|
Entity.USER in self.responders
|
881
|
-
and self.interactive
|
882
886
|
and not self.human_tried
|
883
887
|
and not self.agent.has_tool_message_attempt(self.pending_message)
|
884
888
|
):
|
@@ -930,7 +934,7 @@ class Task:
|
|
930
934
|
if self.is_done:
|
931
935
|
# skip trying other responders in this step
|
932
936
|
break
|
933
|
-
if not found_response: # did not find a
|
937
|
+
if not found_response: # did not find a valid response
|
934
938
|
if no_answer_response:
|
935
939
|
# even though there was no valid response from anyone in this step,
|
936
940
|
# if there was at least one who EXPLICITLY said NO_ANSWER, then
|
@@ -986,7 +990,6 @@ class Task:
|
|
986
990
|
|
987
991
|
if (
|
988
992
|
Entity.USER in self.responders
|
989
|
-
and self.interactive
|
990
993
|
and not self.human_tried
|
991
994
|
and not self.agent.has_tool_message_attempt(self.pending_message)
|
992
995
|
):
|
@@ -1047,15 +1050,10 @@ class Task:
|
|
1047
1050
|
self._show_pending_message_if_debug()
|
1048
1051
|
return self.pending_message
|
1049
1052
|
|
1050
|
-
def
|
1051
|
-
|
1052
|
-
|
1053
|
-
parent: ChatDocument | None,
|
1054
|
-
result: ChatDocument,
|
1055
|
-
) -> None:
|
1056
|
-
"""Processes valid result from a responder, during a step"""
|
1053
|
+
def _update_no_answer_vars(self, result: ChatDocument) -> None:
|
1054
|
+
"""Update variables related to NO_ANSWER responses, to aid
|
1055
|
+
in alternating NO_ANSWER infinite-loop detection."""
|
1057
1056
|
|
1058
|
-
# in case the valid response was a NO_ANSWER,
|
1059
1057
|
if NO_ANSWER in result.content:
|
1060
1058
|
if self._no_answer_step == self._step_idx - 2:
|
1061
1059
|
# N/A two steps ago
|
@@ -1067,6 +1065,16 @@ class Task:
|
|
1067
1065
|
# record the last step where the best explicit response was N/A
|
1068
1066
|
self._no_answer_step = self._step_idx
|
1069
1067
|
|
1068
|
+
def _process_valid_responder_result(
|
1069
|
+
self,
|
1070
|
+
r: Responder,
|
1071
|
+
parent: ChatDocument | None,
|
1072
|
+
result: ChatDocument,
|
1073
|
+
) -> None:
|
1074
|
+
"""Processes valid result from a responder, during a step"""
|
1075
|
+
|
1076
|
+
self._update_no_answer_vars(result)
|
1077
|
+
|
1070
1078
|
# pending_sender is of type Responder,
|
1071
1079
|
# i.e. it is either one of the agent's entities
|
1072
1080
|
# OR a sub-task, that has produced a valid response.
|
@@ -1131,6 +1139,7 @@ class Task:
|
|
1131
1139
|
metadata=ChatDocMetaData(sender=responder, parent_id=parent_id),
|
1132
1140
|
)
|
1133
1141
|
self.pending_sender = responder
|
1142
|
+
self._update_no_answer_vars(self.pending_message)
|
1134
1143
|
self.log_message(self.pending_sender, self.pending_message, mark=True)
|
1135
1144
|
|
1136
1145
|
def _show_pending_message_if_debug(self) -> None:
|
@@ -1346,13 +1355,20 @@ class Task:
|
|
1346
1355
|
def _maybe_infinite_loop(self) -> bool:
|
1347
1356
|
"""
|
1348
1357
|
Detect possible infinite loop based on message frequencies.
|
1349
|
-
NOTE: This
|
1350
|
-
|
1358
|
+
NOTE: This detects two types of loops:
|
1359
|
+
- Alternating NO_ANSWER loops, specifically of the form
|
1360
|
+
x1 NO_ANSWER x2 NO_ANSWER x3 NO_ANSWER...
|
1361
|
+
(e.g. an LLM repeatedly saying something different, and another responder
|
1362
|
+
or sub-task saying NO_ANSWER -- i.e. "DO-NOT-KNOW")
|
1363
|
+
|
1364
|
+
- "exact" loops, i.e. a cycle of messages that repeats exactly, e.g.
|
1351
1365
|
a r b i t r a t e r a t e r a t e r a t e ...
|
1352
1366
|
|
1353
|
-
[It does not detect "approximate" loops, where
|
1354
|
-
|
1367
|
+
[It does not detect more general "approximate" loops, where two entities are
|
1368
|
+
responding to each other potentially forever, with (slightly) different
|
1369
|
+
messages each time]
|
1355
1370
|
|
1371
|
+
Here is the logic for the exact-loop detection:
|
1356
1372
|
Intuition: when you look at a sufficiently long sequence with an m-message
|
1357
1373
|
loop, then the frequencies of these m messages will "dominate" those
|
1358
1374
|
of all other messages.
|
@@ -1370,8 +1386,6 @@ class Task:
|
|
1370
1386
|
If the set of last (W * m) messages are the same as the
|
1371
1387
|
set of m dominant messages, then we are likely in a loop.
|
1372
1388
|
"""
|
1373
|
-
if self.n_no_answer_alternations > self.config.inf_loop_wait_factor:
|
1374
|
-
return True
|
1375
1389
|
|
1376
1390
|
max_cycle_len = self.config.inf_loop_cycle_len
|
1377
1391
|
if max_cycle_len <= 0:
|
@@ -1438,7 +1452,7 @@ class Task:
|
|
1438
1452
|
result = result or self.pending_message
|
1439
1453
|
user_quit = (
|
1440
1454
|
result is not None
|
1441
|
-
and result.content in USER_QUIT_STRINGS
|
1455
|
+
and (result.content in USER_QUIT_STRINGS or DONE in result.content)
|
1442
1456
|
and result.metadata.sender == Entity.USER
|
1443
1457
|
)
|
1444
1458
|
if self._level == 0 and self.interactive and self.only_user_quits_root:
|
@@ -1509,7 +1523,9 @@ class Task:
|
|
1509
1523
|
return (
|
1510
1524
|
result is not None
|
1511
1525
|
and not self._is_empty_message(result)
|
1512
|
-
|
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
|
1513
1529
|
)
|
1514
1530
|
|
1515
1531
|
def log_message(
|
@@ -1590,7 +1606,7 @@ class Task:
|
|
1590
1606
|
return (
|
1591
1607
|
self.pending_message is not None
|
1592
1608
|
and (recipient := self.pending_message.metadata.recipient) != ""
|
1593
|
-
and recipient
|
1609
|
+
and not (recipient == e) # case insensitive for entities
|
1594
1610
|
and recipient != e.name
|
1595
1611
|
and recipient != self.name # case sensitive
|
1596
1612
|
)
|
@@ -0,0 +1,36 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Optional, Tuple
|
3
|
+
|
4
|
+
|
5
|
+
def parse_addressed_message(
|
6
|
+
content: str, addressing: str = "@"
|
7
|
+
) -> Tuple[Optional[str], str]:
|
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
|
[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
|
+
|
@@ -1,27 +0,0 @@
|
|
1
|
-
import re
|
2
|
-
from typing import Optional, Tuple
|
3
|
-
|
4
|
-
|
5
|
-
def parse_addressed_message(
|
6
|
-
content: str, addressing: str = "@"
|
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
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{langroid-0.2.2 → langroid-0.2.4}/langroid/agent/special/sql/utils/description_extractors.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{langroid-0.2.2 → langroid-0.2.4}/langroid/language_models/prompt_formatter/llama2_formatter.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|