langroid 0.16.7__tar.gz → 0.17.0__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 (221) hide show
  1. {langroid-0.16.7 → langroid-0.17.0}/PKG-INFO +6 -3
  2. {langroid-0.16.7 → langroid-0.17.0}/README.md +3 -1
  3. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/base.py +45 -21
  4. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/chat_agent.py +22 -14
  5. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/chat_document.py +22 -13
  6. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tool_message.py +11 -11
  7. langroid-0.17.0/langroid/agent/tools/file_tools.py +234 -0
  8. langroid-0.17.0/langroid/agent/xml_tool_message.py +239 -0
  9. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/constants.py +2 -0
  10. langroid-0.17.0/langroid/utils/git_utils.py +251 -0
  11. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/system.py +78 -0
  12. {langroid-0.16.7 → langroid-0.17.0}/pyproject.toml +3 -2
  13. langroid-0.16.7/langroid/agent/md_tool_message_grammar.py +0 -455
  14. langroid-0.16.7/langroid/agent/tools/code_file_tool_parse.py +0 -150
  15. langroid-0.16.7/langroid/agent/tools/code_file_tool_pyparsing.py +0 -194
  16. langroid-0.16.7/langroid/agent/tools/code_file_tool_pyparsing2.py +0 -199
  17. langroid-0.16.7/langroid/agent/tools/extract_tool.py +0 -96
  18. langroid-0.16.7/langroid/agent/tools/formatted_model_custom.py +0 -150
  19. langroid-0.16.7/langroid/agent/tools/formatted_model_custom2.py +0 -168
  20. langroid-0.16.7/langroid/agent/tools/formatted_model_custom3.py +0 -279
  21. langroid-0.16.7/langroid/agent/tools/formatted_model_custom4.py +0 -395
  22. langroid-0.16.7/langroid/agent/tools/formatted_model_jinja.py +0 -133
  23. langroid-0.16.7/langroid/agent/tools/formatted_model_jinja.py-e +0 -122
  24. langroid-0.16.7/langroid/agent/tools/formatted_model_jinja2.py +0 -145
  25. langroid-0.16.7/langroid/agent/tools/formatted_model_jinja2.py-e +0 -135
  26. langroid-0.16.7/langroid/agent/tools/formatted_model_lark.py +0 -0
  27. langroid-0.16.7/langroid/agent/tools/formatted_model_lark2.py +0 -168
  28. langroid-0.16.7/langroid/agent/tools/formatted_model_parse.py +0 -105
  29. langroid-0.16.7/langroid/agent/tools/formatted_model_parse.py-e +0 -98
  30. langroid-0.16.7/langroid/agent/tools/formatted_model_parse2.py +0 -113
  31. langroid-0.16.7/langroid/agent/tools/formatted_model_parse2.py-e +0 -109
  32. langroid-0.16.7/langroid/agent/tools/formatted_model_parse3.py +0 -114
  33. langroid-0.16.7/langroid/agent/tools/formatted_model_parse3.py-e +0 -110
  34. langroid-0.16.7/langroid/agent/tools/formatted_model_parsimon.py +0 -194
  35. langroid-0.16.7/langroid/agent/tools/formatted_model_parsimon.py-e +0 -186
  36. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing.py +0 -169
  37. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing.py-e +0 -149
  38. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing2.py +0 -159
  39. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing2.py-e +0 -143
  40. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing3.py +0 -133
  41. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing3.py-e +0 -121
  42. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing4.py +0 -213
  43. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing4.py-e +0 -176
  44. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing5.py +0 -173
  45. langroid-0.16.7/langroid/agent/tools/formatted_model_pyparsing5.py-e +0 -142
  46. langroid-0.16.7/langroid/agent/tools/formatted_model_regex.py +0 -246
  47. langroid-0.16.7/langroid/agent/tools/formatted_model_regex.py-e +0 -248
  48. langroid-0.16.7/langroid/agent/tools/formatted_model_regex2.py +0 -250
  49. langroid-0.16.7/langroid/agent/tools/formatted_model_regex2.py-e +0 -253
  50. langroid-0.16.7/langroid/agent/tools/formatted_model_tatsu.py +0 -172
  51. langroid-0.16.7/langroid/agent/tools/formatted_model_tatsu.py-e +0 -160
  52. langroid-0.16.7/langroid/agent/tools/formatted_model_template.py +0 -217
  53. langroid-0.16.7/langroid/agent/tools/formatted_model_template.py-e +0 -200
  54. langroid-0.16.7/langroid/agent/tools/formatted_model_xml.py +0 -178
  55. langroid-0.16.7/langroid/agent/tools/formatted_model_xml2.py +0 -178
  56. langroid-0.16.7/langroid/agent/tools/formatted_model_xml3.py +0 -132
  57. langroid-0.16.7/langroid/agent/tools/formatted_model_xml4.py +0 -130
  58. langroid-0.16.7/langroid/agent/tools/formatted_model_xml5.py +0 -130
  59. langroid-0.16.7/langroid/agent/tools/formatted_model_xml6.py +0 -113
  60. langroid-0.16.7/langroid/agent/tools/formatted_model_xml7.py +0 -117
  61. langroid-0.16.7/langroid/agent/tools/formatted_model_xml8.py +0 -164
  62. langroid-0.16.7/langroid/agent/tools/generator_tool.py +0 -20
  63. langroid-0.16.7/langroid/agent/tools/generic_tool.py +0 -165
  64. langroid-0.16.7/langroid/agent/tools/generic_tool_tatsu.py +0 -275
  65. langroid-0.16.7/langroid/agent/tools/grammar_based_model.py +0 -132
  66. langroid-0.16.7/langroid/agent/tools/grammar_based_model.py-e +0 -128
  67. langroid-0.16.7/langroid/agent/tools/grammar_based_model_lark.py +0 -156
  68. langroid-0.16.7/langroid/agent/tools/grammar_based_model_lark.py-e +0 -153
  69. langroid-0.16.7/langroid/agent/tools/grammar_based_model_parse.py +0 -86
  70. langroid-0.16.7/langroid/agent/tools/grammar_based_model_parse.py-e +0 -80
  71. langroid-0.16.7/langroid/agent/tools/grammar_based_model_parsimonious.py +0 -129
  72. langroid-0.16.7/langroid/agent/tools/grammar_based_model_parsimonious.py-e +0 -120
  73. langroid-0.16.7/langroid/agent/tools/grammar_based_model_pyparsing.py +0 -105
  74. langroid-0.16.7/langroid/agent/tools/grammar_based_model_pyparsing.py-e +0 -103
  75. langroid-0.16.7/langroid/agent/tools/grammar_based_model_regex.py +0 -139
  76. langroid-0.16.7/langroid/agent/tools/grammar_based_model_regex.py-e +0 -130
  77. langroid-0.16.7/langroid/agent/tools/grammar_based_model_regex2.py +0 -124
  78. langroid-0.16.7/langroid/agent/tools/grammar_based_model_regex2.py-e +0 -116
  79. langroid-0.16.7/langroid/agent/tools/grammar_based_model_tatsu.py +0 -80
  80. langroid-0.16.7/langroid/agent/tools/grammar_based_model_tatsu.py-e +0 -77
  81. langroid-0.16.7/langroid/agent/tools/lark_earley_example.py +0 -135
  82. langroid-0.16.7/langroid/agent/tools/lark_earley_example.py-e +0 -117
  83. langroid-0.16.7/langroid/agent/tools/lark_example.py +0 -72
  84. langroid-0.16.7/langroid/agent/tools/note_tool.py +0 -0
  85. langroid-0.16.7/langroid/agent/tools/parse_example.py +0 -76
  86. langroid-0.16.7/langroid/agent/tools/parse_example2.py +0 -87
  87. langroid-0.16.7/langroid/agent/tools/parse_example3.py +0 -42
  88. langroid-0.16.7/langroid/agent/tools/parse_test.py +0 -791
  89. langroid-0.16.7/langroid/agent/tools/run_python_code.py +0 -60
  90. langroid-0.16.7/langroid/agent/xml_tool_message.py +0 -106
  91. {langroid-0.16.7 → langroid-0.17.0}/LICENSE +0 -0
  92. {langroid-0.16.7 → langroid-0.17.0}/langroid/__init__.py +0 -0
  93. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/__init__.py +0 -0
  94. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/batch.py +0 -0
  95. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/callbacks/__init__.py +0 -0
  96. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/callbacks/chainlit.py +0 -0
  97. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/helpers.py +0 -0
  98. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/junk +0 -0
  99. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/openai_assistant.py +0 -0
  100. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/__init__.py +0 -0
  101. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/doc_chat_agent.py +0 -0
  102. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
  103. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/lance_rag/__init__.py +0 -0
  104. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
  105. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/lance_rag/lance_rag_task.py +0 -0
  106. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
  107. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/lance_tools.py +0 -0
  108. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/neo4j/__init__.py +0 -0
  109. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
  110. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
  111. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/neo4j/utils/__init__.py +0 -0
  112. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/neo4j/utils/system_message.py +0 -0
  113. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/relevance_extractor_agent.py +0 -0
  114. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/retriever_agent.py +0 -0
  115. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/sql/__init__.py +0 -0
  116. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
  117. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/sql/utils/__init__.py +0 -0
  118. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
  119. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
  120. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/sql/utils/system_message.py +0 -0
  121. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/sql/utils/tools.py +0 -0
  122. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/special/table_chat_agent.py +0 -0
  123. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/structured_message.py +0 -0
  124. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/task.py +0 -0
  125. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/__init__.py +0 -0
  126. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
  127. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/google_search_tool.py +0 -0
  128. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/metaphor_search_tool.py +0 -0
  129. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/orchestration.py +0 -0
  130. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/recipient_tool.py +0 -0
  131. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/retrieval_tool.py +0 -0
  132. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/rewind_tool.py +0 -0
  133. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/tools/segment_extract_tool.py +0 -0
  134. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent/typed_task.py +0 -0
  135. {langroid-0.16.7 → langroid-0.17.0}/langroid/agent_config.py +0 -0
  136. {langroid-0.16.7 → langroid-0.17.0}/langroid/cachedb/__init__.py +0 -0
  137. {langroid-0.16.7 → langroid-0.17.0}/langroid/cachedb/base.py +0 -0
  138. {langroid-0.16.7 → langroid-0.17.0}/langroid/cachedb/momento_cachedb.py +0 -0
  139. {langroid-0.16.7 → langroid-0.17.0}/langroid/cachedb/redis_cachedb.py +0 -0
  140. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/__init__.py +0 -0
  141. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/base.py +0 -0
  142. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/clustering.py +0 -0
  143. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/models.py +0 -0
  144. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/protoc/__init__.py +0 -0
  145. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/protoc/embeddings.proto +0 -0
  146. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
  147. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
  148. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
  149. {langroid-0.16.7 → langroid-0.17.0}/langroid/embedding_models/remote_embeds.py +0 -0
  150. {langroid-0.16.7 → langroid-0.17.0}/langroid/exceptions.py +0 -0
  151. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/.chainlit/config.toml +0 -0
  152. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/.chainlit/translations/en-US.json +0 -0
  153. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/__init__.py +0 -0
  154. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/azure_openai.py +0 -0
  155. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/base.py +0 -0
  156. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/config.py +0 -0
  157. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/mock_lm.py +0 -0
  158. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/openai_gpt.py +0 -0
  159. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/prompt_formatter/__init__.py +0 -0
  160. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/prompt_formatter/base.py +0 -0
  161. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
  162. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
  163. {langroid-0.16.7 → langroid-0.17.0}/langroid/language_models/utils.py +0 -0
  164. {langroid-0.16.7 → langroid-0.17.0}/langroid/mytypes.py +0 -0
  165. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/__init__.py +0 -0
  166. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/agent_chats.py +0 -0
  167. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/code-parsing.md +0 -0
  168. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/code_parser.py +0 -0
  169. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/config.py +0 -0
  170. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/document_parser.py +0 -0
  171. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/image_text.py +0 -0
  172. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/para_sentence_split.py +0 -0
  173. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/parse_json.py +0 -0
  174. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/parser.py +0 -0
  175. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/repo_loader.py +0 -0
  176. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/routing.py +0 -0
  177. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/search.py +0 -0
  178. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/spider.py +0 -0
  179. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/table_loader.py +0 -0
  180. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/url_loader.py +0 -0
  181. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/url_loader_cookies.py +0 -0
  182. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/urls.py +0 -0
  183. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/utils.py +0 -0
  184. {langroid-0.16.7 → langroid-0.17.0}/langroid/parsing/web_search.py +0 -0
  185. {langroid-0.16.7 → langroid-0.17.0}/langroid/prompts/__init__.py +0 -0
  186. {langroid-0.16.7 → langroid-0.17.0}/langroid/prompts/chat-gpt4-system-prompt.md +0 -0
  187. {langroid-0.16.7 → langroid-0.17.0}/langroid/prompts/dialog.py +0 -0
  188. {langroid-0.16.7 → langroid-0.17.0}/langroid/prompts/prompts_config.py +0 -0
  189. {langroid-0.16.7 → langroid-0.17.0}/langroid/prompts/templates.py +0 -0
  190. {langroid-0.16.7 → langroid-0.17.0}/langroid/py.typed +0 -0
  191. {langroid-0.16.7 → langroid-0.17.0}/langroid/pydantic_v1/__init__.py +0 -0
  192. {langroid-0.16.7 → langroid-0.17.0}/langroid/pydantic_v1/main.py +0 -0
  193. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/.chainlit/config.toml +0 -0
  194. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/.chainlit/translations/en-US.json +0 -0
  195. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/__init__.py +0 -0
  196. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/algorithms/__init__.py +0 -0
  197. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/algorithms/graph.py +0 -0
  198. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/configuration.py +0 -0
  199. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/docker.py +0 -0
  200. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/globals.py +0 -0
  201. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/llms/__init__.py +0 -0
  202. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/llms/strings.py +0 -0
  203. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/logging.py +0 -0
  204. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/object_registry.py +0 -0
  205. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/output/__init__.py +0 -0
  206. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/output/citations.py +0 -0
  207. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/output/printing.py +0 -0
  208. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/output/status.py +0 -0
  209. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/pandas_utils.py +0 -0
  210. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/pydantic_utils.py +0 -0
  211. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/types.py +0 -0
  212. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/web/__init__.py +0 -0
  213. {langroid-0.16.7 → langroid-0.17.0}/langroid/utils/web/login.py +0 -0
  214. {langroid-0.16.7 → langroid-0.17.0}/langroid/vector_store/__init__.py +0 -0
  215. {langroid-0.16.7 → langroid-0.17.0}/langroid/vector_store/base.py +0 -0
  216. {langroid-0.16.7 → langroid-0.17.0}/langroid/vector_store/chromadb.py +0 -0
  217. {langroid-0.16.7 → langroid-0.17.0}/langroid/vector_store/lancedb.py +0 -0
  218. {langroid-0.16.7 → langroid-0.17.0}/langroid/vector_store/meilisearch.py +0 -0
  219. {langroid-0.16.7 → langroid-0.17.0}/langroid/vector_store/momento.py +0 -0
  220. {langroid-0.16.7 → langroid-0.17.0}/langroid/vector_store/qdrant_cloud.py +0 -0
  221. {langroid-0.16.7 → langroid-0.17.0}/langroid/vector_store/qdrantdb.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langroid
