langroid 0.18.0__tar.gz → 0.18.2__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 (143) hide show
  1. {langroid-0.18.0 → langroid-0.18.2}/PKG-INFO +3 -1
  2. {langroid-0.18.0 → langroid-0.18.2}/README.md +2 -0
  3. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/base.py +5 -1
  4. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/callbacks/chainlit.py +4 -1
  5. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/doc_chat_agent.py +15 -6
  6. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/orchestration.py +3 -2
  7. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/document_parser.py +20 -7
  8. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/system.py +32 -8
  9. {langroid-0.18.0 → langroid-0.18.2}/pyproject.toml +1 -1
  10. {langroid-0.18.0 → langroid-0.18.2}/LICENSE +0 -0
  11. {langroid-0.18.0 → langroid-0.18.2}/langroid/__init__.py +0 -0
  12. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/__init__.py +0 -0
  13. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/batch.py +0 -0
  14. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/callbacks/__init__.py +0 -0
  15. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/chat_agent.py +0 -0
  16. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/chat_document.py +0 -0
  17. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/helpers.py +0 -0
  18. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/junk +0 -0
  19. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/openai_assistant.py +0 -0
  20. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/__init__.py +0 -0
  21. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
  22. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/lance_rag/__init__.py +0 -0
  23. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
  24. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/lance_rag/lance_rag_task.py +0 -0
  25. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
  26. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/lance_tools.py +0 -0
  27. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/neo4j/__init__.py +0 -0
  28. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
  29. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
  30. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/neo4j/utils/__init__.py +0 -0
  31. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/neo4j/utils/system_message.py +0 -0
  32. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/relevance_extractor_agent.py +0 -0
  33. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/retriever_agent.py +0 -0
  34. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/sql/__init__.py +0 -0
  35. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
  36. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/sql/utils/__init__.py +0 -0
  37. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
  38. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
  39. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/sql/utils/system_message.py +0 -0
  40. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/sql/utils/tools.py +0 -0
  41. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/special/table_chat_agent.py +0 -0
  42. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/structured_message.py +0 -0
  43. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/task.py +0 -0
  44. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tool_message.py +0 -0
  45. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/__init__.py +0 -0
  46. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
  47. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/file_tools.py +0 -0
  48. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/google_search_tool.py +0 -0
  49. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/metaphor_search_tool.py +0 -0
  50. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/recipient_tool.py +0 -0
  51. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/retrieval_tool.py +0 -0
  52. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/rewind_tool.py +0 -0
  53. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/tools/segment_extract_tool.py +0 -0
  54. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/typed_task.py +0 -0
  55. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent/xml_tool_message.py +0 -0
  56. {langroid-0.18.0 → langroid-0.18.2}/langroid/agent_config.py +0 -0
  57. {langroid-0.18.0 → langroid-0.18.2}/langroid/cachedb/__init__.py +0 -0
  58. {langroid-0.18.0 → langroid-0.18.2}/langroid/cachedb/base.py +0 -0
  59. {langroid-0.18.0 → langroid-0.18.2}/langroid/cachedb/momento_cachedb.py +0 -0
  60. {langroid-0.18.0 → langroid-0.18.2}/langroid/cachedb/redis_cachedb.py +0 -0
  61. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/__init__.py +0 -0
  62. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/base.py +0 -0
  63. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/clustering.py +0 -0
  64. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/models.py +0 -0
  65. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/protoc/__init__.py +0 -0
  66. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/protoc/embeddings.proto +0 -0
  67. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
  68. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
  69. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
  70. {langroid-0.18.0 → langroid-0.18.2}/langroid/embedding_models/remote_embeds.py +0 -0
  71. {langroid-0.18.0 → langroid-0.18.2}/langroid/exceptions.py +0 -0
  72. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/.chainlit/config.toml +0 -0
  73. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/.chainlit/translations/en-US.json +0 -0
  74. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/__init__.py +0 -0
  75. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/azure_openai.py +0 -0
  76. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/base.py +0 -0
  77. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/config.py +0 -0
  78. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/mock_lm.py +0 -0
  79. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/openai_gpt.py +0 -0
  80. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/prompt_formatter/__init__.py +0 -0
  81. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/prompt_formatter/base.py +0 -0
  82. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
  83. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
  84. {langroid-0.18.0 → langroid-0.18.2}/langroid/language_models/utils.py +0 -0
  85. {langroid-0.18.0 → langroid-0.18.2}/langroid/mytypes.py +0 -0
  86. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/__init__.py +0 -0
  87. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/agent_chats.py +0 -0
  88. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/code-parsing.md +0 -0
  89. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/code_parser.py +0 -0
  90. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/config.py +0 -0
  91. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/image_text.py +0 -0
  92. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/para_sentence_split.py +0 -0
  93. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/parse_json.py +0 -0
  94. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/parser.py +0 -0
  95. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/repo_loader.py +0 -0
  96. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/routing.py +0 -0
  97. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/search.py +0 -0
  98. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/spider.py +0 -0
  99. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/table_loader.py +0 -0
  100. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/url_loader.py +0 -0
  101. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/url_loader_cookies.py +0 -0
  102. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/urls.py +0 -0
  103. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/utils.py +0 -0
  104. {langroid-0.18.0 → langroid-0.18.2}/langroid/parsing/web_search.py +0 -0
  105. {langroid-0.18.0 → langroid-0.18.2}/langroid/prompts/__init__.py +0 -0
  106. {langroid-0.18.0 → langroid-0.18.2}/langroid/prompts/chat-gpt4-system-prompt.md +0 -0
  107. {langroid-0.18.0 → langroid-0.18.2}/langroid/prompts/dialog.py +0 -0
  108. {langroid-0.18.0 → langroid-0.18.2}/langroid/prompts/prompts_config.py +0 -0
  109. {langroid-0.18.0 → langroid-0.18.2}/langroid/prompts/templates.py +0 -0
  110. {langroid-0.18.0 → langroid-0.18.2}/langroid/py.typed +0 -0
  111. {langroid-0.18.0 → langroid-0.18.2}/langroid/pydantic_v1/__init__.py +0 -0
  112. {langroid-0.18.0 → langroid-0.18.2}/langroid/pydantic_v1/main.py +0 -0
  113. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/.chainlit/config.toml +0 -0
  114. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/.chainlit/translations/en-US.json +0 -0
  115. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/__init__.py +0 -0
  116. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/algorithms/__init__.py +0 -0
  117. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/algorithms/graph.py +0 -0
  118. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/configuration.py +0 -0
  119. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/constants.py +0 -0
  120. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/docker.py +0 -0
  121. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/git_utils.py +0 -0
  122. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/globals.py +0 -0
  123. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/llms/__init__.py +0 -0
  124. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/llms/strings.py +0 -0
  125. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/logging.py +0 -0
  126. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/object_registry.py +0 -0
  127. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/output/__init__.py +0 -0
  128. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/output/citations.py +0 -0
  129. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/output/printing.py +0 -0
  130. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/output/status.py +0 -0
  131. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/pandas_utils.py +0 -0
  132. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/pydantic_utils.py +0 -0
  133. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/types.py +0 -0
  134. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/web/__init__.py +0 -0
  135. {langroid-0.18.0 → langroid-0.18.2}/langroid/utils/web/login.py +0 -0
  136. {langroid-0.18.0 → langroid-0.18.2}/langroid/vector_store/__init__.py +0 -0
  137. {langroid-0.18.0 → langroid-0.18.2}/langroid/vector_store/base.py +0 -0
  138. {langroid-0.18.0 → langroid-0.18.2}/langroid/vector_store/chromadb.py +0 -0
  139. {langroid-0.18.0 → langroid-0.18.2}/langroid/vector_store/lancedb.py +0 -0
  140. {langroid-0.18.0 → langroid-0.18.2}/langroid/vector_store/meilisearch.py +0 -0
  141. {langroid-0.18.0 → langroid-0.18.2}/langroid/vector_store/momento.py +0 -0
  142. {langroid-0.18.0 → langroid-0.18.2}/langroid/vector_store/qdrant_cloud.py +0 -0
  143. {langroid-0.18.0 → langroid-0.18.2}/langroid/vector_store/qdrantdb.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langroid
