langroid 0.9.5__tar.gz → 0.10.1__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 (140) hide show
  1. {langroid-0.9.5 → langroid-0.10.1}/PKG-INFO +4 -1
  2. {langroid-0.9.5 → langroid-0.10.1}/README.md +3 -0
  3. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/__init__.py +2 -1
  4. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/base.py +58 -44
  5. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/chat_agent.py +2 -2
  6. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/chat_document.py +3 -1
  7. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/lance_rag/lance_rag_task.py +2 -0
  8. langroid-0.10.1/langroid/agent/structured_message.py +9 -0
  9. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/task.py +10 -5
  10. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tool_message.py +37 -0
  11. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/code_parser.py +2 -0
  12. {langroid-0.9.5 → langroid-0.10.1}/pyproject.toml +1 -1
  13. {langroid-0.9.5 → langroid-0.10.1}/LICENSE +0 -0
  14. {langroid-0.9.5 → langroid-0.10.1}/langroid/__init__.py +0 -0
  15. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/batch.py +0 -0
  16. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/callbacks/__init__.py +0 -0
  17. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/callbacks/chainlit.py +0 -0
  18. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/helpers.py +0 -0
  19. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/junk +0 -0
  20. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/openai_assistant.py +0 -0
  21. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/__init__.py +0 -0
  22. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/doc_chat_agent.py +0 -0
  23. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
  24. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/lance_rag/__init__.py +0 -0
  25. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
  26. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
  27. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/lance_tools.py +0 -0
  28. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/neo4j/__init__.py +0 -0
  29. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
  30. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
  31. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/neo4j/utils/__init__.py +0 -0
  32. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/neo4j/utils/system_message.py +0 -0
  33. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/relevance_extractor_agent.py +0 -0
  34. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/retriever_agent.py +0 -0
  35. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/sql/__init__.py +0 -0
  36. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
  37. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/sql/utils/__init__.py +0 -0
  38. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
  39. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
  40. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/sql/utils/system_message.py +0 -0
  41. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/sql/utils/tools.py +0 -0
  42. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/special/table_chat_agent.py +0 -0
  43. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/__init__.py +0 -0
  44. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
  45. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/extract_tool.py +0 -0
  46. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/generator_tool.py +0 -0
  47. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/google_search_tool.py +0 -0
  48. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/metaphor_search_tool.py +0 -0
  49. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/note_tool.py +0 -0
  50. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/orchestration.py +0 -0
  51. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/recipient_tool.py +0 -0
  52. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/retrieval_tool.py +0 -0
  53. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/rewind_tool.py +0 -0
  54. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/run_python_code.py +0 -0
  55. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/tools/segment_extract_tool.py +0 -0
  56. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent/typed_task.py +0 -0
  57. {langroid-0.9.5 → langroid-0.10.1}/langroid/agent_config.py +0 -0
  58. {langroid-0.9.5 → langroid-0.10.1}/langroid/cachedb/__init__.py +0 -0
  59. {langroid-0.9.5 → langroid-0.10.1}/langroid/cachedb/base.py +0 -0
  60. {langroid-0.9.5 → langroid-0.10.1}/langroid/cachedb/momento_cachedb.py +0 -0
  61. {langroid-0.9.5 → langroid-0.10.1}/langroid/cachedb/redis_cachedb.py +0 -0
  62. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/__init__.py +0 -0
  63. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/base.py +0 -0
  64. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/clustering.py +0 -0
  65. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/models.py +0 -0
  66. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/protoc/__init__.py +0 -0
  67. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/protoc/embeddings.proto +0 -0
  68. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
  69. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
  70. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
  71. {langroid-0.9.5 → langroid-0.10.1}/langroid/embedding_models/remote_embeds.py +0 -0
  72. {langroid-0.9.5 → langroid-0.10.1}/langroid/exceptions.py +0 -0
  73. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/.chainlit/config.toml +0 -0
  74. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/.chainlit/translations/en-US.json +0 -0
  75. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/__init__.py +0 -0
  76. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/azure_openai.py +0 -0
  77. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/base.py +0 -0
  78. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/config.py +0 -0
  79. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/mock_lm.py +0 -0
  80. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/openai_gpt.py +0 -0
  81. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/prompt_formatter/__init__.py +0 -0
  82. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/prompt_formatter/base.py +0 -0
  83. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
  84. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
  85. {langroid-0.9.5 → langroid-0.10.1}/langroid/language_models/utils.py +0 -0
  86. {langroid-0.9.5 → langroid-0.10.1}/langroid/mytypes.py +0 -0
  87. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/__init__.py +0 -0
  88. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/agent_chats.py +0 -0
  89. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/code-parsing.md +0 -0
  90. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/config.py +0 -0
  91. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/document_parser.py +0 -0
  92. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/image_text.py +0 -0
  93. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/para_sentence_split.py +0 -0
  94. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/parse_json.py +0 -0
  95. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/parser.py +0 -0
  96. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/repo_loader.py +0 -0
  97. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/routing.py +0 -0
  98. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/search.py +0 -0
  99. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/spider.py +0 -0
  100. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/table_loader.py +0 -0
  101. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/url_loader.py +0 -0
  102. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/url_loader_cookies.py +0 -0
  103. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/urls.py +0 -0
  104. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/utils.py +0 -0
  105. {langroid-0.9.5 → langroid-0.10.1}/langroid/parsing/web_search.py +0 -0
  106. {langroid-0.9.5 → langroid-0.10.1}/langroid/prompts/__init__.py +0 -0
  107. {langroid-0.9.5 → langroid-0.10.1}/langroid/prompts/chat-gpt4-system-prompt.md +0 -0
  108. {langroid-0.9.5 → langroid-0.10.1}/langroid/prompts/dialog.py +0 -0
  109. {langroid-0.9.5 → langroid-0.10.1}/langroid/prompts/prompts_config.py +0 -0
  110. {langroid-0.9.5 → langroid-0.10.1}/langroid/prompts/templates.py +0 -0
  111. {langroid-0.9.5 → langroid-0.10.1}/langroid/pydantic_v1/__init__.py +0 -0
  112. {langroid-0.9.5 → langroid-0.10.1}/langroid/pydantic_v1/main.py +0 -0
  113. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/__init__.py +0 -0
  114. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/algorithms/__init__.py +0 -0
  115. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/algorithms/graph.py +0 -0
  116. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/configuration.py +0 -0
  117. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/constants.py +0 -0
  118. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/docker.py +0 -0
  119. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/globals.py +0 -0
  120. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/llms/__init__.py +0 -0
  121. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/llms/strings.py +0 -0
  122. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/logging.py +0 -0
  123. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/object_registry.py +0 -0
  124. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/output/__init__.py +0 -0
  125. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/output/citations.py +0 -0
  126. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/output/printing.py +0 -0
  127. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/output/status.py +0 -0
  128. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/pandas_utils.py +0 -0
  129. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/pydantic_utils.py +0 -0
  130. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/system.py +0 -0
  131. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/web/__init__.py +0 -0
  132. {langroid-0.9.5 → langroid-0.10.1}/langroid/utils/web/login.py +0 -0
  133. {langroid-0.9.5 → langroid-0.10.1}/langroid/vector_store/__init__.py +0 -0
  134. {langroid-0.9.5 → langroid-0.10.1}/langroid/vector_store/base.py +0 -0
  135. {langroid-0.9.5 → langroid-0.10.1}/langroid/vector_store/chromadb.py +0 -0
  136. {langroid-0.9.5 → langroid-0.10.1}/langroid/vector_store/lancedb.py +0 -0
  137. {langroid-0.9.5 → langroid-0.10.1}/langroid/vector_store/meilisearch.py +0 -0
  138. {langroid-0.9.5 → langroid-0.10.1}/langroid/vector_store/momento.py +0 -0
  139. {langroid-0.9.5 → langroid-0.10.1}/langroid/vector_store/qdrant_cloud.py +0 -0
  140. {langroid-0.9.5 → langroid-0.10.1}/langroid/vector_store/qdrantdb.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langroid
