langroid 0.17.1__py3-none-any.whl → 0.18.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
langroid/agent/base.py CHANGED
@@ -1316,7 +1316,7 @@ class Agent(ABC):
1316
1316
  """
1317
1317
  Convert result of a responder (agent_response or llm_response, or task.run()),
1318
1318
  or tool handler, or handle_message_fallback,
1319
- to a ChatDocument, to enabling handling by other
1319
+ to a ChatDocument, to enable handling by other
1320
1320
  responders/tasks in a task loop possibly involving multiple agents.
1321
1321
 
1322
1322
  Args:
@@ -244,8 +244,11 @@ class ChainlitAgentCallbacks:
244
244
  agent.callbacks.show_error_message = self.show_error_message
245
245
  agent.callbacks.show_start_response = self.show_start_response
246
246
  self.config = config
247
-
248
247
  self.agent: lr.Agent = agent
248
+ if self.agent.llm is not None:
249
+ # We don't want to suppress LLM output in async + streaming,
250
+ # since we often use chainlit async callbacks to display LLM output
251
+ self.agent.llm.config.async_stream_quiet = False
249
252
  if msg is not None:
250
253
  self.show_first_user_message(msg)
251
254
 
@@ -248,12 +248,6 @@ class DocChatAgent(ChatAgent):
248
248
  def ingest(self) -> None:
249
249
  """
250
250
  Chunk + embed + store docs specified by self.config.doc_paths
251
-
252
- Returns:
253
- dict with keys:
254
- n_splits: number of splits
255
- urls: list of urls
256
- paths: list of file paths
257
251
  """
258
252
  if len(self.config.doc_paths) == 0:
259
253
  # we must be using a previously defined collection
@@ -1110,6 +1104,14 @@ class DocChatAgent(ChatAgent):
1110
1104
  Returns:
1111
1105
 
1112
1106
  """
1107
+
1108
+ if (
1109
+ self.vecdb is None
1110
+ or self.vecdb.config.collection_name
1111
+ not in self.vecdb.list_collections(empty=False)
1112
+ ):
1113
+ return []
1114
+
1113
1115
  # if we are using cross-encoder reranking or reciprocal rank fusion (RRF),
1114
1116
  # we can retrieve more docs during retrieval, and leave it to the cross-encoder
1115
1117
  # or RRF reranking to whittle down to self.config.parsing.n_similar_docs
@@ -1275,6 +1277,13 @@ class DocChatAgent(ChatAgent):
1275
1277
  List[Document]: list of relevant extracts
1276
1278
 
1277
1279
  """
1280
+ if (
1281
+ self.vecdb is None
1282
+ or self.vecdb.config.collection_name
1283
+ not in self.vecdb.list_collections(empty=False)
1284
+ ):
1285
+ return query, []
1286
+
1278
1287
  if len(self.dialog) > 0 and not self.config.assistant_mode:
1279
1288
  # Regardless of whether we are in conversation mode or not,
1280
1289
  # for relevant doc/chunk extraction, we must convert the query
@@ -200,12 +200,13 @@ class DonePassTool(PassTool):
200
200
 
201
201
 
202
202
  class ForwardTool(PassTool):
203
- """Tool for forwarding the received msg (ChatDocument) to another agent.
203
+ """Tool for forwarding the received msg (ChatDocument) to another agent or entity.
204
204
  Similar to PassTool, but with a specified recipient agent.
205
205
  """
206
206
 
207
207
  purpose: str = """
208
- To forward the current message to an <agent>.
208
+ To forward the current message to an <agent>, where <agent>
209
+ could be the name of an agent, or an entity such as "user", "llm".
209
210
  """
210
211
  request: str = "forward_tool"
211
212
  agent: str
@@ -39,6 +39,10 @@ ToolTypes = Literal["function"]
39
39
 
40
40
 
41
41
  class LLMConfig(BaseSettings):
42
+ """
43
+ Common configuration for all language models.
44
+ """
45
+
42
46
  type: str = "openai"
43
47
  streamer: Optional[Callable[[Any], None]] = noop_fn
44
48
  api_base: str | None = None
@@ -48,6 +52,7 @@ class LLMConfig(BaseSettings):
48
52
  completion_model: str = ""
49
53
  temperature: float = 0.0
50
54
  chat_context_length: int = 8000
55
+ async_stream_quiet: bool = True # suppress streaming output in async mode?
51
56
  completion_context_length: int = 8000
52
57
  max_output_tokens: int = 1024 # generate at most this many tokens
53
58
  # if input length + max_output_tokens > context length of model,
