langroid 0.1.73__tar.gz → 0.1.76__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 (92) hide show
  1. {langroid-0.1.73 → langroid-0.1.76}/PKG-INFO +5 -7
  2. {langroid-0.1.73 → langroid-0.1.76}/README.md +3 -6
  3. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/doc_chat_agent.py +6 -4
  4. langroid-0.1.73/langroid/parsing/pdf_parser.py → langroid-0.1.76/langroid/parsing/document_parser.py +122 -25
  5. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/parser.py +5 -0
  6. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/repo_loader.py +4 -4
  7. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/url_loader.py +4 -4
  8. {langroid-0.1.73 → langroid-0.1.76}/langroid/vector_store/base.py +1 -1
  9. {langroid-0.1.73 → langroid-0.1.76}/pyproject.toml +2 -1
  10. langroid-0.1.76/setup.py +104 -0
  11. langroid-0.1.73/setup.py +0 -103
  12. {langroid-0.1.73 → langroid-0.1.76}/LICENSE +0 -0
  13. {langroid-0.1.73 → langroid-0.1.76}/langroid/__init__.py +0 -0
  14. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/__init__.py +0 -0
  15. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/base.py +0 -0
  16. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/chat_agent.py +0 -0
  17. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/chat_document.py +0 -0
  18. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/helpers.py +0 -0
  19. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/junk +0 -0
  20. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/__init__.py +0 -0
  21. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/recipient_validator_agent.py +0 -0
  22. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/retriever_agent.py +0 -0
  23. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/sql/__init__.py +0 -0
  24. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
  25. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/sql/utils/__init__.py +0 -0
  26. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
  27. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
  28. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/sql/utils/system_message.py +0 -0
  29. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/sql/utils/tools.py +0 -0
  30. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/special/table_chat_agent.py +0 -0
  31. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/task.py +0 -0
  32. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/tool_message.py +0 -0
  33. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/tools/__init__.py +0 -0
  34. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/tools/google_search_tool.py +0 -0
  35. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent/tools/recipient_tool.py +0 -0
  36. {langroid-0.1.73 → langroid-0.1.76}/langroid/agent_config.py +0 -0
  37. {langroid-0.1.73 → langroid-0.1.76}/langroid/cachedb/__init__.py +0 -0
  38. {langroid-0.1.73 → langroid-0.1.76}/langroid/cachedb/base.py +0 -0
  39. {langroid-0.1.73 → langroid-0.1.76}/langroid/cachedb/momento_cachedb.py +0 -0
  40. {langroid-0.1.73 → langroid-0.1.76}/langroid/cachedb/redis_cachedb.py +0 -0
  41. {langroid-0.1.73 → langroid-0.1.76}/langroid/embedding_models/__init__.py +0 -0
  42. {langroid-0.1.73 → langroid-0.1.76}/langroid/embedding_models/base.py +0 -0
  43. {langroid-0.1.73 → langroid-0.1.76}/langroid/embedding_models/clustering.py +0 -0
  44. {langroid-0.1.73 → langroid-0.1.76}/langroid/embedding_models/models.py +0 -0
  45. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/__init__.py +0 -0
  46. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/azure_openai.py +0 -0
  47. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/base.py +0 -0
  48. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/config.py +0 -0
  49. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/openai_gpt.py +0 -0
  50. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/prompt_formatter/__init__.py +0 -0
  51. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/prompt_formatter/base.py +0 -0
  52. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
  53. {langroid-0.1.73 → langroid-0.1.76}/langroid/language_models/utils.py +0 -0
  54. {langroid-0.1.73 → langroid-0.1.76}/langroid/mytypes.py +0 -0
  55. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/__init__.py +0 -0
  56. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/agent_chats.py +0 -0
  57. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/code-parsing.md +0 -0
  58. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/code_parser.py +0 -0
  59. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/config.py +0 -0
  60. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/json.py +0 -0
  61. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/para_sentence_split.py +0 -0
  62. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/search.py +0 -0
  63. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/table_loader.py +0 -0
  64. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/url_loader_cookies.py +0 -0
  65. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/urls.py +0 -0
  66. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/utils.py +0 -0
  67. {langroid-0.1.73 → langroid-0.1.76}/langroid/parsing/web_search.py +0 -0
  68. {langroid-0.1.73 → langroid-0.1.76}/langroid/prompts/__init__.py +0 -0
  69. {langroid-0.1.73 → langroid-0.1.76}/langroid/prompts/dialog.py +0 -0
  70. {langroid-0.1.73 → langroid-0.1.76}/langroid/prompts/prompts_config.py +0 -0
  71. {langroid-0.1.73 → langroid-0.1.76}/langroid/prompts/templates.py +0 -0
  72. {langroid-0.1.73 → langroid-0.1.76}/langroid/prompts/transforms.py +0 -0
  73. {langroid-0.1.73 → langroid-0.1.76}/langroid/scripts/__init__.py +0 -0
  74. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/__init__.py +0 -0
  75. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/configuration.py +0 -0
  76. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/constants.py +0 -0
  77. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/docker.py +0 -0
  78. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/globals.py +0 -0
  79. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/llms/__init__.py +0 -0
  80. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/llms/strings.py +0 -0
  81. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/logging.py +0 -0
  82. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/output/__init__.py +0 -0
  83. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/output/printing.py +0 -0
  84. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/pydantic_utils.py +0 -0
  85. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/system.py +0 -0
  86. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/web/__init__.py +0 -0
  87. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/web/login.py +0 -0
  88. {langroid-0.1.73 → langroid-0.1.76}/langroid/utils/web/selenium_login.py +0 -0
  89. {langroid-0.1.73 → langroid-0.1.76}/langroid/vector_store/__init__.py +0 -0
  90. {langroid-0.1.73 → langroid-0.1.76}/langroid/vector_store/chromadb.py +0 -0
  91. {langroid-0.1.73 → langroid-0.1.76}/langroid/vector_store/qdrant_cloud.py +0 -0
  92. {langroid-0.1.73 → langroid-0.1.76}/langroid/vector_store/qdrantdb.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langroid
3
- Version: 0.1.73
3
+ Version: 0.1.76
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  License: MIT
6
6
  Author: Prasad Chalasani
@@ -70,6 +70,7 @@ Requires-Dist: trafilatura (>=1.5.0,<2.0.0)
70
70
  Requires-Dist: typer (>=0.7.0,<0.8.0)
71
71
  Requires-Dist: types-redis (>=4.5.5.2,<5.0.0.0)
72
72
  Requires-Dist: types-requests (>=2.31.0.1,<3.0.0.0)
73
+ Requires-Dist: unstructured[docx,pdf,pptx] (>=0.10.16,<0.11.0)
73
74
  Requires-Dist: wget (>=3.2,<4.0)
74
75
  Description-Content-Type: text/markdown
75
76
 