3
- Version: 0.9.5
3
+ Version: 0.10.1
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  License: MIT
6
6
  Author: Prasad Chalasani
@@ -162,6 +162,9 @@ into simplifying the developer experience; it does not use `Langchain`.
162
162
  :fire: See this [Intro to Langroid](https://lancedb.substack.com/p/langoid-multi-agent-programming-framework)
163
163
  blog post from the LanceDB team
164
164
 
165
+ :fire: Just published in ML for Healthcare (2024): a Langroid-based Multi-Agent RAG system for
166
+ pharmacovigilance, see [blog post](https://langroid.github.io/langroid/blog/2024/08/12/malade-multi-agent-architecture-for-pharmacovigilance/)
167
+
165
168
 
166
169
  We welcome contributions -- See the [contributions](./CONTRIBUTING.md) document
167
170
  for ideas on what to contribute.
@@ -55,6 +55,9 @@ into simplifying the developer experience; it does not use `Langchain`.
55
55
  :fire: See this [Intro to Langroid](https://lancedb.substack.com/p/langoid-multi-agent-programming-framework)
56
56
  blog post from the LanceDB team
57
57
 
58
+ :fire: Just published in ML for Healthcare (2024): a Langroid-based Multi-Agent RAG system for
59
+ pharmacovigilance, see [blog post](https://langroid.github.io/langroid/blog/2024/08/12/malade-multi-agent-architecture-for-pharmacovigilance/)
60
+
58
61
 
59
62
  We welcome contributions -- See the [contributions](./CONTRIBUTING.md) document
60
63
  for ideas on what to contribute.
@@ -6,7 +6,7 @@ from .chat_document import (
6
6
  ChatDocument,
7
7
  )
8
8
  from .chat_agent import ChatAgentConfig, ChatAgent
9
- from .tool_message import ToolMessage
9
+ from .tool_message import ToolMessage, FinalResultTool
10
10
  from .task import Task
11
11
 
12
12
  from . import base
@@ -29,6 +29,7 @@ __all__ = [
29
29
  "ChatAgent",
30
30
  "ChatAgentConfig",
31
31
  "ToolMessage",
32
+ "FinalResultTool",
32
33
  "Task",
33
34
  "base",
34
35
  "chat_document",
@@ -1056,6 +1056,7 @@ class Agent(ABC):
1056
1056
  """
1057
1057
  try:
1058
1058
  tools = self.get_tool_messages(msg)
1059
+ tools = [t for t in tools if self._tool_recipient_match(t)]
1059
1060
  except ValidationError as ve:
1060
1061
  # correct tool name but bad fields
1061
1062
  return self.tool_validation_error(ve)
@@ -1068,9 +1069,12 @@ class Agent(ABC):
1068
1069
  return None
1069
1070
  if len(tools) == 0:
1070
1071
  fallback_result = self.handle_message_fallback(msg)
1071
- if fallback_result is not None and isinstance(fallback_result, ToolMessage):
1072
- return self.create_agent_response(tool_messages=[fallback_result])
1073
- return fallback_result
1072
+ if fallback_result is None:
1073
+ return None
1074
+ return self._process_handle_message_result(
1075
+ fallback_result,
1076
+ chat_doc=msg if isinstance(msg, ChatDocument) else None,
1077
+ )
1074
1078
  has_ids = all([t.id != "" for t in tools])
1075
1079
  chat_doc = msg if isinstance(msg, ChatDocument) else None
1076
1080
 
@@ -1161,9 +1165,7 @@ class Agent(ABC):
1161
1165
  final = "\n\n".join(str_results)
1162
1166
  return final
1163
1167
 
1164
- def handle_message_fallback(
1165
- self, msg: str | ChatDocument
1166
- ) -> str | ChatDocument | None:
1168
+ def handle_message_fallback(self, msg: str | ChatDocument) -> Any:
1167
1169
  """
1168
1170
  Fallback method for the "no-tools" scenario.
1169
1171
  This method can be overridden by subclasses, e.g.,
@@ -1173,8 +1175,7 @@ class Agent(ABC):
1173
1175
  Args:
1174
1176
  msg (str | ChatDocument): The input msg to handle
1175
1177
  Returns:
1176
- str: The result of the handler method in string form so it can
1177
- be handled by LLM
1178
+ Any: The result of the handler method
1178
1179
  """
1179
1180
  return None
1180
1181
 
@@ -1260,6 +1261,52 @@ class Agent(ABC):
1260
1261
  raise ve
1261
1262
  return message
1262
1263
 
1264
+ def _process_handle_message_result(
1265
+ self,
1266
+ msg: Any,
1267
+ orig_tool_name: str | None = None,
1268
+ chat_doc: Optional[ChatDocument] = None,
1269
+ ) -> None | str | ChatDocument:
1270
+ """
1271
+ Process result of agent_response or tool handler, or handle_message_fallback.
1272
+ """
1273
+ if isinstance(msg, ToolMessage):
1274
+ # result is a ToolMessage, so...
1275
+ result_tool_name = msg.default_value("request")
1276
+ if result_tool_name in self.llm_tools_handled and (
1277
+ orig_tool_name is None or orig_tool_name != result_tool_name
1278
+ ):
1279
+ # TODO: do we need to remove the tool message from the chat_doc?
1280
+ # if (chat_doc is not None and
1281
+ # msg in chat_doc.tool_messages):
1282
+ # chat_doc.tool_messages.remove(msg)
1283
+ # if we can handle it, do so
1284
+ result = self.handle_tool_message(msg, chat_doc=chat_doc)
1285
+ else:
1286
+ # else wrap it in an agent response and return it so
1287
+ # orchestrator can find a respondent
1288
+ result = self.create_agent_response(tool_messages=[msg])
1289
+ elif isinstance(msg, (ChatDocument, str)):
1290
+ result = msg
1291
+ elif isinstance(msg, BaseModel):
1292
+ result = msg.json()
1293
+ else:
1294
+ # last resort: use json.dumps() or str() to make it a str
1295
+ try:
1296
+ result = json.dumps(msg)
1297
+ except Exception:
1298
+ try:
1299
+ result = str(msg)
1300
+ except Exception as e:
1301
+ logger.error(
1302
+ f"""
1303
+ Error converting msg handler result to str: {e}",
1304
+ """,
1305
+ exc_info=True,
1306
+ )
1307
+ result = None
1308
+ return result
1309
+
1263
1310
  def handle_tool_message(
1264
1311
  self,
1265
1312
  tool: ToolMessage,
@@ -1289,42 +1336,9 @@ class Agent(ABC):
1289
1336
  maybe_result = handler_method(tool, chat_doc=chat_doc)
1290
1337
  else:
1291
1338
  maybe_result = handler_method(tool)
1292
- if isinstance(maybe_result, ToolMessage):
1293
- # result is a ToolMessage, so...
1294
- result_tool_name = maybe_result.default_value("request")
1295
- if (
1296
- result_tool_name in self.llm_tools_handled
1297
- and tool_name != result_tool_name
1298
- ):
1299
- # TODO: do we need to remove the tool message from the chat_doc?
1300
- # if (chat_doc is not None and
1301
- # maybe_result in chat_doc.tool_messages):
1302
- # chat_doc.tool_messages.remove(maybe_result)
1303
- # if we can handle it, do so
1304
- result = self.handle_tool_message(maybe_result, chat_doc=chat_doc)
1305
- else:
1306
- # else wrap it in an agent response and return it so
1307
- # orchestrator can find a respondent
1308
- result = self.create_agent_response(tool_messages=[maybe_result])
1309
- elif isinstance(maybe_result, (ChatDocument, str)):
1310
- result = maybe_result
1311
- elif isinstance(maybe_result, BaseModel):
1312
- result = maybe_result.json()
1313
- else:
1314
- # last resort: use json.dumps() or str() to make it a str
1315
- try:
1316
- result = json.dumps(maybe_result)
1317
- except Exception:
1318
- try:
1319
- result = str(maybe_result)
1320
- except Exception as e:
1321
- logger.error(
1322
- f"""
1323
- Error converting result of {tool_name} to str: {e}",
1324
- """,
1325
- exc_info=True,
1326
- )
1327
- result = None
1339
+ result = self._process_handle_message_result(
1340
+ maybe_result, tool_name, chat_doc
1341
+ )
1328
1342
  except Exception as e:
1329
1343
  # raise the error here since we are sure it's
1330
1344
  # not a pydantic validation error,
@@ -453,7 +453,7 @@ class ChatAgent(Agent):
453
453
  # remove leading and trailing newlines and other whitespace
454
454
  return LLMMessage(role=Role.SYSTEM, content=content.strip())
455
455
 
456
- def unhanded_tools(self) -> set[str]:
456
+ def unhandled_tools(self) -> set[str]:
457
457
  """The set of tools that are known but not handled.
458
458
  Useful in task flow: an agent can refuse to accept an incoming msg
459
459
  when it only has unhandled tools.
@@ -719,7 +719,7 @@ class ChatAgent(Agent):
719
719
  llm_msgs = ChatDocument.to_LLMMessage(message, self.oai_tool_calls)
720
720
  # LLM only responds to the content, so only those msgs with
721
721
  # non-empty content should be kept
722
- llm_msgs = [m for m in llm_msgs if m.content != ""]
722
+ llm_msgs = [m for m in llm_msgs if m.content.strip() != ""]
723
723
  if len(llm_msgs) == 0:
724
724
  return [], 0
725
725
  # process tools if any
@@ -119,7 +119,9 @@ class ChatDocument(Document):
119
119
  oai_tool_id2result: Optional[OrderedDict[str, str]] = None
120
120
  oai_tool_choice: ToolChoiceTypes | Dict[str, Dict[str, str] | str] = "auto"
121
121
  function_call: Optional[LLMFunctionCall] = None
122
- tool_messages: List[ToolMessage] = [] # only handle-able tools
122
+ # tools that are explicitly added by agent response/handler,
123
+ # or tools recognized in the ChatDocument as handle-able tools
124
+ tool_messages: List[ToolMessage] = []
123
125
  # all known tools in the msg that are in an agent's llm_tools_known list,
124
126
  # even if non-used/handled
125
127
  all_tool_messages: List[ToolMessage] = []
@@ -51,11 +51,13 @@ class LanceRAGTaskCreator:
51
51
  critic_name=critic_name,
52
52
  doc_agent_name=doc_agent_name,
53
53
  doc_schema=agent._get_clean_vecdb_schema(),
54
+ llm=agent.config.llm,
54
55
  )
55
56
  query_plan_agent_config.set_system_message()
56
57
 
57
58
  critic_config = QueryPlanCriticConfig(
58
59
  doc_schema=agent._get_clean_vecdb_schema(),
60
+ llm=agent.config.llm,
59
61
  )
60
62
  critic_config.set_system_message()
61
63
 
@@ -0,0 +1,9 @@
1
+ from langroid.agent.tool_message import ToolMessage
2
+
3
+
4
+ class StructuredMessage(ToolMessage):
5
+ request: str = ""
6
+ purpose: str = "Wrapper for a structured message"
7
+
8
+ # allow subclasses to inhert this "extras allowed" config
9
+ # model_config = ConfigDict(extra=Extra.allow)
@@ -33,7 +33,7 @@ from langroid.agent.chat_document import (
33
33
  ChatDocument,
34
34
  StatusCode,
35
35
  )
36
- from langroid.agent.tool_message import ToolMessage
36
+ from langroid.agent.tool_message import FinalResultTool, ToolMessage
37
37
  from langroid.agent.tools.orchestration import AgentDoneTool, DoneTool
38
38
  from langroid.cachedb.redis_cachedb import RedisCache, RedisCacheConfig
39
39
  from langroid.exceptions import InfiniteLoopException
@@ -1399,14 +1399,18 @@ class Task:
1399
1399
  # if there is an LLMDoneTool or AgentDoneTool among these,
1400
1400
  # we extract content and tools from here, and ignore all others
1401
1401
  for t in tool_messages:
1402
- if isinstance(t, (AgentDoneTool, DoneTool)):
1402
+ if isinstance(t, FinalResultTool):
1403
+ content = ""
1404
+ tool_messages = [t] # pass it on to parent so it also quits
1405
+ break
1406
+ elif isinstance(t, (AgentDoneTool, DoneTool)):
1403
1407
  # there shouldn't be multiple tools like this; just take the first
1404
1408
  content = t.content
1405
1409
  if isinstance(t, AgentDoneTool):
1406
1410
  tool_messages = t.tools
1407
1411
  break
1408
1412
  # drop the "Done" tools since they should not be part of the task result,
1409
- # or else they would cause the parent task to get done!
1413
+ # or else they would cause the parent task to get unintentionally done!
1410
1414
  tool_messages = [
1411
1415
  t for t in tool_messages if not isinstance(t, (DoneTool, AgentDoneTool))
1412
1416
  ]
@@ -1473,7 +1477,7 @@ class Task:
1473
1477
  and (
1474
1478
  DONE in result.content
1475
1479
  or any(
1476
- isinstance(t, (DoneTool, AgentDoneTool))
1480
+ isinstance(t, (DoneTool, AgentDoneTool, FinalResultTool))
1477
1481
  for t in result.tool_messages
1478
1482
  )
1479
1483
  )
@@ -1594,7 +1598,8 @@ class Task:
1594
1598
  done_result = result is not None and (
1595
1599
  DONE in (result.content if isinstance(result, str) else result.content)
1596
1600
  or any(
1597
- isinstance(t, (DoneTool, AgentDoneTool)) for t in result.tool_messages
1601
+ isinstance(t, (DoneTool, AgentDoneTool, FinalResultTool))
1602
+ for t in result.tool_messages
1598
1603
  )
1599
1604
  )
1600
1605
 
@@ -273,3 +273,40 @@ class ToolMessage(ABC, BaseModel):
273
273
  exclude=list(cls.Config.schema_extra["exclude"]),
274
274
  )
275
275
  return schema
276
+
277
+
278
+ class FinalResultTool(ToolMessage):
279
+ """Class to use as a wrapper for sending arbitrary results from an Agent's
280
+ agent_response or tool handlers, to:
281
+ (a) trigger completion of the current task as well as all parent tasks, and
282
+ (b) be returned as the final result of the root task, i.e. this tool would appear
283
+ in the final ChatDocument's `tool_messages` list.
284
+ See test_tool_handlers_and_results in test_tool_messages.py, and
285
+ examples/basic/tool-extract-short-example.py.
286
+
287
+ Note:
288
+ - when defining a tool handler or agent_response, you can directly return
289
+ FinalResultTool(field1 = val1, ...),
290
+ where the values can be aribitrary data structures, including nested
291
+ Pydantic objs, or you can define a subclass of FinalResultTool with the
292
+ fields you want to return.
293
+ - This is a special ToolMessage that is NOT meant to be used or handled
294
+ by an agent.
295
+ """
296
+
297
+ request: str = ""
298
+ purpose: str = "Ignored; Wrapper for a structured message"
299
+ id: str = "" # placeholder for OpenAI-API tool_call_id
300
+
301
+ _handle_only: bool = False # only allow handling, but not use (LLM-generation)?
302
+
303
+ class Config:
304
+ extra = Extra.allow
305
+ # only HANDLING allowed, NOT "use" (i.e LLM generation)
306
+ handle_only: bool = False
307
+ arbitrary_types_allowed = False
308
+ validate_all = True
309
+ validate_assignment = True
310
+ # do not include these fields in the generated schema
311
+ # since we don't require the LLM to specify them
312
+ schema_extra = {"exclude": {"purpose", "id"}}
@@ -115,5 +115,7 @@ class CodeParser:
115
115
  for d in docs
116
116
  if d.metadata.language in self.config.extensions # type: ignore
117
117
  ]
118
+ if len(chunked_docs) == 0:
119
+ return []
118
120
  # collapse the list of lists into a single list
119
121
  return reduce(lambda x, y: x + y, chunked_docs)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "langroid"
3
- version = "0.9.5"
3
+ version = "0.10.1"
4
4
  description = "Harness LLMs with Multi-Agent Programming"
5
5
  authors = ["Prasad Chalasani <pchalasani@gmail.com>"]
6
6
  readme = "README.md"
File without changes
File without changes
File without changes
File without changes