@@ -149,6 +154,10 @@ class OpenAIToolSpec(BaseModel):
149
154
 
150
155
 
151
156
  class LLMTokenUsage(BaseModel):
157
+ """
158
+ Usage of tokens by an LLM.
159
+ """
160
+
152
161
  prompt_tokens: int = 0
153
162
  completion_tokens: int = 0
154
163
  cost: float = 0.0
@@ -173,6 +182,10 @@ class LLMTokenUsage(BaseModel):
173
182
 
174
183
 
175
184
  class Role(str, Enum):
185
+ """
186
+ Possible roles for a message in a chat.
187
+ """
188
+
176
189
  USER = "user"
177
190
  SYSTEM = "system"
178
191
  ASSISTANT = "assistant"
@@ -708,7 +708,7 @@ class OpenAIGPT(LanguageModel):
708
708
  event_args = ""
709
709
  event_fn_name = ""
710
710
  event_tool_deltas: Optional[List[Dict[str, Any]]] = None
711
-
711
+ silent = is_async and self.config.async_stream_quiet
712
712
  # The first two events in the stream of Azure OpenAI is useless.
713
713
  # In the 1st: choices list is empty, in the 2nd: the dict delta has null content
714
714
  if chat:
@@ -727,42 +727,40 @@ class OpenAIGPT(LanguageModel):
727
727
  event_text = choices[0]["text"]
728
728
  if event_text:
729
729
  completion += event_text
730
- if not is_async:
730
+ if not silent:
731
731
  sys.stdout.write(Colors().GREEN + event_text)
732
732
  sys.stdout.flush()
733
733
  self.config.streamer(event_text)
734
734
  if event_fn_name:
735
735
  function_name = event_fn_name
736
736
  has_function = True
737
- if not is_async:
737
+ if not silent:
738
738
  sys.stdout.write(Colors().GREEN + "FUNC: " + event_fn_name + ": ")
739
739
  sys.stdout.flush()
740
740
  self.config.streamer(event_fn_name)
741
741
 
742
742
  if event_args:
743
743
  function_args += event_args
744
- if not is_async:
744
+ if not silent:
745
745
  sys.stdout.write(Colors().GREEN + event_args)
746
746
  sys.stdout.flush()
747
747
  self.config.streamer(event_args)
748
748
 
749
- if event_tool_deltas is not None:
750
- # print out streaming tool calls
749
+ if event_tool_deltas is not None and not silent:
750
+ # print out streaming tool calls, if not async
751
751
  for td in event_tool_deltas:
752
752
  if td["function"]["name"] is not None:
753
753
  tool_fn_name = td["function"]["name"]
754
- if not is_async:
755
- sys.stdout.write(
756
- Colors().GREEN + "OAI-TOOL: " + tool_fn_name + ": "
757
- )
758
- sys.stdout.flush()
759
- self.config.streamer(tool_fn_name)
754
+ sys.stdout.write(
755
+ Colors().GREEN + "OAI-TOOL: " + tool_fn_name + ": "
756
+ )
757
+ sys.stdout.flush()
758
+ self.config.streamer(tool_fn_name)
760
759
  if td["function"]["arguments"] != "":
761
760
  tool_fn_args = td["function"]["arguments"]
762
- if not is_async:
763
- sys.stdout.write(Colors().GREEN + tool_fn_args)
764
- sys.stdout.flush()
765
- self.config.streamer(tool_fn_args)
761
+ sys.stdout.write(Colors().GREEN + tool_fn_args)
762
+ sys.stdout.flush()
763
+ self.config.streamer(tool_fn_args)
766
764
 
767
765
  # show this delta in the stream
768
766
  if choices[0].get("finish_reason", "") in [
@@ -872,6 +870,7 @@ class OpenAIGPT(LanguageModel):
872
870
  completion=completion,
873
871
  function_args=function_args,
874
872
  function_name=function_name,
873
+ is_async=True,
875
874
  )
876
875
  if is_break:
877
876
  break
@@ -81,13 +81,25 @@ def is_plain_text(path_or_bytes: str | bytes) -> bool:
81
81
  else:
82
82
  content = path_or_bytes[:1024]
83
83
  try:
84
+ # Use magic to detect the MIME type
85
+ import magic
86
+
87
+ mime_type = magic.from_buffer(content, mime=True)
88
+
89
+ # Check if the MIME type is not a text type
90
+ if not mime_type.startswith("text/"):
91
+ return False
92
+
84
93
  # Attempt to decode the content as UTF-8