3
- Version: 0.16.7
3
+ Version: 0.17.0
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  License: MIT
6
6
  Author: Prasad Chalasani
@@ -48,6 +48,7 @@ Requires-Dist: faker (>=18.9.0,<19.0.0)
48
48
  Requires-Dist: fakeredis (>=2.12.1,<3.0.0)
49
49
  Requires-Dist: fastembed (>=0.3.1,<0.4.0) ; extra == "all" or extra == "fastembed"
50
50
  Requires-Dist: fire (>=0.5.0,<0.6.0)
51
+ Requires-Dist: gitpython (>=3.1.43,<4.0.0)
51
52
  Requires-Dist: google-api-python-client (>=2.95.0,<3.0.0)
52
53
  Requires-Dist: google-generativeai (>=0.5.2,<0.6.0)
53
54
  Requires-Dist: groq (>=0.5.0,<0.6.0)
@@ -55,7 +56,7 @@ Requires-Dist: grpcio (>=1.62.1,<2.0.0)
55
56
  Requires-Dist: halo (>=0.0.31,<0.0.32)
56
57
  Requires-Dist: huggingface-hub (>=0.21.2,<0.22.0) ; extra == "hf-transformers" or extra == "all" or extra == "transformers"
57
58
  Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
58
- Requires-Dist: json-repair (>=0.29.0,<0.30.0)
59
+ Requires-Dist: json-repair (>=0.29.9,<0.30.0)
59
60
  Requires-Dist: lancedb (>=0.8.2,<0.9.0) ; extra == "vecdbs" or extra == "lancedb"