@@ -130,8 +131,8 @@ This Multi-Agent paradigm is inspired by the
130
131
  [Actor Framework](https://en.wikipedia.org/wiki/Actor_model)
131
132
  (but you do not need to know anything about this!).
132
133
 
133
- Langroid is a fresh take on LLM app-development, where considerable thought has gone
134
- into simplifying the developer experience. It does not use `Langchain` or `Llama-Index`.
134
+ `Langroid` is a fresh take on LLM app-development, where considerable thought has gone
135
+ into simplifying the developer experience; it does not use `Langchain`.
135
136
 
136
137
  We welcome contributions -- See the [contributions](./CONTRIBUTING.md) document
137
138
  for ideas on what to contribute.
@@ -169,7 +170,7 @@ See [this test](tests/main/test_recipient_tool.py) for example usage.
169
170
  - **0.1.27**: Added [support](langroid/cachedb/momento_cachedb.py)
170
171
  for [Momento Serverless Cache](https://www.gomomento.com/) as an alternative to Redis.
171
172
  - **0.1.24**: [`DocChatAgent`](langroid/agent/special/doc_chat_agent.py)
172
- now [accepts](langroid/parsing/pdf_parser.py) PDF files or URLs.
173
+ now [accepts](langroid/parsing/document_parser.py) PDF files or URLs.
173
174
 
174
175
  </details>
175
176
 
@@ -233,9 +234,6 @@ Here is what it looks like in action:
233
234
 
234
235
  # :gear: Installation and Setup
235
236
 
236
- :whale: For a simpler setup, see the Docker section below, which lets you get started just
237
- by setting up environment variables in a `.env` file.
238
-
239
237
  ### Install `langroid`
240
238
  Langroid requires Python 3.11+. We recommend using a virtual environment.
241
239
  Use `pip` to install `langroid` (from PyPi) to your virtual environment:
@@ -55,8 +55,8 @@ This Multi-Agent paradigm is inspired by the
55
55
  [Actor Framework](https://en.wikipedia.org/wiki/Actor_model)
56
56
  (but you do not need to know anything about this!).
57
57
 
58
- Langroid is a fresh take on LLM app-development, where considerable thought has gone
59
- into simplifying the developer experience. It does not use `Langchain` or `Llama-Index`.
58
+ `Langroid` is a fresh take on LLM app-development, where considerable thought has gone
59
+ into simplifying the developer experience; it does not use `Langchain`.
60
60
 
61
61
  We welcome contributions -- See the [contributions](./CONTRIBUTING.md) document
62
62
  for ideas on what to contribute.
@@ -94,7 +94,7 @@ See [this test](tests/main/test_recipient_tool.py) for example usage.
94
94
  - **0.1.27**: Added [support](langroid/cachedb/momento_cachedb.py)
95
95
  for [Momento Serverless Cache](https://www.gomomento.com/) as an alternative to Redis.
96
96
  - **0.1.24**: [`DocChatAgent`](langroid/agent/special/doc_chat_agent.py)
97
- now [accepts](langroid/parsing/pdf_parser.py) PDF files or URLs.
97
+ now [accepts](langroid/parsing/document_parser.py) PDF files or URLs.
98
98
 
99
99
  </details>
100
100
 
@@ -158,9 +158,6 @@ Here is what it looks like in action:
158
158
 
159
159
  # :gear: Installation and Setup
160
160
 
161
- :whale: For a simpler setup, see the Docker section below, which lets you get started just
162
- by setting up environment variables in a `.env` file.
163
-
164
161
  ### Install `langroid`
165
162
  Langroid requires Python 3.11+. We recommend using a virtual environment.
166
163
  Use `pip` to install `langroid` (from PyPi) to your virtual environment:
@@ -81,6 +81,7 @@ class DocChatAgentConfig(ChatAgentConfig):
81
81
  # and use the embed(A) to find similar chunks in vecdb.
82
82
  # Referred to as HyDE in the paper:
83
83
  # https://arxiv.org/pdf/2212.10496.pdf
84
+ # It is False by default; its benefits depends on the context.
84
85
  hypothetical_answer: bool = False
85
86
  n_query_rephrases: int = 0
86
87
  use_fuzzy_match: bool = True
@@ -391,13 +392,14 @@ class DocChatAgent(ChatAgent):
391
392
  if self.config.hypothetical_answer:
392
393
  with console.status("[cyan]LLM generating hypothetical answer..."):
393
394
  with StreamingIfAllowed(self.llm, False):
395
+ # TODO: provide an easy way to
396
+ # Adjust this prompt depending on context.
394
397
  answer = self.llm_response_forget(
395
398
  f"""
396
- Give a sample answer to the following query,
399
+ Give an ideal answer to the following query,
397
400
  in up to 3 sentences. Do not explain yourself,
398
401
  and do not apologize, just show
399
- a possible answer. Guess a hypothetical answer
400
- even if you do not have any information.
402
+ a good possible answer, even if you do not have any information.
401
403
  Preface your answer with "HYPOTHETICAL ANSWER: "
402
404
 
403
405
  QUERY: {query}
@@ -504,7 +506,7 @@ class DocChatAgent(ChatAgent):
504
506
 
505
507
  with console.status("[cyan]LLM Extracting verbatim passages..."):
506
508
  with StreamingIfAllowed(self.llm, False):
507
- # these are async calls, one per passage
509
+ # these are async calls, one per passage; turn off streaming
508
510
  extracts = self.llm.get_verbatim_extracts(query, passages)
509
511
  extracts = [e for e in extracts if e.content != NO_ANSWER]
510
512
 
@@ -1,5 +1,6 @@
1
1
  import re
2
2
  from abc import abstractmethod
3
+ from enum import Enum
3
4
  from io import BytesIO
4
5
  from typing import Any, Generator, List, Tuple
5
6
 
@@ -12,35 +13,56 @@ from langroid.mytypes import DocMetaData, Document
12
13
  from langroid.parsing.parser import Parser, ParsingConfig
13
14
 
14
15
 
15
- class PdfParser(Parser):
16
+ class DocumentType(str, Enum):
17
+ PDF = "pdf"
18
+ DOCX = "docx"
19
+
20
+
21
+ class DocumentParser(Parser):
16
22
  """
17
- Abstract base class for extracting text from PDFs.
23
+ Abstract base class for extracting text from special types of docs
24
+ such as PDFs or Docx.
18
25
 
19
26
  Attributes:
20
- source (str): The PDF source, either a URL or a file path.
21
- pdf_bytes (BytesIO): BytesIO object containing the PDF data.
27
+ source (str): The source, either a URL or a file path.
28
+ doc_bytes (BytesIO): BytesIO object containing the doc data.
22
29
  """
23
30
 
24
31
  @classmethod
25
- def create(cls, source: str, config: ParsingConfig) -> "PdfParser":
32
+ def create(cls, source: str, config: ParsingConfig) -> "DocumentParser":
26
33
  """
27
- Create a PDF Parser instance based on config.library specified.
34
+ Create a DocumentParser instance based on source type
35
+ and config.<source_type>.library specified.
28
36
 
29
37
  Args:
30
38
  source (str): The source of the PDF, either a URL or a file path.
31
39
  config (ParserConfig): The parser configuration.
32
40
 
33
41
  Returns:
34
- PdfParser: An instance of a PDF Parser subclass.
35
- """
36
- if config.pdf.library == "fitz":
37
- return FitzPdfParser(source, config)
38
- elif config.pdf.library == "pypdf":
39
- return PyPdfParser(source, config)
40
- elif config.pdf.library == "pdfplumber":
41
- return PdfPlumberParser(source, config)
42
+ DocumentParser: An instance of a DocumentParser subclass.
43
+ """
44
+ if DocumentParser._document_type(source) == DocumentType.PDF:
45
+ if config.pdf.library == "fitz":
46
+ return FitzPDFParser(source, config)
47
+ elif config.pdf.library == "pypdf":
48
+ return PyPDFParser(source, config)
49
+ elif config.pdf.library == "pdfplumber":
50
+ return PDFPlumberParser(source, config)
51
+ elif config.pdf.library == "unstructured":
52
+ return UnstructuredPDFParser(source, config)
53
+ else:
54
+ raise ValueError(
55
+ f"Unsupported PDF library specified: {config.pdf.library}"
56
+ )
57
+ elif DocumentParser._document_type(source) == DocumentType.DOCX:
58
+ if config.docx.library == "unstructured":
59
+ return UnstructuredDocxParser(source, config)
60
+ else:
61
+ raise ValueError(
62
+ f"Unsupported DOCX library specified: {config.docx.library}"
63
+ )
42
64
  else:
43
- raise ValueError(f"Unsupported library specified: {config.pdf.library}")
65
+ raise ValueError(f"Unsupported document type: {source}")
44
66
 
45
67
  def __init__(self, source: str, config: ParsingConfig):
46
68
  """
@@ -52,14 +74,32 @@ class PdfParser(Parser):
52
74
  super().__init__(config)
53
75
  self.source = source
54
76
  self.config = config
55
- self.pdf_bytes = self._load_pdf_as_bytesio()
77
+ self.doc_bytes = self._load_doc_as_bytesio()
78
+
79
+ @staticmethod
80
+ def _document_type(source: str) -> DocumentType:
81
+ """
82
+ Determine the type of document based on the source.
83
+
84
+ Args:
85
+ source (str): The source of the PDF, either a URL or a file path.
86
+
87
+ Returns:
88
+ str: The document type.
89
+ """
90
+ if source.lower().endswith(".pdf"):
91
+ return DocumentType.PDF
92
+ elif source.lower().endswith(".docx"):
93
+ return DocumentType.DOCX
94
+ else:
95
+ raise ValueError(f"Unsupported document type: {source}")
56
96
 
57
- def _load_pdf_as_bytesio(self) -> BytesIO:
97
+ def _load_doc_as_bytesio(self) -> BytesIO:
58
98
  """
59
- Load the PDF into a BytesIO object.
99
+ Load the docs into a BytesIO object.
60
100
 
61
101
  Returns:
62
- BytesIO: A BytesIO object containing the PDF data.
102
+ BytesIO: A BytesIO object containing the doc data.
63
103
  """
64
104
  if self.source.startswith(("http://", "https://")):
65
105
  response = requests.get(self.source)
@@ -159,7 +199,7 @@ class PdfParser(Parser):
159
199
  return docs
160
200
 
161
201
 
162
- class FitzPdfParser(PdfParser):
202
+ class FitzPDFParser(DocumentParser):
163
203
  """
164
204
  Parser for processing PDFs using the `fitz` library.
165
205
  """
@@ -171,7 +211,7 @@ class FitzPdfParser(PdfParser):
171
211
  Returns:
172
212
  Generator[fitz.Page]: Generator yielding each page.
173
213
  """
174
- doc = fitz.open(stream=self.pdf_bytes, filetype="pdf")
214
+ doc = fitz.open(stream=self.doc_bytes, filetype="pdf")
175
215
  for i, page in enumerate(doc):
176
216
  yield i, page
177
217
  doc.close()
@@ -189,7 +229,7 @@ class FitzPdfParser(PdfParser):
189
229
  return self.fix_text(page.get_text())
190
230
 
191
231
 
192
- class PyPdfParser(PdfParser):
232
+ class PyPDFParser(DocumentParser):
193
233
  """
194
234
  Parser for processing PDFs using the `pypdf` library.
195
235
  """
@@ -201,7 +241,7 @@ class PyPdfParser(PdfParser):
201
241
  Returns:
202
242
  Generator[pypdf.pdf.PageObject]: Generator yielding each page.
203
243
  """
204
- reader = pypdf.PdfReader(self.pdf_bytes)
244
+ reader = pypdf.PdfReader(self.doc_bytes)
205
245
  for i, page in enumerate(reader.pages):
206
246
  yield i, page
207
247
 
@@ -218,7 +258,7 @@ class PyPdfParser(PdfParser):
218
258
  return self.fix_text(page.extract_text())
219
259
 
220
260
 
221
- class PdfPlumberParser(PdfParser):
261
+ class PDFPlumberParser(DocumentParser):
222
262
  """
223
263
  Parser for processing PDFs using the `pdfplumber` library.
224
264
  """
@@ -232,7 +272,7 @@ class PdfPlumberParser(PdfParser):
232
272
  Returns:
233
273
  Generator[pdfplumber.Page]: Generator yielding each page.
234
274
  """
235
- with pdfplumber.open(self.pdf_bytes) as pdf:
275
+ with pdfplumber.open(self.doc_bytes) as pdf:
236
276
  for i, page in enumerate(pdf.pages):
237
277
  yield i, page
238
278
 
@@ -247,3 +287,60 @@ class PdfPlumberParser(PdfParser):
247
287
  str: Extracted text from the page.
248
288
  """
249
289
  return self.fix_text(page.extract_text())
290
+
291
+
292
+ class UnstructuredPDFParser(DocumentParser):
293
+ """
294
+ Parser for processing PDF files using the `unstructured` library.
295
+ """
296
+
297
+ def iterate_pages(self) -> Generator[Tuple[int, Any], None, None]: # type: ignore
298
+ from unstructured.partition.pdf import partition_pdf
299
+
300
+ elements = partition_pdf(file=self.doc_bytes, include_page_breaks=True)
301
+ for i, el in enumerate(elements):
302
+ yield i, el
303
+
304
+ def extract_text_from_page(self, page: Any) -> str:
305
+ """
306
+ Extract text from a given `unstructured` element.
307
+
308
+ Args:
309
+ page (unstructured element): The `unstructured` element object.
310
+
311
+ Returns:
312
+ str: Extracted text from the element.
313
+ """
314
+ return self.fix_text(str(page))
315
+
316
+
317
+ class UnstructuredDocxParser(DocumentParser):
318
+ """
319
+ Parser for processing DOCX files using the `unstructured` library.
320
+ """
321
+
322
+ def iterate_pages(self) -> Generator[Tuple[int, Any], None, None]: # type: ignore
323
+ from unstructured.partition.docx import partition_docx
324
+
325
+ elements = partition_docx(file=self.doc_bytes)
326
+ for i, el in enumerate(elements):
327
+ yield i, el
328
+
329
+ def extract_text_from_page(self, page: Any) -> str:
330
+ """
331
+ Extract text from a given `unstructured` element.
332
+
333
+ Note:
334
+ The concept of "pages" doesn't actually exist in the .docx file format in
335
+ the same way it does in formats like .pdf. A .docx file is made up of a
336
+ series of elements like paragraphs and tables, but the division into
337
+ pages is done dynamically based on the rendering settings (like the page
338
+ size, margin size, font size, etc.).
339
+
340
+ Args:
341
+ page (unstructured element): The `unstructured` element object.
342
+
343
+ Returns:
344
+ str: Extracted text from the element.
345
+ """
346
+ return self.fix_text(str(page))
@@ -23,6 +23,10 @@ class PdfParsingConfig(BaseSettings):
23
23
  library: str = "pdfplumber"
24
24
 
25
25
 
26
+ class DocxParsingConfig(BaseSettings):
27
+ library: str = "unstructured"
28
+
29
+
26
30
  class ParsingConfig(BaseSettings):
27
31
  splitter: str = Splitter.TOKENS
28
32
  chunk_size: int = 200 # aim for this many tokens per chunk
@@ -35,6 +39,7 @@ class ParsingConfig(BaseSettings):
35
39
  separators: List[str] = ["\n\n", "\n", " ", ""]
36
40
  token_encoding_model: str = "text-embedding-ada-002"
37
41
  pdf: PdfParsingConfig = PdfParsingConfig()
42
+ docx: DocxParsingConfig = DocxParsingConfig()
38
43
 
39
44
 
40
45
  class Parser:
@@ -18,8 +18,8 @@ from github.Repository import Repository
18
18
  from pydantic import BaseSettings
19
19
 
20
20
  from langroid.mytypes import DocMetaData, Document
21
+ from langroid.parsing.document_parser import DocumentParser
21
22
  from langroid.parsing.parser import Parser, ParsingConfig
22
- from langroid.parsing.pdf_parser import PdfParser
23
23
 
24
24
  logger = logging.getLogger(__name__)
25
25
 
@@ -493,12 +493,12 @@ class RepoLoader:
493
493
 
494
494
  for file_path in file_paths:
495
495
  _, file_extension = os.path.splitext(file_path)
496
- if file_extension.lower() == ".pdf":
497
- pdf_parser = PdfParser.create(
496
+ if file_extension.lower() in [".pdf", ".docx"]:
497
+ doc_parser = DocumentParser.create(
498
498
  file_path,
499
499
  parser.config,
500
500
  )
501
- docs.extend(pdf_parser.get_doc_chunks())
501
+ docs.extend(doc_parser.get_doc_chunks())
502
502
  else:
503
503
  with open(file_path, "r") as f:
504
504
  if lines is not None:
@@ -9,8 +9,8 @@ from trafilatura.downloads import (
9
9
  )
10
10
 
11
11
  from langroid.mytypes import DocMetaData, Document
12
+ from langroid.parsing.document_parser import DocumentParser
12
13
  from langroid.parsing.parser import Parser, ParsingConfig
13
- from langroid.parsing.pdf_parser import PdfParser
14
14
 
15
15
  logging.getLogger("trafilatura").setLevel(logging.ERROR)
16
16
 
@@ -44,12 +44,12 @@ class URLLoader:
44
44
  sleep_time=5,
45
45
  )
46
46
  for url, result in buffered_downloads(buffer, threads):
47
- if url.lower().endswith(".pdf"):
48
- pdf_parser = PdfParser.create(
47
+ if url.lower().endswith(".pdf") or url.lower().endswith(".docx"):
48
+ doc_parser = DocumentParser.create(
49
49
  url,
50
50
  self.parser.config,
51
51
  )
52
- docs.extend(pdf_parser.get_doc_chunks())
52
+ docs.extend(doc_parser.get_doc_chunks())
53
53
  else:
54
54
  text = trafilatura.extract(
55
55
  result,
@@ -132,4 +132,4 @@ class VectorStore(ABC):
132
132
  def show_if_debug(self, doc_score_pairs: List[Tuple[Document, float]]) -> None:
133
133
  if settings.debug:
134
134
  for i, (d, s) in enumerate(doc_score_pairs):
135
- print_long_text("red", "italic red", f"MATCH-{i}", d.content)
135
+ print_long_text("red", "italic red", f"\nMATCH-{i}\n", d.content)
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "langroid"
3
- version = "0.1.73"
3
+ version = "0.1.76"
4
4
  description = "Harness LLMs with Multi-Agent Programming"
5
5
  authors = ["Prasad Chalasani <pchalasani@gmail.com>"]
6
6
  readme = "README.md"
@@ -54,6 +54,7 @@ sqlalchemy = "^2.0.19"
54
54
  prettytable = "^3.8.0"
55
55
  google-api-python-client = "^2.95.0"
56
56
  lxml = "^4.9.3"
57
+ unstructured = {extras = ["docx", "pptx", "pdf"], version = "^0.10.16"}
57
58
 
58
59
  # optional; see extras section below
59
60
  sentence-transformers = {version="2.2.2", optional=true}
@@ -0,0 +1,104 @@
1
+ # -*- coding: utf-8 -*-
2
+ from setuptools import setup
3
+
4
+ packages = \
5
+ ['langroid',
6
+ 'langroid.agent',
7
+ 'langroid.agent.special',
8
+ 'langroid.agent.special.sql',
9
+ 'langroid.agent.special.sql.utils',
10
+ 'langroid.agent.tools',
11
+ 'langroid.cachedb',
12
+ 'langroid.embedding_models',
13
+ 'langroid.language_models',
14
+ 'langroid.language_models.prompt_formatter',
15
+ 'langroid.parsing',
16
+ 'langroid.prompts',
17
+ 'langroid.scripts',
18
+ 'langroid.utils',
19
+ 'langroid.utils.llms',
20
+ 'langroid.utils.output',
21
+ 'langroid.utils.web',
22
+ 'langroid.vector_store']
23
+
24
+ package_data = \
25
+ {'': ['*']}
26
+
27
+ install_requires = \
28
+ ['autopep8>=2.0.2,<3.0.0',
29
+ 'black[jupyter]>=23.3.0,<24.0.0',
30
+ 'bs4>=0.0.1,<0.0.2',
31
+ 'chromadb>=0.3.21,<0.4.0',
32
+ 'colorlog>=6.7.0,<7.0.0',
33
+ 'faker>=18.9.0,<19.0.0',
34
+ 'fakeredis>=2.12.1,<3.0.0',
35
+ 'fire>=0.5.0,<0.6.0',
36
+ 'flake8>=6.0.0,<7.0.0',
37
+ 'google-api-python-client>=2.95.0,<3.0.0',
38
+ 'halo>=0.0.31,<0.0.32',
39
+ 'lxml>=4.9.3,<5.0.0',
40
+ 'mkdocs-awesome-pages-plugin>=2.8.0,<3.0.0',
41
+ 'mkdocs-gen-files>=0.4.0,<0.5.0',
42
+ 'mkdocs-jupyter>=0.24.1,<0.25.0',
43
+ 'mkdocs-literate-nav>=0.6.0,<0.7.0',
44
+ 'mkdocs-material>=9.1.5,<10.0.0',
45
+ 'mkdocs-rss-plugin>=1.8.0,<2.0.0',
46
+ 'mkdocs-section-index>=0.3.5,<0.4.0',
47
+ 'mkdocs>=1.4.2,<2.0.0',
48
+ 'mkdocstrings[python]>=0.21.2,<0.22.0',
49
+ 'momento>=1.7.0,<2.0.0',
50
+ 'mypy>=1.2.0,<2.0.0',
51
+ 'nltk>=3.8.1,<4.0.0',
52
+ 'openai>=0.27.5,<0.28.0',
53
+ 'pandas>=2.0.3,<3.0.0',
54
+ 'pdfplumber>=0.10.2,<0.11.0',
55
+ 'pre-commit>=3.3.2,<4.0.0',
56
+ 'prettytable>=3.8.0,<4.0.0',
57
+ 'pydantic==1.10.11',
58
+ 'pygithub>=1.58.1,<2.0.0',
59
+ 'pygments>=2.15.1,<3.0.0',
60
+ 'pymupdf>=1.23.3,<2.0.0',
61
+ 'pyparsing>=3.0.9,<4.0.0',
62
+ 'pypdf>=3.12.2,<4.0.0',
63
+ 'python-dotenv>=1.0.0,<2.0.0',
64
+ 'qdrant-client>=1.3.1,<2.0.0',
65
+ 'rank-bm25>=0.2.2,<0.3.0',
66
+ 'redis>=4.5.5,<5.0.0',
67
+ 'requests-oauthlib>=1.3.1,<2.0.0',
68
+ 'requests>=2.31.0,<3.0.0',
69
+ 'rich>=13.3.4,<14.0.0',
70
+ 'ruff>=0.0.270,<0.0.271',
71
+ 'sqlalchemy>=2.0.19,<3.0.0',
72
+ 'thefuzz>=0.20.0,<0.21.0',
73
+ 'tiktoken>=0.4.0,<0.5.0',
74
+ 'trafilatura>=1.5.0,<2.0.0',
75
+ 'typer>=0.7.0,<0.8.0',
76
+ 'types-redis>=4.5.5.2,<5.0.0.0',
77
+ 'types-requests>=2.31.0.1,<3.0.0.0',
78
+ 'unstructured[docx,pdf,pptx]>=0.10.16,<0.11.0',
79
+ 'wget>=3.2,<4.0']
80
+
81
+ extras_require = \
82
+ {'hf-embeddings': ['sentence-transformers==2.2.2', 'torch==2.0.0'],
83
+ 'mysql': ['pymysql>=1.1.0,<2.0.0', 'pytest-mysql>=2.4.2,<3.0.0'],
84
+ 'postgres': ['psycopg2>=2.9.7,<3.0.0', 'pytest-postgresql>=5.0.0,<6.0.0']}
85
+
86
+ setup_kwargs = {
87
+ 'name': 'langroid',
88
+ 'version': '0.1.76',
89
+ 'description': 'Harness LLMs with Multi-Agent Programming',
90
+ 'long_description': '<div align="center">\n <img src="docs/assets/langroid-card-lambda-ossem-rust-1200-630.png" alt="Logo" \n width="400" align="center">\n</div>\n\n<div align="center">\n\n[![PyPI - Version](https://img.shields.io/pypi/v/langroid)](https://pypi.org/project/langroid/)\n[![Pytest](https://github.com/langroid/langroid/actions/workflows/pytest.yml/badge.svg)](https://github.com/langroid/langroid/actions/workflows/pytest.yml)\n[![codecov](https://codecov.io/gh/langroid/langroid/branch/main/graph/badge.svg?token=H94BX5F0TE)](https://codecov.io/gh/langroid/langroid)\n[![Lint](https://github.com/langroid/langroid/actions/workflows/validate.yml/badge.svg)](https://github.com/langroid/langroid/actions/workflows/validate.yml)\n[![Docs](https://github.com/langroid/langroid/actions/workflows/mkdocs-deploy.yml/badge.svg)](https://github.com/langroid/langroid/actions/workflows/mkdocs-deploy.yml)\n\n[![Static Badge](https://img.shields.io/badge/Documentation-blue?link=https%3A%2F%2Flangroid.github.io%2Flangroid%2F&link=https%3A%2F%2Flangroid.github.io%2Flangroid%2F)](https://langroid.github.io/langroid)\n[![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/ZU36McDgDs)\n[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langroid/langroid/blob/main/examples/langroid_quick_examples.ipynb)\n\n[![Docker Pulls](https://img.shields.io/docker/pulls/langroid/langroid.svg)](https://hub.docker.com/r/langroid/langroid)\n![Docker Image Size (tag)](https://img.shields.io/docker/image-size/langroid/langroid/latest)\n[![Multi-Architecture DockerHub](https://github.com/langroid/langroid/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/langroid/langroid/actions/workflows/docker-publish.yml)\n\n[![Substack](https://img.shields.io/badge/Substack-%23006f5c.svg?style=for-the-badge&logo=substack&logoColor=FF6719)](https://langroid.substack.com/p/langroid-harness-llms-with-multi-agent-programming)\n\n[![Share on Hacker News](https://img.shields.io/badge/-Share%20on%20Hacker%20News-orange)](https://news.ycombinator.com/submitlink?u=https%3A%2F%2Fgithub.com%2Flangroid%2Flangroid&t=Harness%20LLMs%20with%20Multi-Agent%20Programming)\n[![Share on Reddit](https://img.shields.io/badge/Reddit-FF4500?style=for-the-badge&logo=reddit&logoColor=white)](https://www.reddit.com/submit?url=https%3A%2F%2Fgithub.com%2Flangroid%2Flangroid&title=Harness%20LLMs%20with%20Multi-Agent%20Programming)\n[![Share on Twitter](https://img.shields.io/twitter/url?style=social&url=https://github.com/langroid/langroid)](https://twitter.com/intent/tweet?text=Langroid%20is%20a%20powerful,%20elegant%20new%20framework%20to%20easily%20build%20%23LLM%20applications.%20You%20set%20up%20LLM-powered%20Agents%20with%20vector-stores,%20assign%20tasks,%20and%20have%20them%20collaboratively%20solve%20problems%20via%20message-transformations.%20https://github.com/langroid/langroid)\n[![LinkedIn](https://img.shields.io/badge/linkedin-%230077B5.svg?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/shareArticle?mini=true&url=https://github.com/langroid/langroid&title=Langroid:%20A%20Powerful,%20Elegant%20Framework&summary=Langroid%20is%20a%20powerful,%20elegant%20new%20framework%20to%20easily%20build%20%23LLM%20applications.%20You%20set%20up%20LLM-powered%20Agents%20with%20vector-stores,%20assign%20tasks,%20and%20have%20them%20collaboratively%20solve%20problems%20via%20message-transformations.)\n\n\n</div>\n\n<h3 align="center">\n <a target="_blank" \n href="https://langroid.github.io/langroid/" rel="dofollow">\n <strong>Documentation</strong></a>\n &middot;\n <a target="_blank" href="https://github.com/langroid/langroid-examples" rel="dofollow">\n <strong>Examples Repo</strong></a>\n &middot;\n <a target="_blank" href="https://discord.gg/ZU36McDgDs" rel="dofollow">\n <strong>Discord</strong></a>\n &middot;\n <a target="_blank" href="./CONTRIBUTING.md" rel="dofollow">\n <strong>Contributing</strong></a>\n\n <br />\n</h3>\n\n`Langroid` is an intuitive, lightweight, extensible and principled\nPython framework to easily build LLM-powered applications. \nYou set up Agents, equip them with optional components (LLM, \nvector-store and methods), assign them tasks, and have them \ncollaboratively solve a problem by exchanging messages. \nThis Multi-Agent paradigm is inspired by the\n[Actor Framework](https://en.wikipedia.org/wiki/Actor_model)\n(but you do not need to know anything about this!). \n\n`Langroid` is a fresh take on LLM app-development, where considerable thought has gone \ninto simplifying the developer experience; it does not use `Langchain`.\n\nWe welcome contributions -- See the [contributions](./CONTRIBUTING.md) document\nfor ideas on what to contribute.\n\n**Questions, Feedback, Ideas? Join us on [Discord](https://discord.gg/ZU36McDgDs)!**\n\n<details>\n<summary> <b>:fire: Updates/Releases</b></summary>\n\n- **Sep 2023:**\n - **0.1.72:** Many improvements to DocChatAgent: better embedding model, \n hybrid search to improve retrieval, better pdf parsing, re-ranking retrieved results with cross-encoders. \n - **Use with local LLama Models:** see tutorial [here](https://langroid.github.io/langroid/blog/2023/09/14/using-langroid-with-local-llms/)\n - **Langroid Blog/Newsletter Launched!**: First post is [here](https://substack.com/notes/post/p-136704592) -- Please subscribe to stay updated. \n - **0.1.56:** Support Azure OpenAI. \n - **0.1.55:** Improved [`SQLChatAgent`](https://github.com/langroid/langroid/blob/main/langroid/agent/special/sql/sql_chat_agent.py) that efficiently retrieves relevant schema info when translating natural language to SQL. \n- **Aug 2023:**\n - **[Hierarchical computation](https://langroid.github.io/langroid/examples/agent-tree/)** example using Langroid agents and task orchestration.\n - **0.1.51:** Support for global state, see [test_global_state.py](tests/main/test_global_state.py).\n - **:whale: Langroid Docker image**, available, see instructions below.\n - [**RecipientTool**](langroid/agent/tools/recipient_tool.py) enables (+ enforces) LLM to \nspecify an intended recipient when talking to 2 or more agents. \nSee [this test](tests/main/test_recipient_tool.py) for example usage.\n - **Example:** [Answer questions](examples/docqa/chat-search.py) using Google Search + vecdb-retrieval from URL contents. \n - **0.1.39:** [`GoogleSearchTool`](langroid/agent/tools/google_search_tool.py) to enable Agents (their LLM) to do Google searches via function-calling/tools.\n See [this chat example](examples/basic/chat-search.py) for how easy it is to add this tool to an agent.\n - **Colab notebook** to try the quick-start examples: [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langroid/langroid/blob/main/examples/langroid_quick_examples.ipynb) \n - **0.1.37:** Added [`SQLChatAgent`](langroid/agent/special/sql_chat_agent.py) -- thanks to our latest contributor [Rithwik Babu](https://github.com/rithwikbabu)!\n - Multi-agent Example: [Autocorrect chat](examples/basic/autocorrect.py)\n- **July 2023:** \n - **0.1.30:** Added [`TableChatAgent`](langroid/agent/special/table_chat_agent.py) to \n [chat](examples/data-qa/table_chat.py) with tabular datasets (dataframes, files, URLs): LLM generates Pandas code,\n and code is executed using Langroid\'s tool/function-call mechanism. \n - **Demo:** 3-agent system for Audience [Targeting](https://langroid.github.io/langroid/demos/targeting/audience-targeting/).\n - **0.1.27**: Added [support](langroid/cachedb/momento_cachedb.py) \n for [Momento Serverless Cache](https://www.gomomento.com/) as an alternative to Redis.\n - **0.1.24**: [`DocChatAgent`](langroid/agent/special/doc_chat_agent.py) \n now [accepts](langroid/parsing/document_parser.py) PDF files or URLs.\n\n</details>\n\n# :rocket: Demo\nSuppose you want to extract structured information about the key terms \nof a commercial lease document. You can easily do this with Langroid using a two-agent system,\nas we show in the [langroid-examples](https://github.com/langroid/langroid-examples/blob/main/examples/docqa/chat_multi_extract.py) repo.\nThe demo showcases just a few of the many features of Langroid, such as:\n- Multi-agent collaboration: `LeaseExtractor` is in charge of the task, and its LLM (GPT4) generates questions \nto be answered by the `DocAgent`.\n- Retrieval augmented question-answering, with **source-citation**: `DocAgent` LLM (GPT4) uses retrieval from a vector-store to \nanswer the `LeaseExtractor`\'s questions, cites the specific excerpt supporting the answer. \n- Function-calling (also known as tool/plugin): When it has all the information it \nneeds, the `LeaseExtractor` LLM presents the information in a structured \nformat using a Function-call. \n\nHere is what it looks like in action:\n\n![Demo](docs/assets/demos/lease-extractor-demo.gif)\n\n\n# :zap: Highlights\n\n- **Agents as first-class citizens:** The [Agent](https://langroid.github.io/langroid/reference/agent/base/#langroid.agent.base.Agent) class encapsulates LLM conversation state,\n and optionally a vector-store and tools. Agents are a core abstraction in Langroid;\n Agents act as _message transformers_, and by default provide 3 _responder_ methods, one corresponding to each entity: LLM, Agent, User.\n- **Tasks:** A [Task](https://langroid.github.io/langroid/reference/agent/task/) class wraps an Agent, and gives the agent instructions (or roles, or goals), \n manages iteration over an Agent\'s responder methods, \n and orchestrates multi-agent interactions via hierarchical, recursive\n task-delegation. The `Task.run()` method has the same \n type-signature as an Agent\'s responder\'s methods, and this is key to how \n a task of an agent can delegate to other sub-tasks: from the point of view of a Task,\n sub-tasks are simply additional responders, to be used in a round-robin fashion \n after the agent\'s own responders.\n- **Modularity, Reusabilily, Loose coupling:** The `Agent` and `Task` abstractions allow users to design\n Agents with specific skills, wrap them in Tasks, and combine tasks in a flexible way.\n- **LLM Support**: Langroid supports OpenAI LLMs including GPT-3.5-Turbo,\n GPT-4.\n- **Caching of LLM responses:** Langroid supports [Redis](https://redis.com/try-free/) and \n [Momento](https://www.gomomento.com/) to cache LLM responses.\n- **Vector-stores**: [Qdrant](https://qdrant.tech/) and [Chroma](https://www.trychroma.com/) are currently supported.\n Vector stores allow for Retrieval-Augmented-Generation (RAG).\n- **Grounding and source-citation:** Access to external documents via vector-stores \n allows for grounding and source-citation.\n- **Observability, Logging, Lineage:** Langroid generates detailed logs of multi-agent interactions and\n maintains provenance/lineage of messages, so that you can trace back\n the origin of a message.\n- **Tools/Plugins/Function-calling**: Langroid supports OpenAI\'s recently\n released [function calling](https://platform.openai.com/docs/guides/gpt/function-calling)\n feature. In addition, Langroid has its own native equivalent, which we\n call **tools** (also known as "plugins" in other contexts). Function\n calling and tools have the same developer-facing interface, implemented\n using [Pydantic](https://docs.pydantic.dev/latest/),\n which makes it very easy to define tools/functions and enable agents\n to use them. Benefits of using Pydantic are that you never have to write\n complex JSON specs for function calling, and when the LLM\n hallucinates malformed JSON, the Pydantic error message is sent back to\n the LLM so it can fix it!\n\n--- \n\n# :gear: Installation and Setup\n\n### Install `langroid`\nLangroid requires Python 3.11+. We recommend using a virtual environment.\nUse `pip` to install `langroid` (from PyPi) to your virtual environment:\n```bash\npip install langroid\n```\nThe core Langroid package lets you use OpenAI Embeddings models via their API. \nIf you instead want to use the `all-MiniLM-L6-v2` embeddings model\nfrom from HuggingFace, install Langroid like this:\n```bash\npip install langroid[hf-embeddings]\n```\nNote that this will install `torch` and `sentence-transformers` libraries.\n\n<details>\n<summary><b>Optional Installs for using SQL Chat with a PostgreSQL DB </b></summary>\n\nIf you are using `SQLChatAgent` \n(e.g. the script [`examples/data-qa/sql-chat/sql_chat.py`](examples/data-qa/sql-chat/sql_chat.py)),\nwith a postgres db, you will need to:\n\n- Install PostgreSQL dev libraries for your platform, e.g.\n - `sudo apt-get install libpq-dev` on Ubuntu,\n - `brew install postgresql` on Mac, etc.\n- Install langroid with the postgres extra, e.g. `pip install langroid[postgres]`\n or `poetry add langroid[postgres]` or `poetry install -E postgres`.\n If this gives you an error, try `pip install psycopg2-binary` in your virtualenv.\n</details>\n\n### Set up environment variables (API keys, etc)\n\nTo get started, all you need is an OpenAI API Key.\nIf you don\'t have one, see [this OpenAI Page](https://help.openai.com/en/collections/3675940-getting-started-with-openai-api).\nCurrently only OpenAI models are supported. Others will be added later\n(Pull Requests welcome!).\n\nIn the root of the repo, copy the `.env-template` file to a new file `.env`: \n```bash\ncp .env-template .env\n```\nThen insert your OpenAI API Key. \nYour `.env` file should look like this:\n```bash\nOPENAI_API_KEY=your-key-here-without-quotes\n````\n\nAlternatively, you can set this as an environment variable in your shell\n(you will need to do this every time you open a new shell):\n```bash\nexport OPENAI_API_KEY=your-key-here-without-quotes\n```\n\n\n<details>\n<summary><b>Optional Setup Instructions (click to expand) </b></summary>\n\nAll of the following environment variable settings are optional, and some are only needed \nto use specific features (as noted below).\n\n- **Qdrant** Vector Store API Key, URL. This is only required if you want to use Qdrant cloud.\n You can sign up for a free 1GB account at [Qdrant cloud](https://cloud.qdrant.io).\n If you skip setting up these, Langroid will use Qdrant in local-storage mode.\n Alternatively [Chroma](https://docs.trychroma.com/) is also currently supported. \n We use the local-storage version of Chroma, so there is no need for an API key.\n Langroid uses Qdrant by default.\n- **Redis** Password, host, port: This is optional, and only needed to cache LLM API responses\n using Redis Cloud. Redis [offers](https://redis.com/try-free/) a free 30MB Redis account\n which is more than sufficient to try out Langroid and even beyond.\n If you don\'t set up these, Langroid will use a pure-python \n Redis in-memory cache via the [Fakeredis](https://fakeredis.readthedocs.io/en/latest/) library.\n- **Momento** Serverless Caching of LLM API responses (as an alternative to Redis). \n To use Momento instead of Redis:\n - enter your Momento Token in the `.env` file, as the value of `MOMENTO_AUTH_TOKEN` (see example file below),\n - in the `.env` file set `CACHE_TYPE=momento` (instead of `CACHE_TYPE=redis` which is the default).\n- **GitHub** Personal Access Token (required for apps that need to analyze git\n repos; token-based API calls are less rate-limited). See this\n [GitHub page](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens).\n- **Google Custom Search API Credentials:** Only needed to enable an Agent to use the `GoogleSearchTool`.\n To use Google Search as an LLM Tool/Plugin/function-call, \n you\'ll need to set up \n [a Google API key](https://developers.google.com/custom-search/v1/introduction#identify_your_application_to_google_with_api_key),\n then [setup a Google Custom Search Engine (CSE) and get the CSE ID](https://developers.google.com/custom-search/docs/tutorial/creatingcse).\n (Documentation for these can be challenging, we suggest asking GPT4 for a step-by-step guide.)\n After obtaining these credentials, store them as values of \n `GOOGLE_API_KEY` and `GOOGLE_CSE_ID` in your `.env` file. \n Full documentation on using this (and other such "stateless" tools) is coming soon, but \n in the meantime take a peek at this [chat example](examples/basic/chat-search.py), which \n shows how you can easily equip an Agent with a `GoogleSearchtool`.\n \n\n\nIf you add all of these optional variables, your `.env` file should look like this:\n```bash\nOPENAI_API_KEY=your-key-here-without-quotes\nGITHUB_ACCESS_TOKEN=your-personal-access-token-no-quotes\nCACHE_TYPE=redis # or momento\nREDIS_PASSWORD=your-redis-password-no-quotes\nREDIS_HOST=your-redis-hostname-no-quotes\nREDIS_PORT=your-redis-port-no-quotes\nMOMENTO_AUTH_TOKEN=your-momento-token-no-quotes # instead of REDIS* variables\nQDRANT_API_KEY=your-key\nQDRANT_API_URL=https://your.url.here:6333 # note port number must be included\nGOOGLE_API_KEY=your-key\nGOOGLE_CSE_ID=your-cse-id\n```\n</details>\n\n<details>\n<summary><b>Optional setup instructions for Microsoft Azure OpenAI(click to expand)</b></summary> \n\nWhen using Azure OpenAI, additional environment variables are required in the \n`.env` file.\nThis page [Microsoft Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line&pivots=programming-language-python#environment-variables)\nprovides more information, and you can set each environment variable as follows:\n\n- `AZURE_API_KEY`, from the value of `API_KEY`\n- `AZURE_OPENAI_API_BASE` from the value of `ENDPOINT`, typically looks like `https://your.domain.azure.com`.\n- For `AZURE_OPENAI_API_VERSION`, you can use the default value in `.env-template`, and latest version can be found [here](https://learn.microsoft.com/en-us/azure/ai-services/openai/whats-new#azure-openai-chat-completion-general-availability-ga)\n- `AZURE_OPENAI_DEPLOYMENT_NAME` is the name of the deployed model, which is defined by the user during the model setup \n- `AZURE_GPT_MODEL_NAME` GPT-3.5-Turbo or GPT-4 model names that you chose when you setup your Azure OpenAI account.\n\n</details>\n\n---\n\n# :whale: Docker Instructions\n\nWe provide a containerized version of the [`langroid-examples`](https://github.com/langroid/langroid-examples) \nrepository via this [Docker Image](https://hub.docker.com/r/langroid/langroid).\nAll you need to do is set up environment variables in the `.env` file.\nPlease follow these steps to setup the container:\n\n```bash\n# get the .env file template from `langroid` repo\nwget https://github.com/langroid/langroid/blob/main/.env-template .env\n\n# Edit the .env file with your favorite editor (here nano), \n# and add API keys as explained above\nnano .env\n\n# launch the container\ndocker run -it -v ./.env:/.env langroid/langroid\n\n# Use this command to run any of the scripts in the `examples` directory\npython examples/<Path/To/Example.py> \n``` \n\n\n\n# :tada: Usage Examples\n\nThese are quick teasers to give a glimpse of what you can do with Langroid\nand how your code would look. \n\n:warning: The code snippets below are intended to give a flavor of the code\nand they are **not** complete runnable examples! For that we encourage you to \nconsult the [`langroid-examples`](https://github.com/langroid/langroid-examples) \nrepository.\n\n:information_source: The various LLM prompts and instructions in Langroid\nhave been tested to work well with GPT4.\nSwitching to GPT3.5-Turbo is easy via a config flag\n(e.g., `cfg = OpenAIGPTConfig(chat_model=OpenAIChatModel.GPT3_5_TURBO)`),\nand may suffice for some applications, but in general you may see inferior results.\n\n\n:book: Also see the\n[`Getting Started Guide`](https://langroid.github.io/langroid/quick-start/)\nfor a detailed tutorial.\n\n\n\nClick to expand any of the code examples below.\nAll of these can be run in a Colab notebook:\n[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langroid/langroid/blob/main/examples/langroid_quick_examples.ipynb)\n\n<details>\n<summary> <b> Direct interaction with OpenAI LLM </b> </summary>\n\n```python\nfrom langroid.language_models.openai_gpt import ( \n OpenAIGPTConfig, OpenAIChatModel, OpenAIGPT,\n)\nfrom langroid.language_models.base import LLMMessage, Role\n\ncfg = OpenAIGPTConfig(chat_model=OpenAIChatModel.GPT4)\n\nmdl = OpenAIGPT(cfg)\n\nmessages = [\n LLMMessage(content="You are a helpful assistant", role=Role.SYSTEM), \n LLMMessage(content="What is the capital of Ontario?", role=Role.USER),\n]\nresponse = mdl.chat(messages, max_tokens=200)\nprint(response.message)\n```\n</details>\n\n<details>\n<summary> <b> Define an agent, set up a task, and run it </b> </summary>\n\n```python\nfrom langroid.agent.chat_agent import ChatAgent, ChatAgentConfig\nfrom langroid.agent.task import Task\nfrom langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig\n\nconfig = ChatAgentConfig(\n llm = OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n vecdb=None, # no vector store\n)\nagent = ChatAgent(config)\n# get response from agent\'s LLM, and put this in an interactive loop...\n# answer = agent.llm_response("What is the capital of Ontario?")\n # ... OR instead, set up a task (which has a built-in loop) and run it\ntask = Task(agent, name="Bot") \ntask.run() # ... a loop seeking response from LLM or User at each turn\n```\n</details>\n\n<details>\n<summary><b> Three communicating agents </b></summary>\n\nA toy numbers game, where when given a number `n`:\n- `repeater_agent`\'s LLM simply returns `n`,\n- `even_agent`\'s LLM returns `n/2` if `n` is even, else says "DO-NOT-KNOW"\n- `odd_agent`\'s LLM returns `3*n+1` if `n` is odd, else says "DO-NOT-KNOW"\n\nFirst define the 3 agents, and set up their tasks with instructions:\n\n```python\nfrom langroid.utils.constants import NO_ANSWER\nfrom langroid.agent.chat_agent import ChatAgent, ChatAgentConfig\nfrom langroid.agent.task import Task\nfrom langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig\nconfig = ChatAgentConfig(\n llm = OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n vecdb = None,\n)\nrepeater_agent = ChatAgent(config)\nrepeater_task = Task(\n repeater_agent,\n name = "Repeater",\n system_message="""\n Your job is to repeat whatever number you receive.\n """,\n llm_delegate=True, # LLM takes charge of task\n single_round=False, \n)\neven_agent = ChatAgent(config)\neven_task = Task(\n even_agent,\n name = "EvenHandler",\n system_message=f"""\n You will be given a number. \n If it is even, divide by 2 and say the result, nothing else.\n If it is odd, say {NO_ANSWER}\n """,\n single_round=True, # task done after 1 step() with valid response\n)\n\nodd_agent = ChatAgent(config)\nodd_task = Task(\n odd_agent,\n name = "OddHandler",\n system_message=f"""\n You will be given a number n. \n If it is odd, return (n*3+1), say nothing else. \n If it is even, say {NO_ANSWER}\n """,\n single_round=True, # task done after 1 step() with valid response\n)\n```\nThen add the `even_task` and `odd_task` as sub-tasks of `repeater_task`, \nand run the `repeater_task`, kicking it off with a number as input:\n```python\nrepeater_task.add_sub_task([even_task, odd_task])\nrepeater_task.run("3")\n```\n\n</details>\n\n<details>\n<summary><b> Simple Tool/Function-calling example </b></summary>\n\nLangroid leverages Pydantic to support OpenAI\'s\n[Function-calling API](https://platform.openai.com/docs/guides/gpt/function-calling)\nas well as its own native tools. The benefits are that you don\'t have to write\nany JSON to specify the schema, and also if the LLM hallucinates a malformed\ntool syntax, Langroid sends the Pydantic validation error (suitiably sanitized) \nto the LLM so it can fix it!\n\nSimple example: Say the agent has a secret list of numbers, \nand we want the LLM to find the smallest number in the list. \nWe want to give the LLM a `probe` tool/function which takes a\nsingle number `n` as argument. The tool handler method in the agent\nreturns how many numbers in its list are at most `n`.\n\nFirst define the tool using Langroid\'s `ToolMessage` class:\n\n\n```python\nfrom langroid.agent.tool_message import ToolMessage\nclass ProbeTool(ToolMessage):\n request: str = "probe" # specifies which agent method handles this tool\n purpose: str = """\n To find how many numbers in my list are less than or equal to \n the <number> you specify.\n """ # description used to instruct the LLM on when/how to use the tool\n number: int # required argument to the tool\n```\n\nThen define a `SpyGameAgent` as a subclass of `ChatAgent`, \nwith a method `probe` that handles this tool:\n\n```python\nfrom langroid.agent.chat_agent import ChatAgent, ChatAgentConfig\nclass SpyGameAgent(ChatAgent):\n def __init__(self, config: ChatAgentConfig):\n super().__init__(config)\n self.numbers = [3, 4, 8, 11, 15, 25, 40, 80, 90]\n\n def probe(self, msg: ProbeTool) -> str:\n # return how many numbers in self.numbers are less or equal to msg.number\n return str(len([n for n in self.numbers if n <= msg.number]))\n```\n\nWe then instantiate the agent and enable it to use and respond to the tool:\n\n```python\nfrom langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig\nspy_game_agent = SpyGameAgent(\n ChatAgentConfig(\n name="Spy",\n llm = OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n vecdb=None,\n use_tools=False, # don\'t use Langroid native tool\n use_functions_api=True, # use OpenAI function-call API\n )\n)\nspy_game_agent.enable_message(ProbeTool)\n```\n\nFor a full working example see the\n[chat-agent-tool.py](https://github.com/langroid/langroid-examples/blob/main/examples/quick-start/chat-agent-tool.py)\nscript in the `langroid-examples` repo.\n</details>\n\n<details>\n<summary> <b>Tool/Function-calling to extract structured information from text </b> </summary>\n\nSuppose you want an agent to extract \nthe key terms of a lease, from a lease document, as a nested JSON structure.\nFirst define the desired structure via Pydantic models:\n\n```python\nfrom pydantic import BaseModel\nclass LeasePeriod(BaseModel):\n start_date: str\n end_date: str\n\n\nclass LeaseFinancials(BaseModel):\n monthly_rent: str\n deposit: str\n\nclass Lease(BaseModel):\n period: LeasePeriod\n financials: LeaseFinancials\n address: str\n```\n\nThen define the `LeaseMessage` tool as a subclass of Langroid\'s `ToolMessage`.\nNote the tool has a required argument `terms` of type `Lease`:\n\n```python\nclass LeaseMessage(ToolMessage):\n request: str = "lease_info"\n purpose: str = """\n Collect information about a Commercial Lease.\n """\n terms: Lease\n```\n\nThen define a `LeaseExtractorAgent` with a method `lease_info` that handles this tool,\ninstantiate the agent, and enable it to use and respond to this tool:\n\n```python\nclass LeaseExtractorAgent(ChatAgent):\n def lease_info(self, message: LeaseMessage) -> str:\n print(\n f"""\n DONE! Successfully extracted Lease Info:\n {message.terms}\n """\n )\n return json.dumps(message.terms.dict())\n \nlease_extractor_agent = LeaseExtractorAgent(\n ChatAgentConfig(\n llm=OpenAIGPTConfig(),\n use_functions_api=False,\n use_tools=True,\n )\n)\nlease_extractor_agent.enable_message(LeaseMessage)\n```\n\nSee the [`chat_multi_extract.py`](https://github.com/langroid/langroid-examples/blob/main/examples/docqa/chat_multi_extract.py)\nscript in the `langroid-examples` repo for a full working example.\n</details>\n\n<details>\n<summary><b> Chat with documents (file paths, URLs, etc) </b></summary>\n\nLangroid provides a specialized agent class `DocChatAgent` for this purpose.\nIt incorporates document sharding, embedding, storage in a vector-DB, \nand retrieval-augmented query-answer generation.\nUsing this class to chat with a collection of documents is easy.\nFirst create a `DocChatAgentConfig` instance, with a \n`doc_paths` field that specifies the documents to chat with.\n\n```python\nfrom langroid.agent.doc_chat_agent import DocChatAgentConfig\nfrom langroid.vector_store.qdrantdb import QdrantDBConfig\nconfig = DocChatAgentConfig(\n doc_paths = [\n "https://en.wikipedia.org/wiki/Language_model",\n "https://en.wikipedia.org/wiki/N-gram_language_model",\n "/path/to/my/notes-on-language-models.txt",\n ]\n llm = OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n vecdb=QdrantDBConfig()\n)\n```\n\nThen instantiate the `DocChatAgent` (this ingests the docs into the vector-store):\n\n```python\nagent = DocChatAgent(config)\n```\nThen we can either ask the agent one-off questions,\n```python\nagent.chat("What is a language model?")\n```\nor wrap it in a `Task` and run an interactive loop with the user:\n```python\nfrom langroid.task import Task\ntask = Task(agent)\ntask.run()\n```\n\nSee full working scripts in the \n[`docqa`](https://github.com/langroid/langroid-examples/tree/main/examples/docqa)\nfolder of the `langroid-examples` repo.\n</details>\n\n<details>\n<summary><b> :fire: Chat with tabular data (file paths, URLs, dataframes) </b></summary>\n\nUsing Langroid you can set up a `TableChatAgent` with a dataset (file path, URL or dataframe),\nand query it. The Agent\'s LLM generates Pandas code to answer the query, \nvia function-calling (or tool/plugin), and the Agent\'s function-handling method\nexecutes the code and returns the answer.\n\nHere is how you can do this:\n\n```python\nfrom langroid.agent.special.table_chat_agent import TableChatAgent, TableChatAgentConfig\nfrom langroid.agent.task import Task\nfrom langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig\n```\n\nSet up a `TableChatAgent` for a data file, URL or dataframe\n(Ensure the data table has a header row; the delimiter/separator is auto-detected):\n```python\ndataset = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"\n# or dataset = "/path/to/my/data.csv"\n# or dataset = pd.read_csv("/path/to/my/data.csv")\nagent = TableChatAgent(\n config=TableChatAgentConfig(\n data=dataset, \n llm=OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n )\n)\n```\nSet up a task, and ask one-off questions like this: \n\n```python\ntask = Task(\n agent, \n name = "DataAssistant",\n default_human_response="", # to avoid waiting for user input\n)\nresult = task.run(\n "What is the average alcohol content of wines with a quality rating above 7?",\n turns=2 # return after user question, LLM fun-call/tool response, Agent code-exec result\n) \nprint(result.content)\n```\nOr alternatively, set up a task and run it in an interactive loop with the user:\n\n```python\ntask = Task(agent, name="DataAssistant")\ntask.run()\n``` \n\nFor a full working example see the \n[`table_chat.py`](https://github.com/langroid/langroid-examples/tree/main/examples/data-qa/table_chat.py)\nscript in the `langroid-examples` repo.\n\n\n</details>\n\n---\n\n# :heart: Thank you to our [supporters](https://github.com/langroid/langroid/stargazers)\n\nIf you like this project, please give it a star ⭐ and 📢 spread the word in your network or social media:\n\n[![Share on Twitter](https://img.shields.io/twitter/url?style=social&url=https://github.com/langroid/langroid)](https://twitter.com/intent/tweet?text=Langroid%20is%20a%20powerful,%20elegant%20new%20framework%20to%20easily%20build%20%23LLM%20applications.%20You%20set%20up%20LLM-powered%20Agents%20with%20vector-stores,%20assign%20tasks,%20and%20have%20them%20collaboratively%20solve%20problems%20via%20message-transformations.%20https://github.com/langroid/langroid)\n[![Share on LinkedIn](https://img.shields.io/badge/Share%20on-LinkedIn-blue)](https://www.linkedin.com/shareArticle?mini=true&url=https://github.com/langroid/langroid&title=Langroid:%20A%20Powerful,%20Elegant%20Framework&summary=Langroid%20is%20a%20powerful,%20elegant%20new%20framework%20to%20easily%20build%20%23LLM%20applications.%20You%20set%20up%20LLM-powered%20Agents%20with%20vector-stores,%20assign%20tasks,%20and%20have%20them%20collaboratively%20solve%20problems%20via%20message-transformations.)\n[![Share on Hacker News](https://img.shields.io/badge/-Share%20on%20Hacker%20News-orange)](https://news.ycombinator.com/submitlink?u=https%3A%2F%2Fgithub.com%2Flangroid%2Flangroid&t=Harness%20LLMs%20with%20Multi-Agent%20Programming)\n[![Share on Reddit](https://img.shields.io/badge/-Share%20on%20Reddit-blue)](https://www.reddit.com/submit?url=https%3A%2F%2Fgithub.com%2Flangroid%2Flangroid&title=Harness%20LLMs%20with%20Multi-Agent%20Programming)\n\n\n\n\nYour support will help build Langroid\'s momentum and community.\n\n\n\n\n# Langroid Co-Founders\n\n- [Prasad Chalasani](https://www.linkedin.com/in/pchalasani/) (IIT BTech/CS, CMU PhD/ML; Independent ML Consultant)\n- [Somesh Jha](https://www.linkedin.com/in/somesh-jha-80208015/) (IIT BTech/CS, CMU PhD/CS; Professor of CS, U Wisc at Madison)\n\n\n\n',
91
+ 'author': 'Prasad Chalasani',
92
+ 'author_email': 'pchalasani@gmail.com',
93
+ 'maintainer': 'None',
94
+ 'maintainer_email': 'None',
95
+ 'url': 'None',
96
+ 'packages': packages,
97
+ 'package_data': package_data,
98
+ 'install_requires': install_requires,
99
+ 'extras_require': extras_require,
100
+ 'python_requires': '>=3.8.1,<3.12',
101
+ }
102
+
103
+
104
+ setup(**setup_kwargs)
langroid-0.1.73/setup.py DELETED
@@ -1,103 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- from setuptools import setup
3
-
4
- packages = \
5
- ['langroid',
6
- 'langroid.agent',
7
- 'langroid.agent.special',
8
- 'langroid.agent.special.sql',
9
- 'langroid.agent.special.sql.utils',
10
- 'langroid.agent.tools',
11
- 'langroid.cachedb',
12
- 'langroid.embedding_models',
13
- 'langroid.language_models',
14
- 'langroid.language_models.prompt_formatter',
15
- 'langroid.parsing',
16
- 'langroid.prompts',
17
- 'langroid.scripts',
18
- 'langroid.utils',
19
- 'langroid.utils.llms',
20
- 'langroid.utils.output',
21
- 'langroid.utils.web',
22
- 'langroid.vector_store']
23
-
24
- package_data = \
25
- {'': ['*']}
26
-
27
- install_requires = \
28
- ['autopep8>=2.0.2,<3.0.0',
29
- 'black[jupyter]>=23.3.0,<24.0.0',
30
- 'bs4>=0.0.1,<0.0.2',
31
- 'chromadb>=0.3.21,<0.4.0',
32
- 'colorlog>=6.7.0,<7.0.0',
33
- 'faker>=18.9.0,<19.0.0',
34
- 'fakeredis>=2.12.1,<3.0.0',
35
- 'fire>=0.5.0,<0.6.0',
36
- 'flake8>=6.0.0,<7.0.0',
37
- 'google-api-python-client>=2.95.0,<3.0.0',
38
- 'halo>=0.0.31,<0.0.32',
39
- 'lxml>=4.9.3,<5.0.0',
40
- 'mkdocs-awesome-pages-plugin>=2.8.0,<3.0.0',
41
- 'mkdocs-gen-files>=0.4.0,<0.5.0',
42
- 'mkdocs-jupyter>=0.24.1,<0.25.0',
43
- 'mkdocs-literate-nav>=0.6.0,<0.7.0',
44
- 'mkdocs-material>=9.1.5,<10.0.0',
45
- 'mkdocs-rss-plugin>=1.8.0,<2.0.0',
46
- 'mkdocs-section-index>=0.3.5,<0.4.0',
47
- 'mkdocs>=1.4.2,<2.0.0',
48
- 'mkdocstrings[python]>=0.21.2,<0.22.0',
49
- 'momento>=1.7.0,<2.0.0',
50
- 'mypy>=1.2.0,<2.0.0',
51
- 'nltk>=3.8.1,<4.0.0',
52
- 'openai>=0.27.5,<0.28.0',
53
- 'pandas>=2.0.3,<3.0.0',
54
- 'pdfplumber>=0.10.2,<0.11.0',
55
- 'pre-commit>=3.3.2,<4.0.0',
56
- 'prettytable>=3.8.0,<4.0.0',
57
- 'pydantic==1.10.11',
58
- 'pygithub>=1.58.1,<2.0.0',
59
- 'pygments>=2.15.1,<3.0.0',
60
- 'pymupdf>=1.23.3,<2.0.0',
61
- 'pyparsing>=3.0.9,<4.0.0',
62
- 'pypdf>=3.12.2,<4.0.0',
63
- 'python-dotenv>=1.0.0,<2.0.0',
64
- 'qdrant-client>=1.3.1,<2.0.0',
65
- 'rank-bm25>=0.2.2,<0.3.0',
66
- 'redis>=4.5.5,<5.0.0',
67
- 'requests-oauthlib>=1.3.1,<2.0.0',
68
- 'requests>=2.31.0,<3.0.0',
69
- 'rich>=13.3.4,<14.0.0',
70
- 'ruff>=0.0.270,<0.0.271',
71
- 'sqlalchemy>=2.0.19,<3.0.0',
72
- 'thefuzz>=0.20.0,<0.21.0',
73
- 'tiktoken>=0.4.0,<0.5.0',
74
- 'trafilatura>=1.5.0,<2.0.0',
75
- 'typer>=0.7.0,<0.8.0',
76
- 'types-redis>=4.5.5.2,<5.0.0.0',
77
- 'types-requests>=2.31.0.1,<3.0.0.0',
78
- 'wget>=3.2,<4.0']
79
-
80
- extras_require = \
81
- {'hf-embeddings': ['sentence-transformers==2.2.2', 'torch==2.0.0'],
82
- 'mysql': ['pymysql>=1.1.0,<2.0.0', 'pytest-mysql>=2.4.2,<3.0.0'],
83
- 'postgres': ['psycopg2>=2.9.7,<3.0.0', 'pytest-postgresql>=5.0.0,<6.0.0']}
84
-
85
- setup_kwargs = {
86
- 'name': 'langroid',
87
- 'version': '0.1.73',
88
- 'description': 'Harness LLMs with Multi-Agent Programming',
89
- 'long_description': '<div align="center">\n <img src="docs/assets/langroid-card-lambda-ossem-rust-1200-630.png" alt="Logo" \n width="400" align="center">\n</div>\n\n<div align="center">\n\n[![PyPI - Version](https://img.shields.io/pypi/v/langroid)](https://pypi.org/project/langroid/)\n[![Pytest](https://github.com/langroid/langroid/actions/workflows/pytest.yml/badge.svg)](https://github.com/langroid/langroid/actions/workflows/pytest.yml)\n[![codecov](https://codecov.io/gh/langroid/langroid/branch/main/graph/badge.svg?token=H94BX5F0TE)](https://codecov.io/gh/langroid/langroid)\n[![Lint](https://github.com/langroid/langroid/actions/workflows/validate.yml/badge.svg)](https://github.com/langroid/langroid/actions/workflows/validate.yml)\n[![Docs](https://github.com/langroid/langroid/actions/workflows/mkdocs-deploy.yml/badge.svg)](https://github.com/langroid/langroid/actions/workflows/mkdocs-deploy.yml)\n\n[![Static Badge](https://img.shields.io/badge/Documentation-blue?link=https%3A%2F%2Flangroid.github.io%2Flangroid%2F&link=https%3A%2F%2Flangroid.github.io%2Flangroid%2F)](https://langroid.github.io/langroid)\n[![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white)](https://discord.gg/ZU36McDgDs)\n[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langroid/langroid/blob/main/examples/langroid_quick_examples.ipynb)\n\n[![Docker Pulls](https://img.shields.io/docker/pulls/langroid/langroid.svg)](https://hub.docker.com/r/langroid/langroid)\n![Docker Image Size (tag)](https://img.shields.io/docker/image-size/langroid/langroid/latest)\n[![Multi-Architecture DockerHub](https://github.com/langroid/langroid/actions/workflows/docker-publish.yml/badge.svg)](https://github.com/langroid/langroid/actions/workflows/docker-publish.yml)\n\n[![Substack](https://img.shields.io/badge/Substack-%23006f5c.svg?style=for-the-badge&logo=substack&logoColor=FF6719)](https://langroid.substack.com/p/langroid-harness-llms-with-multi-agent-programming)\n\n[![Share on Hacker News](https://img.shields.io/badge/-Share%20on%20Hacker%20News-orange)](https://news.ycombinator.com/submitlink?u=https%3A%2F%2Fgithub.com%2Flangroid%2Flangroid&t=Harness%20LLMs%20with%20Multi-Agent%20Programming)\n[![Share on Reddit](https://img.shields.io/badge/Reddit-FF4500?style=for-the-badge&logo=reddit&logoColor=white)](https://www.reddit.com/submit?url=https%3A%2F%2Fgithub.com%2Flangroid%2Flangroid&title=Harness%20LLMs%20with%20Multi-Agent%20Programming)\n[![Share on Twitter](https://img.shields.io/twitter/url?style=social&url=https://github.com/langroid/langroid)](https://twitter.com/intent/tweet?text=Langroid%20is%20a%20powerful,%20elegant%20new%20framework%20to%20easily%20build%20%23LLM%20applications.%20You%20set%20up%20LLM-powered%20Agents%20with%20vector-stores,%20assign%20tasks,%20and%20have%20them%20collaboratively%20solve%20problems%20via%20message-transformations.%20https://github.com/langroid/langroid)\n[![LinkedIn](https://img.shields.io/badge/linkedin-%230077B5.svg?style=for-the-badge&logo=linkedin&logoColor=white)](https://www.linkedin.com/shareArticle?mini=true&url=https://github.com/langroid/langroid&title=Langroid:%20A%20Powerful,%20Elegant%20Framework&summary=Langroid%20is%20a%20powerful,%20elegant%20new%20framework%20to%20easily%20build%20%23LLM%20applications.%20You%20set%20up%20LLM-powered%20Agents%20with%20vector-stores,%20assign%20tasks,%20and%20have%20them%20collaboratively%20solve%20problems%20via%20message-transformations.)\n\n\n</div>\n\n<h3 align="center">\n <a target="_blank" \n href="https://langroid.github.io/langroid/" rel="dofollow">\n <strong>Documentation</strong></a>\n &middot;\n <a target="_blank" href="https://github.com/langroid/langroid-examples" rel="dofollow">\n <strong>Examples Repo</strong></a>\n &middot;\n <a target="_blank" href="https://discord.gg/ZU36McDgDs" rel="dofollow">\n <strong>Discord</strong></a>\n &middot;\n <a target="_blank" href="./CONTRIBUTING.md" rel="dofollow">\n <strong>Contributing</strong></a>\n\n <br />\n</h3>\n\n`Langroid` is an intuitive, lightweight, extensible and principled\nPython framework to easily build LLM-powered applications. \nYou set up Agents, equip them with optional components (LLM, \nvector-store and methods), assign them tasks, and have them \ncollaboratively solve a problem by exchanging messages. \nThis Multi-Agent paradigm is inspired by the\n[Actor Framework](https://en.wikipedia.org/wiki/Actor_model)\n(but you do not need to know anything about this!). \n\nLangroid is a fresh take on LLM app-development, where considerable thought has gone \ninto simplifying the developer experience. It does not use `Langchain` or `Llama-Index`.\n\nWe welcome contributions -- See the [contributions](./CONTRIBUTING.md) document\nfor ideas on what to contribute.\n\n**Questions, Feedback, Ideas? Join us on [Discord](https://discord.gg/ZU36McDgDs)!**\n\n<details>\n<summary> <b>:fire: Updates/Releases</b></summary>\n\n- **Sep 2023:**\n - **0.1.72:** Many improvements to DocChatAgent: better embedding model, \n hybrid search to improve retrieval, better pdf parsing, re-ranking retrieved results with cross-encoders. \n - **Use with local LLama Models:** see tutorial [here](https://langroid.github.io/langroid/blog/2023/09/14/using-langroid-with-local-llms/)\n - **Langroid Blog/Newsletter Launched!**: First post is [here](https://substack.com/notes/post/p-136704592) -- Please subscribe to stay updated. \n - **0.1.56:** Support Azure OpenAI. \n - **0.1.55:** Improved [`SQLChatAgent`](https://github.com/langroid/langroid/blob/main/langroid/agent/special/sql/sql_chat_agent.py) that efficiently retrieves relevant schema info when translating natural language to SQL. \n- **Aug 2023:**\n - **[Hierarchical computation](https://langroid.github.io/langroid/examples/agent-tree/)** example using Langroid agents and task orchestration.\n - **0.1.51:** Support for global state, see [test_global_state.py](tests/main/test_global_state.py).\n - **:whale: Langroid Docker image**, available, see instructions below.\n - [**RecipientTool**](langroid/agent/tools/recipient_tool.py) enables (+ enforces) LLM to \nspecify an intended recipient when talking to 2 or more agents. \nSee [this test](tests/main/test_recipient_tool.py) for example usage.\n - **Example:** [Answer questions](examples/docqa/chat-search.py) using Google Search + vecdb-retrieval from URL contents. \n - **0.1.39:** [`GoogleSearchTool`](langroid/agent/tools/google_search_tool.py) to enable Agents (their LLM) to do Google searches via function-calling/tools.\n See [this chat example](examples/basic/chat-search.py) for how easy it is to add this tool to an agent.\n - **Colab notebook** to try the quick-start examples: [![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langroid/langroid/blob/main/examples/langroid_quick_examples.ipynb) \n - **0.1.37:** Added [`SQLChatAgent`](langroid/agent/special/sql_chat_agent.py) -- thanks to our latest contributor [Rithwik Babu](https://github.com/rithwikbabu)!\n - Multi-agent Example: [Autocorrect chat](examples/basic/autocorrect.py)\n- **July 2023:** \n - **0.1.30:** Added [`TableChatAgent`](langroid/agent/special/table_chat_agent.py) to \n [chat](examples/data-qa/table_chat.py) with tabular datasets (dataframes, files, URLs): LLM generates Pandas code,\n and code is executed using Langroid\'s tool/function-call mechanism. \n - **Demo:** 3-agent system for Audience [Targeting](https://langroid.github.io/langroid/demos/targeting/audience-targeting/).\n - **0.1.27**: Added [support](langroid/cachedb/momento_cachedb.py) \n for [Momento Serverless Cache](https://www.gomomento.com/) as an alternative to Redis.\n - **0.1.24**: [`DocChatAgent`](langroid/agent/special/doc_chat_agent.py) \n now [accepts](langroid/parsing/pdf_parser.py) PDF files or URLs.\n\n</details>\n\n# :rocket: Demo\nSuppose you want to extract structured information about the key terms \nof a commercial lease document. You can easily do this with Langroid using a two-agent system,\nas we show in the [langroid-examples](https://github.com/langroid/langroid-examples/blob/main/examples/docqa/chat_multi_extract.py) repo.\nThe demo showcases just a few of the many features of Langroid, such as:\n- Multi-agent collaboration: `LeaseExtractor` is in charge of the task, and its LLM (GPT4) generates questions \nto be answered by the `DocAgent`.\n- Retrieval augmented question-answering, with **source-citation**: `DocAgent` LLM (GPT4) uses retrieval from a vector-store to \nanswer the `LeaseExtractor`\'s questions, cites the specific excerpt supporting the answer. \n- Function-calling (also known as tool/plugin): When it has all the information it \nneeds, the `LeaseExtractor` LLM presents the information in a structured \nformat using a Function-call. \n\nHere is what it looks like in action:\n\n![Demo](docs/assets/demos/lease-extractor-demo.gif)\n\n\n# :zap: Highlights\n\n- **Agents as first-class citizens:** The [Agent](https://langroid.github.io/langroid/reference/agent/base/#langroid.agent.base.Agent) class encapsulates LLM conversation state,\n and optionally a vector-store and tools. Agents are a core abstraction in Langroid;\n Agents act as _message transformers_, and by default provide 3 _responder_ methods, one corresponding to each entity: LLM, Agent, User.\n- **Tasks:** A [Task](https://langroid.github.io/langroid/reference/agent/task/) class wraps an Agent, and gives the agent instructions (or roles, or goals), \n manages iteration over an Agent\'s responder methods, \n and orchestrates multi-agent interactions via hierarchical, recursive\n task-delegation. The `Task.run()` method has the same \n type-signature as an Agent\'s responder\'s methods, and this is key to how \n a task of an agent can delegate to other sub-tasks: from the point of view of a Task,\n sub-tasks are simply additional responders, to be used in a round-robin fashion \n after the agent\'s own responders.\n- **Modularity, Reusabilily, Loose coupling:** The `Agent` and `Task` abstractions allow users to design\n Agents with specific skills, wrap them in Tasks, and combine tasks in a flexible way.\n- **LLM Support**: Langroid supports OpenAI LLMs including GPT-3.5-Turbo,\n GPT-4.\n- **Caching of LLM responses:** Langroid supports [Redis](https://redis.com/try-free/) and \n [Momento](https://www.gomomento.com/) to cache LLM responses.\n- **Vector-stores**: [Qdrant](https://qdrant.tech/) and [Chroma](https://www.trychroma.com/) are currently supported.\n Vector stores allow for Retrieval-Augmented-Generation (RAG).\n- **Grounding and source-citation:** Access to external documents via vector-stores \n allows for grounding and source-citation.\n- **Observability, Logging, Lineage:** Langroid generates detailed logs of multi-agent interactions and\n maintains provenance/lineage of messages, so that you can trace back\n the origin of a message.\n- **Tools/Plugins/Function-calling**: Langroid supports OpenAI\'s recently\n released [function calling](https://platform.openai.com/docs/guides/gpt/function-calling)\n feature. In addition, Langroid has its own native equivalent, which we\n call **tools** (also known as "plugins" in other contexts). Function\n calling and tools have the same developer-facing interface, implemented\n using [Pydantic](https://docs.pydantic.dev/latest/),\n which makes it very easy to define tools/functions and enable agents\n to use them. Benefits of using Pydantic are that you never have to write\n complex JSON specs for function calling, and when the LLM\n hallucinates malformed JSON, the Pydantic error message is sent back to\n the LLM so it can fix it!\n\n--- \n\n# :gear: Installation and Setup\n\n:whale: For a simpler setup, see the Docker section below, which lets you get started just\nby setting up environment variables in a `.env` file.\n\n### Install `langroid`\nLangroid requires Python 3.11+. We recommend using a virtual environment.\nUse `pip` to install `langroid` (from PyPi) to your virtual environment:\n```bash\npip install langroid\n```\nThe core Langroid package lets you use OpenAI Embeddings models via their API. \nIf you instead want to use the `all-MiniLM-L6-v2` embeddings model\nfrom from HuggingFace, install Langroid like this:\n```bash\npip install langroid[hf-embeddings]\n```\nNote that this will install `torch` and `sentence-transformers` libraries.\n\n<details>\n<summary><b>Optional Installs for using SQL Chat with a PostgreSQL DB </b></summary>\n\nIf you are using `SQLChatAgent` \n(e.g. the script [`examples/data-qa/sql-chat/sql_chat.py`](examples/data-qa/sql-chat/sql_chat.py)),\nwith a postgres db, you will need to:\n\n- Install PostgreSQL dev libraries for your platform, e.g.\n - `sudo apt-get install libpq-dev` on Ubuntu,\n - `brew install postgresql` on Mac, etc.\n- Install langroid with the postgres extra, e.g. `pip install langroid[postgres]`\n or `poetry add langroid[postgres]` or `poetry install -E postgres`.\n If this gives you an error, try `pip install psycopg2-binary` in your virtualenv.\n</details>\n\n### Set up environment variables (API keys, etc)\n\nTo get started, all you need is an OpenAI API Key.\nIf you don\'t have one, see [this OpenAI Page](https://help.openai.com/en/collections/3675940-getting-started-with-openai-api).\nCurrently only OpenAI models are supported. Others will be added later\n(Pull Requests welcome!).\n\nIn the root of the repo, copy the `.env-template` file to a new file `.env`: \n```bash\ncp .env-template .env\n```\nThen insert your OpenAI API Key. \nYour `.env` file should look like this:\n```bash\nOPENAI_API_KEY=your-key-here-without-quotes\n````\n\nAlternatively, you can set this as an environment variable in your shell\n(you will need to do this every time you open a new shell):\n```bash\nexport OPENAI_API_KEY=your-key-here-without-quotes\n```\n\n\n<details>\n<summary><b>Optional Setup Instructions (click to expand) </b></summary>\n\nAll of the following environment variable settings are optional, and some are only needed \nto use specific features (as noted below).\n\n- **Qdrant** Vector Store API Key, URL. This is only required if you want to use Qdrant cloud.\n You can sign up for a free 1GB account at [Qdrant cloud](https://cloud.qdrant.io).\n If you skip setting up these, Langroid will use Qdrant in local-storage mode.\n Alternatively [Chroma](https://docs.trychroma.com/) is also currently supported. \n We use the local-storage version of Chroma, so there is no need for an API key.\n Langroid uses Qdrant by default.\n- **Redis** Password, host, port: This is optional, and only needed to cache LLM API responses\n using Redis Cloud. Redis [offers](https://redis.com/try-free/) a free 30MB Redis account\n which is more than sufficient to try out Langroid and even beyond.\n If you don\'t set up these, Langroid will use a pure-python \n Redis in-memory cache via the [Fakeredis](https://fakeredis.readthedocs.io/en/latest/) library.\n- **Momento** Serverless Caching of LLM API responses (as an alternative to Redis). \n To use Momento instead of Redis:\n - enter your Momento Token in the `.env` file, as the value of `MOMENTO_AUTH_TOKEN` (see example file below),\n - in the `.env` file set `CACHE_TYPE=momento` (instead of `CACHE_TYPE=redis` which is the default).\n- **GitHub** Personal Access Token (required for apps that need to analyze git\n repos; token-based API calls are less rate-limited). See this\n [GitHub page](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens).\n- **Google Custom Search API Credentials:** Only needed to enable an Agent to use the `GoogleSearchTool`.\n To use Google Search as an LLM Tool/Plugin/function-call, \n you\'ll need to set up \n [a Google API key](https://developers.google.com/custom-search/v1/introduction#identify_your_application_to_google_with_api_key),\n then [setup a Google Custom Search Engine (CSE) and get the CSE ID](https://developers.google.com/custom-search/docs/tutorial/creatingcse).\n (Documentation for these can be challenging, we suggest asking GPT4 for a step-by-step guide.)\n After obtaining these credentials, store them as values of \n `GOOGLE_API_KEY` and `GOOGLE_CSE_ID` in your `.env` file. \n Full documentation on using this (and other such "stateless" tools) is coming soon, but \n in the meantime take a peek at this [chat example](examples/basic/chat-search.py), which \n shows how you can easily equip an Agent with a `GoogleSearchtool`.\n \n\n\nIf you add all of these optional variables, your `.env` file should look like this:\n```bash\nOPENAI_API_KEY=your-key-here-without-quotes\nGITHUB_ACCESS_TOKEN=your-personal-access-token-no-quotes\nCACHE_TYPE=redis # or momento\nREDIS_PASSWORD=your-redis-password-no-quotes\nREDIS_HOST=your-redis-hostname-no-quotes\nREDIS_PORT=your-redis-port-no-quotes\nMOMENTO_AUTH_TOKEN=your-momento-token-no-quotes # instead of REDIS* variables\nQDRANT_API_KEY=your-key\nQDRANT_API_URL=https://your.url.here:6333 # note port number must be included\nGOOGLE_API_KEY=your-key\nGOOGLE_CSE_ID=your-cse-id\n```\n</details>\n\n<details>\n<summary><b>Optional setup instructions for Microsoft Azure OpenAI(click to expand)</b></summary> \n\nWhen using Azure OpenAI, additional environment variables are required in the \n`.env` file.\nThis page [Microsoft Azure OpenAI](https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line&pivots=programming-language-python#environment-variables)\nprovides more information, and you can set each environment variable as follows:\n\n- `AZURE_API_KEY`, from the value of `API_KEY`\n- `AZURE_OPENAI_API_BASE` from the value of `ENDPOINT`, typically looks like `https://your.domain.azure.com`.\n- For `AZURE_OPENAI_API_VERSION`, you can use the default value in `.env-template`, and latest version can be found [here](https://learn.microsoft.com/en-us/azure/ai-services/openai/whats-new#azure-openai-chat-completion-general-availability-ga)\n- `AZURE_OPENAI_DEPLOYMENT_NAME` is the name of the deployed model, which is defined by the user during the model setup \n- `AZURE_GPT_MODEL_NAME` GPT-3.5-Turbo or GPT-4 model names that you chose when you setup your Azure OpenAI account.\n\n</details>\n\n---\n\n# :whale: Docker Instructions\n\nWe provide a containerized version of the [`langroid-examples`](https://github.com/langroid/langroid-examples) \nrepository via this [Docker Image](https://hub.docker.com/r/langroid/langroid).\nAll you need to do is set up environment variables in the `.env` file.\nPlease follow these steps to setup the container:\n\n```bash\n# get the .env file template from `langroid` repo\nwget https://github.com/langroid/langroid/blob/main/.env-template .env\n\n# Edit the .env file with your favorite editor (here nano), \n# and add API keys as explained above\nnano .env\n\n# launch the container\ndocker run -it -v ./.env:/.env langroid/langroid\n\n# Use this command to run any of the scripts in the `examples` directory\npython examples/<Path/To/Example.py> \n``` \n\n\n\n# :tada: Usage Examples\n\nThese are quick teasers to give a glimpse of what you can do with Langroid\nand how your code would look. \n\n:warning: The code snippets below are intended to give a flavor of the code\nand they are **not** complete runnable examples! For that we encourage you to \nconsult the [`langroid-examples`](https://github.com/langroid/langroid-examples) \nrepository.\n\n:information_source: The various LLM prompts and instructions in Langroid\nhave been tested to work well with GPT4.\nSwitching to GPT3.5-Turbo is easy via a config flag\n(e.g., `cfg = OpenAIGPTConfig(chat_model=OpenAIChatModel.GPT3_5_TURBO)`),\nand may suffice for some applications, but in general you may see inferior results.\n\n\n:book: Also see the\n[`Getting Started Guide`](https://langroid.github.io/langroid/quick-start/)\nfor a detailed tutorial.\n\n\n\nClick to expand any of the code examples below.\nAll of these can be run in a Colab notebook:\n[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langroid/langroid/blob/main/examples/langroid_quick_examples.ipynb)\n\n<details>\n<summary> <b> Direct interaction with OpenAI LLM </b> </summary>\n\n```python\nfrom langroid.language_models.openai_gpt import ( \n OpenAIGPTConfig, OpenAIChatModel, OpenAIGPT,\n)\nfrom langroid.language_models.base import LLMMessage, Role\n\ncfg = OpenAIGPTConfig(chat_model=OpenAIChatModel.GPT4)\n\nmdl = OpenAIGPT(cfg)\n\nmessages = [\n LLMMessage(content="You are a helpful assistant", role=Role.SYSTEM), \n LLMMessage(content="What is the capital of Ontario?", role=Role.USER),\n]\nresponse = mdl.chat(messages, max_tokens=200)\nprint(response.message)\n```\n</details>\n\n<details>\n<summary> <b> Define an agent, set up a task, and run it </b> </summary>\n\n```python\nfrom langroid.agent.chat_agent import ChatAgent, ChatAgentConfig\nfrom langroid.agent.task import Task\nfrom langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig\n\nconfig = ChatAgentConfig(\n llm = OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n vecdb=None, # no vector store\n)\nagent = ChatAgent(config)\n# get response from agent\'s LLM, and put this in an interactive loop...\n# answer = agent.llm_response("What is the capital of Ontario?")\n # ... OR instead, set up a task (which has a built-in loop) and run it\ntask = Task(agent, name="Bot") \ntask.run() # ... a loop seeking response from LLM or User at each turn\n```\n</details>\n\n<details>\n<summary><b> Three communicating agents </b></summary>\n\nA toy numbers game, where when given a number `n`:\n- `repeater_agent`\'s LLM simply returns `n`,\n- `even_agent`\'s LLM returns `n/2` if `n` is even, else says "DO-NOT-KNOW"\n- `odd_agent`\'s LLM returns `3*n+1` if `n` is odd, else says "DO-NOT-KNOW"\n\nFirst define the 3 agents, and set up their tasks with instructions:\n\n```python\nfrom langroid.utils.constants import NO_ANSWER\nfrom langroid.agent.chat_agent import ChatAgent, ChatAgentConfig\nfrom langroid.agent.task import Task\nfrom langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig\nconfig = ChatAgentConfig(\n llm = OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n vecdb = None,\n)\nrepeater_agent = ChatAgent(config)\nrepeater_task = Task(\n repeater_agent,\n name = "Repeater",\n system_message="""\n Your job is to repeat whatever number you receive.\n """,\n llm_delegate=True, # LLM takes charge of task\n single_round=False, \n)\neven_agent = ChatAgent(config)\neven_task = Task(\n even_agent,\n name = "EvenHandler",\n system_message=f"""\n You will be given a number. \n If it is even, divide by 2 and say the result, nothing else.\n If it is odd, say {NO_ANSWER}\n """,\n single_round=True, # task done after 1 step() with valid response\n)\n\nodd_agent = ChatAgent(config)\nodd_task = Task(\n odd_agent,\n name = "OddHandler",\n system_message=f"""\n You will be given a number n. \n If it is odd, return (n*3+1), say nothing else. \n If it is even, say {NO_ANSWER}\n """,\n single_round=True, # task done after 1 step() with valid response\n)\n```\nThen add the `even_task` and `odd_task` as sub-tasks of `repeater_task`, \nand run the `repeater_task`, kicking it off with a number as input:\n```python\nrepeater_task.add_sub_task([even_task, odd_task])\nrepeater_task.run("3")\n```\n\n</details>\n\n<details>\n<summary><b> Simple Tool/Function-calling example </b></summary>\n\nLangroid leverages Pydantic to support OpenAI\'s\n[Function-calling API](https://platform.openai.com/docs/guides/gpt/function-calling)\nas well as its own native tools. The benefits are that you don\'t have to write\nany JSON to specify the schema, and also if the LLM hallucinates a malformed\ntool syntax, Langroid sends the Pydantic validation error (suitiably sanitized) \nto the LLM so it can fix it!\n\nSimple example: Say the agent has a secret list of numbers, \nand we want the LLM to find the smallest number in the list. \nWe want to give the LLM a `probe` tool/function which takes a\nsingle number `n` as argument. The tool handler method in the agent\nreturns how many numbers in its list are at most `n`.\n\nFirst define the tool using Langroid\'s `ToolMessage` class:\n\n\n```python\nfrom langroid.agent.tool_message import ToolMessage\nclass ProbeTool(ToolMessage):\n request: str = "probe" # specifies which agent method handles this tool\n purpose: str = """\n To find how many numbers in my list are less than or equal to \n the <number> you specify.\n """ # description used to instruct the LLM on when/how to use the tool\n number: int # required argument to the tool\n```\n\nThen define a `SpyGameAgent` as a subclass of `ChatAgent`, \nwith a method `probe` that handles this tool:\n\n```python\nfrom langroid.agent.chat_agent import ChatAgent, ChatAgentConfig\nclass SpyGameAgent(ChatAgent):\n def __init__(self, config: ChatAgentConfig):\n super().__init__(config)\n self.numbers = [3, 4, 8, 11, 15, 25, 40, 80, 90]\n\n def probe(self, msg: ProbeTool) -> str:\n # return how many numbers in self.numbers are less or equal to msg.number\n return str(len([n for n in self.numbers if n <= msg.number]))\n```\n\nWe then instantiate the agent and enable it to use and respond to the tool:\n\n```python\nfrom langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig\nspy_game_agent = SpyGameAgent(\n ChatAgentConfig(\n name="Spy",\n llm = OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n vecdb=None,\n use_tools=False, # don\'t use Langroid native tool\n use_functions_api=True, # use OpenAI function-call API\n )\n)\nspy_game_agent.enable_message(ProbeTool)\n```\n\nFor a full working example see the\n[chat-agent-tool.py](https://github.com/langroid/langroid-examples/blob/main/examples/quick-start/chat-agent-tool.py)\nscript in the `langroid-examples` repo.\n</details>\n\n<details>\n<summary> <b>Tool/Function-calling to extract structured information from text </b> </summary>\n\nSuppose you want an agent to extract \nthe key terms of a lease, from a lease document, as a nested JSON structure.\nFirst define the desired structure via Pydantic models:\n\n```python\nfrom pydantic import BaseModel\nclass LeasePeriod(BaseModel):\n start_date: str\n end_date: str\n\n\nclass LeaseFinancials(BaseModel):\n monthly_rent: str\n deposit: str\n\nclass Lease(BaseModel):\n period: LeasePeriod\n financials: LeaseFinancials\n address: str\n```\n\nThen define the `LeaseMessage` tool as a subclass of Langroid\'s `ToolMessage`.\nNote the tool has a required argument `terms` of type `Lease`:\n\n```python\nclass LeaseMessage(ToolMessage):\n request: str = "lease_info"\n purpose: str = """\n Collect information about a Commercial Lease.\n """\n terms: Lease\n```\n\nThen define a `LeaseExtractorAgent` with a method `lease_info` that handles this tool,\ninstantiate the agent, and enable it to use and respond to this tool:\n\n```python\nclass LeaseExtractorAgent(ChatAgent):\n def lease_info(self, message: LeaseMessage) -> str:\n print(\n f"""\n DONE! Successfully extracted Lease Info:\n {message.terms}\n """\n )\n return json.dumps(message.terms.dict())\n \nlease_extractor_agent = LeaseExtractorAgent(\n ChatAgentConfig(\n llm=OpenAIGPTConfig(),\n use_functions_api=False,\n use_tools=True,\n )\n)\nlease_extractor_agent.enable_message(LeaseMessage)\n```\n\nSee the [`chat_multi_extract.py`](https://github.com/langroid/langroid-examples/blob/main/examples/docqa/chat_multi_extract.py)\nscript in the `langroid-examples` repo for a full working example.\n</details>\n\n<details>\n<summary><b> Chat with documents (file paths, URLs, etc) </b></summary>\n\nLangroid provides a specialized agent class `DocChatAgent` for this purpose.\nIt incorporates document sharding, embedding, storage in a vector-DB, \nand retrieval-augmented query-answer generation.\nUsing this class to chat with a collection of documents is easy.\nFirst create a `DocChatAgentConfig` instance, with a \n`doc_paths` field that specifies the documents to chat with.\n\n```python\nfrom langroid.agent.doc_chat_agent import DocChatAgentConfig\nfrom langroid.vector_store.qdrantdb import QdrantDBConfig\nconfig = DocChatAgentConfig(\n doc_paths = [\n "https://en.wikipedia.org/wiki/Language_model",\n "https://en.wikipedia.org/wiki/N-gram_language_model",\n "/path/to/my/notes-on-language-models.txt",\n ]\n llm = OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n vecdb=QdrantDBConfig()\n)\n```\n\nThen instantiate the `DocChatAgent` (this ingests the docs into the vector-store):\n\n```python\nagent = DocChatAgent(config)\n```\nThen we can either ask the agent one-off questions,\n```python\nagent.chat("What is a language model?")\n```\nor wrap it in a `Task` and run an interactive loop with the user:\n```python\nfrom langroid.task import Task\ntask = Task(agent)\ntask.run()\n```\n\nSee full working scripts in the \n[`docqa`](https://github.com/langroid/langroid-examples/tree/main/examples/docqa)\nfolder of the `langroid-examples` repo.\n</details>\n\n<details>\n<summary><b> :fire: Chat with tabular data (file paths, URLs, dataframes) </b></summary>\n\nUsing Langroid you can set up a `TableChatAgent` with a dataset (file path, URL or dataframe),\nand query it. The Agent\'s LLM generates Pandas code to answer the query, \nvia function-calling (or tool/plugin), and the Agent\'s function-handling method\nexecutes the code and returns the answer.\n\nHere is how you can do this:\n\n```python\nfrom langroid.agent.special.table_chat_agent import TableChatAgent, TableChatAgentConfig\nfrom langroid.agent.task import Task\nfrom langroid.language_models.openai_gpt import OpenAIChatModel, OpenAIGPTConfig\n```\n\nSet up a `TableChatAgent` for a data file, URL or dataframe\n(Ensure the data table has a header row; the delimiter/separator is auto-detected):\n```python\ndataset = "https://archive.ics.uci.edu/ml/machine-learning-databases/wine-quality/winequality-red.csv"\n# or dataset = "/path/to/my/data.csv"\n# or dataset = pd.read_csv("/path/to/my/data.csv")\nagent = TableChatAgent(\n config=TableChatAgentConfig(\n data=dataset, \n llm=OpenAIGPTConfig(\n chat_model=OpenAIChatModel.GPT4,\n ),\n )\n)\n```\nSet up a task, and ask one-off questions like this: \n\n```python\ntask = Task(\n agent, \n name = "DataAssistant",\n default_human_response="", # to avoid waiting for user input\n)\nresult = task.run(\n "What is the average alcohol content of wines with a quality rating above 7?",\n turns=2 # return after user question, LLM fun-call/tool response, Agent code-exec result\n) \nprint(result.content)\n```\nOr alternatively, set up a task and run it in an interactive loop with the user:\n\n```python\ntask = Task(agent, name="DataAssistant")\ntask.run()\n``` \n\nFor a full working example see the \n[`table_chat.py`](https://github.com/langroid/langroid-examples/tree/main/examples/data-qa/table_chat.py)\nscript in the `langroid-examples` repo.\n\n\n</details>\n\n---\n\n# :heart: Thank you to our [supporters](https://github.com/langroid/langroid/stargazers)\n\nIf you like this project, please give it a star ⭐ and 📢 spread the word in your network or social media:\n\n[![Share on Twitter](https://img.shields.io/twitter/url?style=social&url=https://github.com/langroid/langroid)](https://twitter.com/intent/tweet?text=Langroid%20is%20a%20powerful,%20elegant%20new%20framework%20to%20easily%20build%20%23LLM%20applications.%20You%20set%20up%20LLM-powered%20Agents%20with%20vector-stores,%20assign%20tasks,%20and%20have%20them%20collaboratively%20solve%20problems%20via%20message-transformations.%20https://github.com/langroid/langroid)\n[![Share on LinkedIn](https://img.shields.io/badge/Share%20on-LinkedIn-blue)](https://www.linkedin.com/shareArticle?mini=true&url=https://github.com/langroid/langroid&title=Langroid:%20A%20Powerful,%20Elegant%20Framework&summary=Langroid%20is%20a%20powerful,%20elegant%20new%20framework%20to%20easily%20build%20%23LLM%20applications.%20You%20set%20up%20LLM-powered%20Agents%20with%20vector-stores,%20assign%20tasks,%20and%20have%20them%20collaboratively%20solve%20problems%20via%20message-transformations.)\n[![Share on Hacker News](https://img.shields.io/badge/-Share%20on%20Hacker%20News-orange)](https://news.ycombinator.com/submitlink?u=https%3A%2F%2Fgithub.com%2Flangroid%2Flangroid&t=Harness%20LLMs%20with%20Multi-Agent%20Programming)\n[![Share on Reddit](https://img.shields.io/badge/-Share%20on%20Reddit-blue)](https://www.reddit.com/submit?url=https%3A%2F%2Fgithub.com%2Flangroid%2Flangroid&title=Harness%20LLMs%20with%20Multi-Agent%20Programming)\n\n\n\n\nYour support will help build Langroid\'s momentum and community.\n\n\n\n\n# Langroid Co-Founders\n\n- [Prasad Chalasani](https://www.linkedin.com/in/pchalasani/) (IIT BTech/CS, CMU PhD/ML; Independent ML Consultant)\n- [Somesh Jha](https://www.linkedin.com/in/somesh-jha-80208015/) (IIT BTech/CS, CMU PhD/CS; Professor of CS, U Wisc at Madison)\n\n\n\n',
90
- 'author': 'Prasad Chalasani',
91
- 'author_email': 'pchalasani@gmail.com',
92
- 'maintainer': 'None',
93
- 'maintainer_email': 'None',
94
- 'url': 'None',
95
- 'packages': packages,
96
- 'package_data': package_data,
97
- 'install_requires': install_requires,
98
- 'extras_require': extras_require,
99
- 'python_requires': '>=3.8.1,<3.12',
100
- }
101
-
102
-
103
- setup(**setup_kwargs)
File without changes
File without changes
File without changes