85
94
  content = content[: find_last_full_char(content)]
86
95
 
87
- _ = content.decode("utf-8")
88
- # Additional checks can go here, e.g., to verify that the content
89
- # doesn't contain too many unusual characters for it to be considered text
90
- return True
96
+ try:
97
+ _ = content.decode("utf-8")
98
+ # Additional checks can go here, e.g., to verify that the content
99
+ # doesn't contain too many unusual characters for it to be considered text
100
+ return True
101
+ except UnicodeDecodeError:
102
+ return False
91
103
  except UnicodeDecodeError:
92
104
  # If decoding fails, it's likely not plain text (or not encoded in UTF-8)
93
105
  return False
@@ -123,7 +135,8 @@ class DocumentParser(Parser):
123
135
  Returns:
124
136
  DocumentParser: An instance of a DocumentParser subclass.
125
137
  """
126
- if DocumentParser._document_type(source, doc_type) == DocumentType.PDF:
138
+ inferred_doc_type = DocumentParser._document_type(source, doc_type)
139
+ if inferred_doc_type == DocumentType.PDF:
127
140
  if config.pdf.library == "fitz":
128
141
  return FitzPDFParser(source, config)
129
142
  elif config.pdf.library == "pypdf":
@@ -138,7 +151,7 @@ class DocumentParser(Parser):
138
151
  raise ValueError(
139
152
  f"Unsupported PDF library specified: {config.pdf.library}"
140
153
  )
141
- elif DocumentParser._document_type(source, doc_type) == DocumentType.DOCX:
154
+ elif inferred_doc_type == DocumentType.DOCX:
142
155
  if config.docx.library == "unstructured":
143
156
  return UnstructuredDocxParser(source, config)
144
157
  elif config.docx.library == "python-docx":
@@ -147,7 +160,7 @@ class DocumentParser(Parser):
147
160
  raise ValueError(
148
161
  f"Unsupported DOCX library specified: {config.docx.library}"
149
162
  )
150
- elif DocumentParser._document_type(source, doc_type) == DocumentType.DOC:
163
+ elif inferred_doc_type == DocumentType.DOC:
151
164
  return UnstructuredDocParser(source, config)
152
165
  else:
153
166
  source_name = source if isinstance(source, str) else "bytes"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: langroid
3
- Version: 0.17.1
3
+ Version: 0.18.1
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  License: MIT
6
6
  Author: Prasad Chalasani
@@ -244,7 +244,9 @@ teacher_task.run()
244
244
  <summary> <b>Click to expand</b></summary>
245
245
 
246
246
  - **Oct 2024:**
247
- - **[0.17.0]** XML-based tools, see [docs](https://langroid.github.io/langroid/tutorials/xml-tools/).
247
+ - **[0.18.0]** [LLMConfig.async_stream_quiet](https://langroid.github.io/langroid/notes/async-streaming/) flag to
248
+ turn off LLM output in async + stream mode.
249
+ - **[0.17.0]** XML-based tools, see [docs](https://langroid.github.io/langroid/notes/xml-tools/).
248
250
  - **Sep 2024:**
249
251
  - **[0.16.0](https://github.com/langroid/langroid/releases/tag/0.16.0)** Support for OpenAI `o1-mini` and `o1-preview` models.
250
252
  - **[0.15.0](https://github.com/langroid/langroid/releases/tag/0.15.0)** Cerebras API support -- run llama-3.1 models hosted on Cerebras Cloud (very fast inference).
@@ -1,16 +1,16 @@
1
1
  langroid/__init__.py,sha256=z_fCOLQJPOw3LLRPBlFB5-2HyCjpPgQa4m4iY5Fvb8Y,1800
2
2
  langroid/agent/__init__.py,sha256=ll0Cubd2DZ-fsCMl7e10hf9ZjFGKzphfBco396IKITY,786
3
- langroid/agent/base.py,sha256=hX_IGnEWcIhFGaLyhkZr6HWe_nQ4y6ERko-2H9mTkhM,64047
3
+ langroid/agent/base.py,sha256=yFR9yrUWbuIOufgVDPOSjFDIP28JvGJWqvEu0HXEMX8,64045
4
4
  langroid/agent/batch.py,sha256=QZdlt1563hx4l3AXrCaGovE-PNG93M3DsvQAbDzdiS8,13705
5
5
  langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- langroid/agent/callbacks/chainlit.py,sha256=Qedk1-CBCgo9PdaIa7AboLBFCTgAMg9q5nGmoqpZ378,22050
6
+ langroid/agent/callbacks/chainlit.py,sha256=oiZYfPyfkUfFqhVFBiobN3hAJEaYn0FBSglvNE7t17Q,22302
7
7
  langroid/agent/chat_agent.py,sha256=m1fs-XsBzusRicwBETId3dpTQG8qEaS0yym_o4mfAI0,49931
8
8
  langroid/agent/chat_document.py,sha256=FZ_PkeKU5OVp1IUlMvspfqxIXzlyd7J_F32DSYrxQ7E,17651
9
9
  langroid/agent/helpers.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
10
  langroid/agent/junk,sha256=LxfuuW7Cijsg0szAzT81OjWWv1PMNI-6w_-DspVIO2s,339
11
11
  langroid/agent/openai_assistant.py,sha256=2rjCZw45ysNBEGNzQM4uf0bTC4KkatGYAWcVcW4xcek,34337
12
12
  langroid/agent/special/__init__.py,sha256=gik_Xtm_zV7U9s30Mn8UX3Gyuy4jTjQe9zjiE3HWmEo,1273
13
- langroid/agent/special/doc_chat_agent.py,sha256=r1uPunYf2lQcqYQ4fsD8Q5gB9cZyf7cn0KPcR_CLtrU,59065
13
+ langroid/agent/special/doc_chat_agent.py,sha256=xIqBOyLax_jMU0UevxqXf_aQUrRkW6MQUKpKnKvaqkQ,59281
14
14
  langroid/agent/special/lance_doc_chat_agent.py,sha256=s8xoRs0gGaFtDYFUSIRchsgDVbS5Q3C2b2mr3V1Fd-Q,10419
15
15
  langroid/agent/special/lance_rag/__init__.py,sha256=QTbs0IVE2ZgDg8JJy1zN97rUUg4uEPH7SLGctFNumk4,174
16
16
  langroid/agent/special/lance_rag/critic_agent.py,sha256=OtFuHthKQLkdVkvuZ2m0GNq1qOYLqHkm1pfLRFnSg5c,9548
@@ -40,7 +40,7 @@ langroid/agent/tools/duckduckgo_search_tool.py,sha256=NhsCaGZkdv28nja7yveAhSK_w6
40
40
  langroid/agent/tools/file_tools.py,sha256=GjPB5YDILucYapElnvvoYpGJuZQ25ecLs2REv7edPEo,7292
41
41
  langroid/agent/tools/google_search_tool.py,sha256=y7b-3FtgXf0lfF4AYxrZ3K5pH2dhidvibUOAGBE--WI,1456
42
42
  langroid/agent/tools/metaphor_search_tool.py,sha256=qj4gt453cLEX3EGW7nVzVu6X7LCdrwjSlcNY0qJW104,2489
43
- langroid/agent/tools/orchestration.py,sha256=afIt8Ah0dfrY2TQ7aNrkxUFLV288VTQ0L40Yse_-HeY,10785
43
+ langroid/agent/tools/orchestration.py,sha256=u_iQDwD5L9JgE39pCemmIRG3MxyrBsw6KpQYf7pH-Ek,10881
44
44
  langroid/agent/tools/recipient_tool.py,sha256=0m2kQhYKTeGujAxhSPqH5z6hSAhVB_Dqour6uul2U30,9427
45
45
  langroid/agent/tools/retrieval_tool.py,sha256=2q2pfoYbZNfbWQ0McxrtmfF0ekGglIgRl-6uF26pa-E,871
46
46
  langroid/agent/tools/rewind_tool.py,sha256=XAXL3BpNhCmBGYq_qi_sZfHJuIw7NY2jp4wnojJ7WRs,5606
@@ -67,10 +67,10 @@ langroid/language_models/.chainlit/config.toml,sha256=1t5lHORGzc2E6dkaO9P15jYHu2
67
67
  langroid/language_models/.chainlit/translations/en-US.json,sha256=DAFz2HjOFFfboCStrUfKFg2BpplJPK_OOtixwF_GivY,9931
68
68
  langroid/language_models/__init__.py,sha256=1sUGobooTqq77XC7LxKsvME0RgSd5GGmeyrPo9SMh4U,940
69
69
  langroid/language_models/azure_openai.py,sha256=G4le3j4YLHV7IwgB2C37hO3MKijZ1KjynbYlEvpIF7Y,6214
70
- langroid/language_models/base.py,sha256=3DjxxVL7heuTLYRvNMjaIKS-eP8pMDvu8Gbkb6N3B0U,22286
70
+ langroid/language_models/base.py,sha256=L02ToM7o1y5OlM23xYX8QbriRMVUodgwbnHodl3US28,22542
71
71
  langroid/language_models/config.py,sha256=9Q8wk5a7RQr8LGMT_0WkpjY8S4ywK06SalVRjXlfCiI,378
72
72
  langroid/language_models/mock_lm.py,sha256=HuiAvjHiCfffYF5xjFJUq945HVTW0QPbeUUctOnNCzQ,3868
73
- langroid/language_models/openai_gpt.py,sha256=haPcPe3CXAXpdU39rxGThObfv_YV0UivnL2hEdyyaHk,64810
73
+ langroid/language_models/openai_gpt.py,sha256=AlHg-jbf7PBFcS1iqb5z--upokDcWS7RJ4qpAmovsVw,64822
74
74
  langroid/language_models/prompt_formatter/__init__.py,sha256=2-5cdE24XoFDhifOLl8yiscohil1ogbP1ECkYdBlBsk,372
75
75
  langroid/language_models/prompt_formatter/base.py,sha256=eDS1sgRNZVnoajwV_ZIha6cba5Dt8xjgzdRbPITwx3Q,1221
76
76
  langroid/language_models/prompt_formatter/hf_formatter.py,sha256=PVJppmjRvD-2DF-XNC6mE05vTZ9wbu37SmXwZBQhad0,5055
@@ -82,7 +82,7 @@ langroid/parsing/agent_chats.py,sha256=sbZRV9ujdM5QXvvuHVjIi2ysYSYlap-uqfMMUKulr
82
82
  langroid/parsing/code-parsing.md,sha256=--cyyNiSZSDlIwcjAV4-shKrSiRe2ytF3AdSoS_hD2g,3294
83
83
  langroid/parsing/code_parser.py,sha256=AOxb3xbYpTBPP3goOm5dKfJdh5hS_2BhLVCEkifWZN8,3796
84
84
  langroid/parsing/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
85
- langroid/parsing/document_parser.py,sha256=WGnA5ADwMHliGJt6WW9rc4RiFXQcKU33b5zdPiGrtEY,24265
85
+ langroid/parsing/document_parser.py,sha256=ZGxgG4ytnCIah4HWk3ZrK3EGAMXDjAzI9KaPEXCbMm0,24589
86
86
  langroid/parsing/image_text.py,sha256=sbLIQ5nHe2UnYUksBaQsmZGaX-X0qgEpPd7CEzi_z5M,910
87
87
  langroid/parsing/para_sentence_split.py,sha256=AJBzZojP3zpB-_IMiiHismhqcvkrVBQ3ZINoQyx_bE4,2000
88
88
  langroid/parsing/parse_json.py,sha256=aADo38bAHQhC8on4aWZZzVzSDy-dK35vRLZsFI2ewh8,4756
@@ -137,8 +137,8 @@ langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3Hmh
137
137
  langroid/vector_store/momento.py,sha256=qR-zBF1RKVHQZPZQYW_7g-XpTwr46p8HJuYPCkfJbM4,10534
138
138
  langroid/vector_store/qdrant_cloud.py,sha256=3im4Mip0QXLkR6wiqVsjV1QvhSElfxdFSuDKddBDQ-4,188
139
139
  langroid/vector_store/qdrantdb.py,sha256=v88lqFkepADvlN6lByUj9I4NEKa9X9lWH16uTPPbYrE,17457
140
- pyproject.toml,sha256=dR9DMCoHjNyfJEKqnlAz-hxr3smiZmI2KvQRzip5W9o,7179
141
- langroid-0.17.1.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
142
- langroid-0.17.1.dist-info/METADATA,sha256=LRVlfLjGecpCWm57L0QvP7nWOA3I3o6FayGaqYZ_F78,56325
143
- langroid-0.17.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
144
- langroid-0.17.1.dist-info/RECORD,,
140
+ pyproject.toml,sha256=4WsW43aYs4SPCgfi1cEPVqaOQIHDZRuyUz-S8Ka9oAY,7179
141
+ langroid-0.18.1.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
142
+ langroid-0.18.1.dist-info/METADATA,sha256=wamviAoQosjm8zl2aLANeU22nc0fsiosnPt8weJNsvs,56486
143
+ langroid-0.18.1.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
144
+ langroid-0.18.1.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "langroid"
3
- version = "0.17.1"
3
+ version = "0.18.1"
4
4
  description = "Harness LLMs with Multi-Agent Programming"
5
5
  authors = ["Prasad Chalasani <pchalasani@gmail.com>"]
6
6
  readme = "README.md"