3
- Version: 0.18.0
3
+ Version: 0.18.2
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  License: MIT
6
6
  Author: Prasad Chalasani
@@ -244,6 +244,8 @@ teacher_task.run()
244
244
  <summary> <b>Click to expand</b></summary>
245
245
 
246
246
  - **Oct 2024:**
247
+ - **[0.18.0]** [LLMConfig.async_stream_quiet](https://langroid.github.io/langroid/notes/async-streaming/) flag to
248
+ turn off LLM output in async + stream mode.
247
249
  - **[0.17.0]** XML-based tools, see [docs](https://langroid.github.io/langroid/notes/xml-tools/).
248
250
  - **Sep 2024:**
249
251
  - **[0.16.0](https://github.com/langroid/langroid/releases/tag/0.16.0)** Support for OpenAI `o1-mini` and `o1-preview` models.
@@ -135,6 +135,8 @@ teacher_task.run()
135
135
  <summary> <b>Click to expand</b></summary>
136
136
 
137
137
  - **Oct 2024:**
138
+ - **[0.18.0]** [LLMConfig.async_stream_quiet](https://langroid.github.io/langroid/notes/async-streaming/) flag to
139
+ turn off LLM output in async + stream mode.
138
140
  - **[0.17.0]** XML-based tools, see [docs](https://langroid.github.io/langroid/notes/xml-tools/).
139
141
  - **Sep 2024:**
140
142
  - **[0.16.0](https://github.com/langroid/langroid/releases/tag/0.16.0)** Support for OpenAI `o1-mini` and `o1-preview` models.
@@ -91,6 +91,8 @@ class AgentConfig(BaseSettings):
91
91
  show_stats: bool = True # show token usage/cost stats?
92
92
  add_to_registry: bool = True # register agent in ObjectRegistry?
93
93
  respond_tools_only: bool = False # respond only to tool messages (not plain text)?
94
+ # allow multiple tool messages in a single response?
95
+ allow_multiple_tools: bool = True
94
96
 
95
97
  @validator("name")
96
98
  def check_name_alphanum(cls, v: str) -> str:
@@ -1120,6 +1122,8 @@ class Agent(ABC):
1120
1122
  # as a response to the tool message even though the tool was not intended
1121
1123
  # for this agent.
1122
1124
  return None
1125
+ if len(tools) > 1 and not self.config.allow_multiple_tools:
1126
+ return self.to_ChatDocument("ERROR: Use ONE tool at a time!")
1123
1127
  if len(tools) == 0:
1124
1128
  fallback_result = self.handle_message_fallback(msg)
1125
1129
  if fallback_result is None:
@@ -1316,7 +1320,7 @@ class Agent(ABC):
1316
1320
  """
1317
1321
  Convert result of a responder (agent_response or llm_response, or task.run()),
1318
1322
  or tool handler, or handle_message_fallback,
1319
- to a ChatDocument, to enabling handling by other
1323
+ to a ChatDocument, to enable handling by other
1320
1324
  responders/tasks in a task loop possibly involving multiple agents.
1321
1325
 
1322
1326
  Args:
@@ -244,8 +244,11 @@ class ChainlitAgentCallbacks:
244
244
  agent.callbacks.show_error_message = self.show_error_message
245
245
  agent.callbacks.show_start_response = self.show_start_response
246
246
  self.config = config
247
-
248
247
  self.agent: lr.Agent = agent
248
+ if self.agent.llm is not None:
249
+ # We don't want to suppress LLM output in async + streaming,
250
+ # since we often use chainlit async callbacks to display LLM output
251
+ self.agent.llm.config.async_stream_quiet = False
249
252
  if msg is not None:
250
253
  self.show_first_user_message(msg)
251
254
 
@@ -248,12 +248,6 @@ class DocChatAgent(ChatAgent):
248
248
  def ingest(self) -> None:
249
249
  """
250
250
  Chunk + embed + store docs specified by self.config.doc_paths
251
-
252
- Returns:
253
- dict with keys:
254
- n_splits: number of splits
255
- urls: list of urls
256
- paths: list of file paths
257
251
  """
258
252
  if len(self.config.doc_paths) == 0:
259
253
  # we must be using a previously defined collection
@@ -1110,6 +1104,14 @@ class DocChatAgent(ChatAgent):
1110
1104
  Returns:
1111
1105
 
1112
1106
  """
1107
+
1108
+ if (
1109
+ self.vecdb is None
1110
+ or self.vecdb.config.collection_name
1111
+ not in self.vecdb.list_collections(empty=False)
1112
+ ):
1113
+ return []
1114
+
1113
1115
  # if we are using cross-encoder reranking or reciprocal rank fusion (RRF),
1114
1116
  # we can retrieve more docs during retrieval, and leave it to the cross-encoder
1115
1117
  # or RRF reranking to whittle down to self.config.parsing.n_similar_docs
@@ -1275,6 +1277,13 @@ class DocChatAgent(ChatAgent):
1275
1277
  List[Document]: list of relevant extracts
1276
1278
 
1277
1279
  """
1280
+ if (
1281
+ self.vecdb is None
1282
+ or self.vecdb.config.collection_name
1283
+ not in self.vecdb.list_collections(empty=False)
1284
+ ):
1285
+ return query, []
1286
+
1278
1287
  if len(self.dialog) > 0 and not self.config.assistant_mode:
1279
1288
  # Regardless of whether we are in conversation mode or not,
1280
1289
  # for relevant doc/chunk extraction, we must convert the query
@@ -200,12 +200,13 @@ class DonePassTool(PassTool):
200
200
 
201
201
 
202
202
  class ForwardTool(PassTool):
203
- """Tool for forwarding the received msg (ChatDocument) to another agent.
203
+ """Tool for forwarding the received msg (ChatDocument) to another agent or entity.
204
204
  Similar to PassTool, but with a specified recipient agent.
205
205
  """
206
206
 
207
207
  purpose: str = """
208
- To forward the current message to an <agent>.
208
+ To forward the current message to an <agent>, where <agent>
209
+ could be the name of an agent, or an entity such as "user", "llm".
209
210
  """
210
211
  request: str = "forward_tool"
211
212
  agent: str
@@ -81,13 +81,25 @@ def is_plain_text(path_or_bytes: str | bytes) -> bool:
81
81
  else:
82
82
  content = path_or_bytes[:1024]
83
83
  try:
84
+ # Use magic to detect the MIME type
85
+ import magic
86
+
87
+ mime_type = magic.from_buffer(content, mime=True)
88
+
89
+ # Check if the MIME type is not a text type
90
+ if not mime_type.startswith("text/"):
91
+ return False
92
+
84
93
  # Attempt to decode the content as UTF-8
85
94
  content = content[: find_last_full_char(content)]
86
95
 
87
- _ = content.decode("utf-8")
88
- # Additional checks can go here, e.g., to verify that the content
89
- # doesn't contain too many unusual characters for it to be considered text
90
- return True
96
+ try:
97
+ _ = content.decode("utf-8")
98
+ # Additional checks can go here, e.g., to verify that the content
99
+ # doesn't contain too many unusual characters for it to be considered text
100
+ return True
101
+ except UnicodeDecodeError:
102
+ return False
91
103
  except UnicodeDecodeError:
92
104
  # If decoding fails, it's likely not plain text (or not encoded in UTF-8)
93
105
  return False
@@ -123,7 +135,8 @@ class DocumentParser(Parser):
123
135
  Returns:
124
136
  DocumentParser: An instance of a DocumentParser subclass.
125
137
  """
126
- if DocumentParser._document_type(source, doc_type) == DocumentType.PDF:
138
+ inferred_doc_type = DocumentParser._document_type(source, doc_type)
139
+ if inferred_doc_type == DocumentType.PDF:
127
140
  if config.pdf.library == "fitz":
128
141
  return FitzPDFParser(source, config)
129
142
  elif config.pdf.library == "pypdf":
@@ -138,7 +151,7 @@ class DocumentParser(Parser):
138
151
  raise ValueError(
139
152
  f"Unsupported PDF library specified: {config.pdf.library}"
140
153
  )
141
- elif DocumentParser._document_type(source, doc_type) == DocumentType.DOCX:
154
+ elif inferred_doc_type == DocumentType.DOCX:
142
155
  if config.docx.library == "unstructured":
143
156
  return UnstructuredDocxParser(source, config)
144
157
  elif config.docx.library == "python-docx":
@@ -147,7 +160,7 @@ class DocumentParser(Parser):
147
160
  raise ValueError(
148
161
  f"Unsupported DOCX library specified: {config.docx.library}"
149
162
  )
150
- elif DocumentParser._document_type(source, doc_type) == DocumentType.DOC:
163
+ elif inferred_doc_type == DocumentType.DOC:
151
164
  return UnstructuredDocParser(source, config)
152
165
  else:
153
166
  source_name = source if isinstance(source, str) else "bytes"
@@ -10,7 +10,7 @@ import socket
10
10
  import traceback
11
11
  import uuid
12
12
  from pathlib import Path
13
- from typing import Any
13
+ from typing import Any, Literal
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
16
 
@@ -186,24 +186,48 @@ def generate_unique_id() -> str:
186
186
  return str(uuid.uuid4())
187
187
 
188
188
 
189
- def create_file(filepath: str | Path, content: str = "") -> None:
189
+ def create_file(
190
+ filepath: str | Path,
191
+ content: str = "",
192
+ if_exists: Literal["overwrite", "skip", "error", "append"] = "overwrite",
193
+ ) -> None:
190
194
  """
191
- Create a file with the given content in the specified directory.
195
+ Create, overwrite or append to a file, with the given content
196
+ at the specified filepath.
192
197
  If content is empty, it will simply touch to create an empty file.
193
198
 
194
199
  Args:
195
200
  filepath (str|Path): The relative path of the file to be created
196
201
  content (str): The content to be written to the file
202
+ if_exists (Literal["overwrite", "skip", "error", "append"]):
203
+ Action to take if file exists
197
204
  """
198
- Path(filepath).parent.mkdir(parents=True, exist_ok=True)
199
- if content == "":
200
- Path(filepath).touch()
205
+ filepath = Path(filepath)
206
+ filepath.parent.mkdir(parents=True, exist_ok=True)
207
+
208
+ if filepath.exists():
209
+ if if_exists == "skip":
210
+ logger.warning(f"File already exists, skipping: {filepath}")
211
+ return
212
+ elif if_exists == "error":
213
+ raise FileExistsError(f"File already exists: {filepath}")
214
+ elif if_exists == "append":
215
+ mode = "a"
216
+ else: # overwrite
217
+ mode = "w"
218
+ else:
219
+ mode = "w"
220
+
221
+ if content == "" and mode in ["a", "w"]:
222
+ filepath.touch()
223
+ logger.warning(f"Empty file created: {filepath}")
201
224
  else:
202
225
  # the newline = '\n` argument is used to ensure that
203
226
  # newlines in the content are written as actual line breaks
204
- with open(filepath, "w", newline="\n") as f:
227
+ with open(filepath, mode, newline="\n") as f:
205
228
  f.write(content)
206
- logger.warning(f"File created/updated: {filepath}")
229
+ action = "appended to" if mode == "a" else "created/updated in"
230
+ logger.warning(f"Content {action}: {filepath}")
207
231
 
208
232
 
209
233
  def read_file(path: str, line_numbers: bool = False) -> str:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "langroid"
3
- version = "0.18.0"
3
+ version = "0.18.2"
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