60
61
  Requires-Dist: litellm (>=1.30.1,<2.0.0) ; extra == "all" or extra == "litellm"
61
62
  Requires-Dist: lxml (>=4.9.3,<5.0.0)
@@ -143,7 +144,7 @@ Description-Content-Type: text/markdown
143
144
  </h3>
144
145
 
145
146
  `Langroid` is an intuitive, lightweight, extensible and principled
146
- Python framework to easily build LLM-powered applications, from ex-CMU and UW-Madison researchers.
147
+ Python framework to easily build LLM-powered applications, from CMU and UW-Madison researchers.
147
148
  You set up Agents, equip them with optional components (LLM,
148
149
  vector-store and tools/functions), assign them tasks, and have them
149
150
  collaboratively solve a problem by exchanging messages.
@@ -242,6 +243,8 @@ teacher_task.run()
242
243
  <details>
243
244
  <summary> <b>Click to expand</b></summary>
244
245
 
246
+ - **Oct 2024:**
247
+ - **[0.17.0]** XML-based tools, see [docs](https://langroid.github.io/langroid/tutorials/xml-tools/).
245
248
  - **Sep 2024:**
246
249
  - **[0.16.0](https://github.com/langroid/langroid/releases/tag/0.16.0)** Support for OpenAI `o1-mini` and `o1-preview` models.
247
250
  - **[0.15.0](https://github.com/langroid/langroid/releases/tag/0.15.0)** Cerebras API support -- run llama-3.1 models hosted on Cerebras Cloud (very fast inference).
@@ -35,7 +35,7 @@
35
35
  </h3>
36
36
 
37
37
  `Langroid` is an intuitive, lightweight, extensible and principled
38
- Python framework to easily build LLM-powered applications, from ex-CMU and UW-Madison researchers.
38
+ Python framework to easily build LLM-powered applications, from CMU and UW-Madison researchers.
39
39
  You set up Agents, equip them with optional components (LLM,
40
40
  vector-store and tools/functions), assign them tasks, and have them
41
41
  collaboratively solve a problem by exchanging messages.
@@ -134,6 +134,8 @@ teacher_task.run()
134
134
  <details>
135
135
  <summary> <b>Click to expand</b></summary>
136
136
 
137
+ - **Oct 2024:**
138
+ - **[0.17.0]** XML-based tools, see [docs](https://langroid.github.io/langroid/tutorials/xml-tools/).
137
139
  - **Sep 2024:**
138
140
  - **[0.16.0](https://github.com/langroid/langroid/releases/tag/0.16.0)** Support for OpenAI `o1-mini` and `o1-preview` models.
139
141
  - **[0.15.0](https://github.com/langroid/langroid/releases/tag/0.15.0)** Cerebras API support -- run llama-3.1 models hosted on Cerebras Cloud (very fast inference).
@@ -32,6 +32,7 @@ from rich.prompt import Prompt
32
32
 
33
33
  from langroid.agent.chat_document import ChatDocMetaData, ChatDocument
34
34
  from langroid.agent.tool_message import ToolMessage
35
+ from langroid.agent.xml_tool_message import XMLToolMessage
35
36
  from langroid.language_models.base import (
36
37
  LanguageModel,
37
38
  LLMConfig,
@@ -55,7 +56,13 @@ from langroid.pydantic_v1 import (
55
56
  validator,
56
57
  )
57
58
  from langroid.utils.configuration import settings
58
- from langroid.utils.constants import DONE, NO_ANSWER, PASS, PASS_TO, SEND_TO
59
+ from langroid.utils.constants import (
60
+ DONE,
61
+ NO_ANSWER,
62
+ PASS,
63
+ PASS_TO,
64
+ SEND_TO,
65
+ )
59
66
  from langroid.utils.object_registry import ObjectRegistry
60
67
  from langroid.utils.output import status
61
68
  from langroid.utils.types import from_string, to_string
@@ -910,7 +917,7 @@ class Agent(ABC):
910
917
  return []
911
918
 
912
919
  if isinstance(msg, str):
913
- json_tools = self.get_json_tool_messages(msg)
920
+ json_tools = self.get_formatted_tool_messages(msg)
914
921
  if all_tools:
915
922
  return json_tools
916
923
  else:
@@ -938,7 +945,7 @@ class Agent(ABC):
938
945
  and msg.function_call is None
939
946
  ):
940
947
 
941
- tools = self.get_json_tool_messages(msg.content)
948
+ tools = self.get_formatted_tool_messages(msg.content)
942
949
  msg.all_tool_messages = tools
943
950
  my_tools = [t for t in tools if self._tool_recipient_match(t)]
944
951
  msg.tool_messages = my_tools
@@ -966,9 +973,16 @@ class Agent(ABC):
966
973
  else:
967
974
  return my_tools
968
975
 
969
- def get_json_tool_messages(self, input_str: str) -> List[ToolMessage]:
976
+ def get_formatted_tool_messages(self, input_str: str) -> List[ToolMessage]:
970
977
  """
971
- Returns ToolMessage objects (tools) corresponding to JSON substrings, if any.
978
+ Returns ToolMessage objects (tools) corresponding to
979
+ tool-formatted substrings, if any.
980
+ ASSUMPTION - These tools are either ALL JSON-based, or ALL XML-based
981
+ (i.e. not a mix of both).
982
+ Terminology: a "formatted tool msg" is one which the LLM generates as
983
+ part of its raw string output, rather than within a JSON object
984
+ in the API response (i.e. this method does not extract tools/fns returned
985
+ by OpenAI's tools/fns API or similar APIs).
972
986
 
973
987
  Args:
974
988
  input_str (str): input string, typically a message sent by an LLM
@@ -976,10 +990,15 @@ class Agent(ABC):
976
990
  Returns:
977
991
  List[ToolMessage]: list of ToolMessage objects
978
992
  """
979
- json_substrings = extract_top_level_json(input_str)
980
- if len(json_substrings) == 0:
981
- return []
982
- results = [self._get_one_tool_message(j) for j in json_substrings]
993
+ substrings = XMLToolMessage.find_candidates(input_str)
994
+ is_json = False
995
+ if len(substrings) == 0:
996
+ substrings = extract_top_level_json(input_str)
997
+ is_json = len(substrings) > 0
998
+ if not is_json:
999
+ return []
1000
+
1001
+ results = [self._get_one_tool_message(j, is_json) for j in substrings]
983
1002
  return [r for r in results if r is not None]
984
1003
 
985
1004
  def get_function_call_class(self, msg: ChatDocument) -> Optional[ToolMessage]:
@@ -1200,17 +1219,22 @@ class Agent(ABC):
1200
1219
  """
1201
1220
  return None
1202
1221
 
1203
- def _get_one_tool_message(self, json_str: str) -> Optional[ToolMessage]:
1222
+ def _get_one_tool_message(
1223
+ self, tool_candidate_str: str, is_json: bool = True
1224
+ ) -> Optional[ToolMessage]:
1204
1225
  """
1205
- Parse the json str into ANY ToolMessage KNOWN to agent --
1226
+ Parse the tool_candidate_str into ANY ToolMessage KNOWN to agent --
1206
1227
  This includes non-used/handled tools, i.e. any tool in self.llm_tools_known.
1207
1228
  The exception to this is below where we try our best to infer the tool
1208
- when the LLM has "forgotten" to include the "request" field in the JSON --
1229
+ when the LLM has "forgotten" to include the "request" field in the tool str ---
1209
1230
  in this case we ONLY look at the possible set of HANDLED tools, i.e.
1210
1231
  self.llm_tools_handled.
1211
1232
  """
1212
- json_data = json.loads(json_str)
1213
- # check if the json_data contains a "properties" field
1233
+ if is_json:
1234
+ maybe_tool_dict = json.loads(tool_candidate_str)
1235
+ else:
1236
+ maybe_tool_dict = XMLToolMessage.extract_field_values(tool_candidate_str)
1237
+ # check if the maybe_tool_dict contains a "properties" field
1214
1238
  # which further contains the actual tool-call
1215
1239
  # (some weak LLMs do this). E.g. gpt-4o sometimes generates this:
1216
1240
  # TOOL: {
@@ -1225,17 +1249,17 @@ class Agent(ABC):
1225
1249
  # ]
1226
1250
  # }
1227
1251
 
1228
- if not isinstance(json_data, dict):
1252
+ if not isinstance(maybe_tool_dict, dict):
1229
1253
  return None
1230
1254
 
1231
- properties = json_data.get("properties")
1255
+ properties = maybe_tool_dict.get("properties")
1232
1256
  if isinstance(properties, dict):
1233
- json_data = properties
1234
- request = json_data.get("request")
1257
+ maybe_tool_dict = properties
1258
+ request = maybe_tool_dict.get("request")
1235
1259
  if request is None:
1236
1260
  possible = [self.llm_tools_map[r] for r in self.llm_tools_handled]
1237
1261
  default_keys = set(ToolMessage.__fields__.keys())
1238
- request_keys = set(json_data.keys())
1262
+ request_keys = set(maybe_tool_dict.keys())
1239
1263
 
1240
1264
  def maybe_parse(tool: type[ToolMessage]) -> Optional[ToolMessage]:
1241
1265
  all_keys = set(tool.__fields__.keys())
@@ -1250,7 +1274,7 @@ class Agent(ABC):
1250
1274
  return None
1251
1275
 
1252
1276
  try:
1253
- return tool.parse_obj(json_data)
1277
+ return tool.parse_obj(maybe_tool_dict)
1254
1278
  except ValidationError:
1255
1279
  return None
1256
1280
 
@@ -1277,7 +1301,7 @@ class Agent(ABC):
1277
1301
  return None
1278
1302
 
1279
1303
  try:
1280
- message = message_class.parse_obj(json_data)
1304
+ message = message_class.parse_obj(maybe_tool_dict)
1281
1305
  except ValidationError as ve:
1282
1306
  raise ve
1283
1307
  return message
@@ -12,6 +12,7 @@ from rich.markup import escape
12
12
  from langroid.agent.base import Agent, AgentConfig, noop_fn
13
13
  from langroid.agent.chat_document import ChatDocument
14
14
  from langroid.agent.tool_message import ToolMessage
15
+ from langroid.agent.xml_tool_message import XMLToolMessage
15
16
  from langroid.language_models.base import (
16
17
  LLMFunctionSpec,
17
18
  LLMMessage,
@@ -141,7 +142,7 @@ class ChatAgent(Agent):
141
142
  # if they are specified in a ToolMessage class as a classmethod `instructions`
142
143
  self.system_tool_instructions: str = ""
143
144
  # (b) these are only for the builtin in Langroid TOOLS mechanism:
144
- self.system_json_tool_instructions: str = ""
145
+ self.system_tool_format_instructions: str = ""
145
146
 
146
147
  self.llm_functions_map: Dict[str, LLMFunctionSpec] = {}
147
148
  self.llm_functions_handled: Set[str] = set()
@@ -207,7 +208,7 @@ class ChatAgent(Agent):
207
208
  config_copy.name = f"{config_copy.name}-{i}"
208
209
  new_agent = agent_cls(config_copy)
209
210
  new_agent.system_tool_instructions = self.system_tool_instructions
210
- new_agent.system_json_tool_instructions = self.system_json_tool_instructions
211
+ new_agent.system_tool_format_instructions = self.system_tool_format_instructions
211
212
  new_agent.llm_tools_map = self.llm_tools_map
212
213
  new_agent.llm_functions_map = self.llm_functions_map
213
214
  new_agent.llm_functions_handled = self.llm_functions_handled
@@ -303,10 +304,11 @@ class ChatAgent(Agent):
303
304
  ]
304
305
  )
305
306
 
306
- def json_format_rules(self) -> str:
307
+ def tool_format_rules(self) -> str:
307
308
  """
308
- Specification of JSON formatting rules, based on the currently enabled
309
- usable `ToolMessage`s
309
+ Specification of tool formatting rules
310
+ (typically JSON-based but can be non-JSON, e.g. XMLToolMessage),
311
+ based on the currently enabled usable `ToolMessage`s
310
312
 
311
313
  Returns:
312
314
  str: formatting rules
@@ -320,9 +322,9 @@ class ChatAgent(Agent):
320
322
 
321
323
  if len(usable_tool_classes) == 0:
322
324
  return "You can ask questions in natural language."
323
- json_instructions = "\n\n".join(
325
+ format_instructions = "\n\n".join(
324
326
  [
325
- msg_cls.json_instructions(tool=self.config.use_tools)
327
+ msg_cls.format_instructions(tool=self.config.use_tools)
326
328
  for msg_cls in usable_tool_classes
327
329
  ]
328
330
  )
@@ -332,11 +334,11 @@ class ChatAgent(Agent):
332
334
  if hasattr(msg_cls, "json_group_instructions") and callable(
333
335
  getattr(msg_cls, "json_group_instructions")
334
336
  ):
335
- return msg_cls.json_group_instructions().format(
336
- json_instructions=json_instructions
337
+ return msg_cls.group_format_instructions().format(
338
+ format_instructions=format_instructions
337
339
  )
338
- return ToolMessage.json_group_instructions().format(
339
- json_instructions=json_instructions
340
+ return ToolMessage.group_format_instructions().format(
341
+ format_instructions=format_instructions
340
342
  )
341
343
 
342
344
  def tool_instructions(self) -> str:
@@ -358,7 +360,7 @@ class ChatAgent(Agent):
358
360
  and inspect.ismethod(msg_cls.instructions)
359
361
  and msg_cls.default_value("request") in self.llm_tools_usable
360
362
  ):
361
- # example will be shown in json_format_rules() when using TOOLs,
363
+ # example will be shown in tool_format_rules() when using TOOLs,
362
364
  # so we don't need to show it here.
363
365
  example = "" if self.config.use_tools else (msg_cls.usage_examples())
364
366
  if example != "":
@@ -457,7 +459,7 @@ class ChatAgent(Agent):
457
459
 
458
460
  {self.system_tool_instructions}
459
461
 
460
- {self.system_json_tool_instructions}
462
+ {self.system_tool_format_instructions}
461
463
 
462
464
  """.lstrip()
463
465
  )
@@ -520,6 +522,12 @@ class ChatAgent(Agent):
520
522
  return None
521
523
  if require_recipient and message_class is not None:
522
524
  message_class = message_class.require_recipient()
525
+ if isinstance(message_class, XMLToolMessage):
526
+ # XMLToolMessage is not compatible with OpenAI's Tools/functions API,
527
+ # so we disable use of functions API, enable langroid-native Tools,
528
+ # which are prompt-based.
529
+ self.config.use_functions_api = False
530
+ self.config.use_tools = True
523
531
  super().enable_message_handling(message_class) # enables handling only
524
532
  tools = self._get_tool_list(message_class)
525
533
  if message_class is not None:
@@ -570,7 +578,7 @@ class ChatAgent(Agent):
570
578
 
571
579
  # Set tool instructions and JSON format instructions
572
580
  if self.config.use_tools:
573
- self.system_json_tool_instructions = self.json_format_rules()
581
+ self.system_tool_format_instructions = self.tool_format_rules()
574
582
  self.system_tool_instructions = self.tool_instructions()
575
583
 
576
584
  def disable_message_handling(
@@ -7,6 +7,7 @@ from enum import Enum
7
7
  from typing import Any, Dict, List, Optional, Union, cast
8
8
 
9
9
  from langroid.agent.tool_message import ToolMessage
10
+ from langroid.agent.xml_tool_message import XMLToolMessage
10
11
  from langroid.language_models.base import (
11
12
  LLMFunctionCall,
12
13
  LLMMessage,
@@ -181,21 +182,29 @@ class ChatDocument(Document):
181
182
  f"{recipient_str}{tool_str}{fields.content}"
182
183
  )
183
184
 
184
- def get_json_tools(self) -> List[str]:
185
+ def get_tool_names(self) -> List[str]:
185
186
  """
186
- Get names of attempted JSON tool usages in the content
187
+ Get names of attempted tool usages (JSON or non-JSON) in the content
187
188
  of the message.
188
189
  Returns:
189
- List[str]: list of JSON tool names
190
+ List[str]: list of *attempted* tool names
191
+ (We say "attempted" since we ONLY look at the `request` component of the
192
+ tool-call representation, and we're not fully parsing it into the
193
+ corresponding tool message class)
194
+
190
195
  """
191
- jsons = extract_top_level_json(self.content)
192
- tools = []
193
- for j in jsons:
194
- json_data = json.loads(j)
195
- tool = json_data.get("request")
196
- if tool is not None:
197
- tools.append(str(tool))
198
- return tools
196
+ tool_candidates = XMLToolMessage.find_candidates(self.content)
197
+ if len(tool_candidates) == 0:
198
+ tool_candidates = extract_top_level_json(self.content)
199
+ if len(tool_candidates) == 0:
200
+ return []
201
+ tools = [json.loads(tc).get("request") for tc in tool_candidates]
202
+ else:
203
+ tool_dicts = [
204
+ XMLToolMessage.extract_field_values(tc) for tc in tool_candidates
205
+ ]
206
+ tools = [td.get("request") for td in tool_dicts if td is not None]
207
+ return [str(tool) for tool in tools if tool is not None]
199
208
 
200
209
  def log_fields(self) -> ChatDocLoggerFields:
201
210
  """
@@ -208,9 +217,9 @@ class ChatDocument(Document):
208
217
  if self.function_call is not None:
209
218
  tool_type = "FUNC"
210
219
  tool = self.function_call.name
211
- elif self.get_json_tools() != []:
220
+ elif (json_tools := self.get_tool_names()) != []:
212
221
  tool_type = "TOOL"
213
- tool = self.get_json_tools()[0]
222
+ tool = json_tools[0]
214
223
  recipient = self.metadata.recipient
215
224
  content = self.content
216
225
  sender_entity = self.metadata.sender
@@ -72,7 +72,7 @@ class ToolMessage(ABC, BaseModel):
72
72
  @classmethod
73
73
  def examples(cls) -> List["ToolMessage" | Tuple[str, "ToolMessage"]]:
74
74
  """
75
- Examples to use in few-shot demos with JSON formatting instructions.
75
+ Examples to use in few-shot demos with formatting instructions.
76
76
  Each example can be either:
77
77
  - just a ToolMessage instance, e.g. MyTool(param1=1, param2="hello"), or
78
78
  - a tuple (description, ToolMessage instance), where the description is
@@ -104,20 +104,20 @@ class ToolMessage(ABC, BaseModel):
104
104
  examples = [choice(cls.examples())]
105
105
  else:
106
106
  examples = cls.examples()
107
- examples_jsons = [
107
+ formatted_examples = [
108
108
  (
109
- f"EXAMPLE {i}: (THOUGHT: {ex[0]}) => \n{ex[1].json_example()}"
109
+ f"EXAMPLE {i}: (THOUGHT: {ex[0]}) => \n{ex[1].format_example()}"
110
110
  if isinstance(ex, tuple)
111
- else f"EXAMPLE {i}:\n {ex.json_example()}"
111
+ else f"EXAMPLE {i}:\n {ex.format_example()}"
112
112
  )
113
113
  for i, ex in enumerate(examples, 1)
114
114
  ]
115
- return "\n\n".join(examples_jsons)
115
+ return "\n\n".join(formatted_examples)
116
116
 
117
117
  def to_json(self) -> str:
118
118
  return self.json(indent=4, exclude=self.Config.schema_extra["exclude"])
119
119
 
120
- def json_example(self) -> str:
120
+ def format_example(self) -> str:
121
121
  return self.json(indent=4, exclude=self.Config.schema_extra["exclude"])
122
122
 
123
123
  def dict_example(self) -> Dict[str, Any]:
@@ -148,7 +148,7 @@ class ToolMessage(ABC, BaseModel):
148
148
  return properties.get(f, {}).get("default", None)
149
149
 
150
150
  @classmethod
151
- def json_instructions(cls, tool: bool = False) -> str:
151
+ def format_instructions(cls, tool: bool = False) -> str:
152
152
  """
153
153
  Default Instructions to the LLM showing how to use the tool/function-call.
154
154
  Works for GPT4 but override this for weaker LLMs if needed.
@@ -181,19 +181,19 @@ class ToolMessage(ABC, BaseModel):
181
181
  )
182
182
 
183
183
  @staticmethod
184
- def json_group_instructions() -> str:
184
+ def group_format_instructions() -> str:
185
185
  """Template for instructions for a group of tools.
186
186
  Works with GPT4 but override this for weaker LLMs if needed.
187
187
  """
188
188
  return textwrap.dedent(
189
189
  """
190
- === ALL AVAILABLE TOOLS and THEIR JSON FORMAT INSTRUCTIONS ===
190
+ === ALL AVAILABLE TOOLS and THEIR FORMAT INSTRUCTIONS ===
191
191
  You have access to the following TOOLS to accomplish your task:
192
192
 
193
- {json_instructions}
193
+ {format_instructions}
194
194
 
195
195
  When one of the above TOOLs is applicable, you must express your
196
- request as "TOOL:" followed by the request in the above JSON format.
196
+ request as "TOOL:" followed by the request in the above format.
197
197
  """
198
198
  )
199
199
 
@@ -0,0 +1,234 @@
1
+ from contextlib import chdir
2
+ from pathlib import Path
3
+ from textwrap import dedent
4
+ from typing import Callable, List, Tuple, Type
5
+
6
+ import git
7
+
8
+ from langroid.agent.tool_message import ToolMessage
9
+ from langroid.agent.xml_tool_message import XMLToolMessage
10
+ from langroid.pydantic_v1 import Field
11
+ from langroid.utils.git_utils import git_commit_file
12
+ from langroid.utils.system import create_file, list_dir, read_file
13
+
14
+
15
+ class ReadFileTool(ToolMessage):
16
+ request: str = "read_file_tool"
17
+ purpose: str = "Read the contents of a <file_path>"
18
+ file_path: str
19
+
20
+ _line_nums: bool = True # whether to add line numbers to the content
21
+ _curr_dir: Callable[[], str] | None = None
22
+
23
+ @classmethod
24
+ def create(
25
+ cls,
26
+ get_curr_dir: Callable[[], str] | None,
27
+ ) -> Type["ReadFileTool"]:
28
+ """
29
+ Create a subclass of ReadFileTool for a specific directory
30
+
31
+ Args:
32
+ get_curr_dir (callable): A function that returns the current directory.
33
+
34
+ Returns:
35
+ Type[ReadFileTool]: A subclass of the ReadFileTool class, specifically
36
+ for the current directory.
37
+ """
38
+
39
+ class CustomReadFileTool(cls): # type: ignore
40
+ _curr_dir: Callable[[], str] | None = (
41
+ staticmethod(get_curr_dir) if get_curr_dir else None
42
+ )
43
+
44
+ return CustomReadFileTool
45
+
46
+ @classmethod
47
+ def examples(cls) -> List[ToolMessage | tuple[str, ToolMessage]]:
48
+ return [
49
+ cls(file_path="src/lib.rs"),
50
+ (
51
+ "I want to read the contents of src/main.rs",
52
+ cls(file_path="src/main.rs"),
53
+ ),
54
+ ]
55
+
56
+ def handle(self) -> str:
57
+ # return contents as str for LLM to read
58
+ # ASSUME: file_path should be relative to the curr_dir
59
+ try:
60
+ dir = (self._curr_dir and self._curr_dir()) or Path.cwd()
61
+ with chdir(dir):
62
+ # if file doesn't exist, return an error message
63
+ content = read_file(self.file_path, self._line_nums)
64
+ line_num_str = ""
65
+ if self._line_nums:
66
+ line_num_str = "(Line numbers added for reference only!)"
67
+ return f"""
68
+ CONTENTS of {self.file_path}:
69
+ {line_num_str}
70
+ ---------------------------
71
+ {content}
72
+ """
73
+ except FileNotFoundError:
74
+ return f"File not found: {self.file_path}"
75
+
76
+
77
+ class WriteFileTool(XMLToolMessage):
78
+ request: str = "write_file_tool"
79
+ purpose: str = """
80
+ Tool for writing <content> in a certain <language> to a <file_path>
81
+ """
82
+
83
+ file_path: str = Field(..., description="The path to the file to write the content")
84
+
85
+ language: str = Field(
86
+ default="",
87
+ description="""
88
+ The language of the content; could be human language or programming language
89
+ """,
90
+ )
91
+ content: str = Field(
92
+ ...,
93
+ description="The content to write to the file",
94
+ verbatim=True, # preserve the content as is; uses CDATA section in XML
95
+ )
96
+ _curr_dir: Callable[[], str] | None = None
97
+ _git_repo: Callable[[], git.Repo] | None = None
98
+ _commit_message: str = "Agent write file tool"
99
+
100
+ @classmethod
101
+ def create(
102
+ cls,
103
+ get_curr_dir: Callable[[], str] | None,
104
+ get_git_repo: Callable[[], str] | None,
105
+ ) -> Type["WriteFileTool"]:
106
+ """
107
+ Create a subclass of WriteFileTool with the current directory and git repo.
108
+
109
+ Args:
110
+ get_curr_dir (callable): A function that returns the current directory.
111
+ get_git_repo (callable): A function that returns the git repo.
112
+
113
+ Returns:
114
+ Type[WriteFileTool]: A subclass of the WriteFileTool class, specifically
115
+ for the current directory and git repo.
116
+ """
117
+
118
+ class CustomWriteFileTool(cls): # type: ignore
119
+ _curr_dir: Callable[[], str] | None = (
120
+ staticmethod(get_curr_dir) if get_curr_dir else None
121
+ )
122
+ _git_repo: Callable[[], str] | None = (
123
+ staticmethod(get_git_repo) if get_git_repo else None
124
+ )
125
+
126
+ return CustomWriteFileTool
127
+
128
+ @classmethod
129
+ def examples(cls) -> List[ToolMessage | Tuple[str, ToolMessage]]:
130
+ return [
131
+ (
132
+ """
133
+ I want to define a simple hello world python function
134
+ in a file "mycode/hello.py"
135
+ """,
136
+ cls(
137
+ file_path="mycode/hello.py",
138
+ language="python",
139
+ content="""
140
+ def hello():
141
+ print("Hello, World!")
142
+ """,
143
+ ),
144
+ ),
145
+ cls(
146
+ file_path="src/lib.rs",
147
+ language="rust",
148
+ content="""
149
+ fn main() {
150
+ println!("Hello, World!");
151
+ }
152
+ """,
153
+ ),
154
+ cls(
155
+ file_path="docs/intro.txt",
156
+ content="""
157
+ # Introduction
158
+ This is the first sentence of the introduction.
159
+ """,
160
+ ),
161
+ ]
162
+
163
+ def handle(self) -> str:
164
+ curr_dir = (self._curr_dir and self._curr_dir()) or Path.cwd()
165
+ with chdir(curr_dir):
166
+ create_file(self.file_path, self.content)
167
+ msg = f"Content written to {self.file_path}"
168
+ # possibly commit the file
169
+ if self._git_repo:
170
+ git_commit_file(
171
+ self._git_repo(),
172
+ self.file_path,
173
+ self._commit_message,
174
+ )
175
+ msg += " and committed"
176
+ return msg
177
+
178
+
179
+ class ListDirTool(ToolMessage):
180
+ request: str = "list_dir_tool"
181
+ purpose: str = "List the contents of a <dir_path>"
182
+ dir_path: str
183
+
184
+ _curr_dir: Callable[[], str] | None = None
185
+
186
+ @classmethod
187
+ def create(
188
+ cls,
189
+ get_curr_dir: Callable[[], str] | None,
190
+ ) -> Type["ReadFileTool"]:
191
+ """
192
+ Create a subclass of ListDirTool for a specific directory
193
+
194
+ Args:
195
+ get_curr_dir (callable): A function that returns the current directory.
196
+
197
+ Returns:
198
+ Type[ReadFileTool]: A subclass of the ReadFileTool class, specifically
199
+ for the current directory.
200
+ """
201
+
202
+ class CustomListDirTool(cls): # type: ignore
203
+ _curr_dir: Callable[[], str] | None = (
204
+ staticmethod(get_curr_dir) if get_curr_dir else None
205
+ )
206
+
207
+ return CustomListDirTool
208
+
209
+ @classmethod
210
+ def examples(cls) -> List[ToolMessage | tuple[str, ToolMessage]]:
211
+ return [
212
+ cls(dir_path="src"),
213
+ (
214
+ "I want to list the contents of src",
215
+ cls(dir_path="src"),
216
+ ),
217
+ ]
218
+
219
+ def handle(self) -> str:
220
+ # ASSUME: dir_path should be relative to the curr_dir_path
221
+ dir = (self._curr_dir and self._curr_dir()) or Path.cwd()
222
+ with chdir(dir):
223
+ contents = list_dir(self.dir_path)
224
+
225
+ if not contents:
226
+ return f"Directory not found or empty: {self.dir_path}"
227
+ contents_str = "\n".join(contents)
228
+ return dedent(
229
+ f"""
230
+ LISTING of directory {self.dir_path}:
231
+ ---------------------------
232
+ {contents_str}
233
+ """.strip()
234
+ )