langroid 0.52.6__py3-none-any.whl → 0.52.8__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.
@@ -872,6 +872,7 @@ class DocChatAgent(ChatAgent):
872
872
  else:
873
873
  answer_doc = super().llm_response_forget(final_prompt)
874
874
 
875
+ assert answer_doc is not None, "LLM response should not be None here"
875
876
  final_answer = answer_doc.content.strip()
876
877
  show_if_debug(final_answer, "SUMMARIZE_RESPONSE= ")
877
878
 
@@ -336,15 +336,16 @@ class OpenAIGPTConfig(LLMConfig):
336
336
  # (e.g. anthropic doesn't like first msg to be system msg)
337
337
  litellm.modify_params = True
338
338
  self.seed = None # some local mdls don't support seed
339
- keys_dict = litellm.utils.validate_environment(self.chat_model)
340
- missing_keys = keys_dict.get("missing_keys", [])
341
- if len(missing_keys) > 0:
342
- raise ValueError(
343
- f"""
344
- Missing environment variables for litellm-proxied model:
345
- {missing_keys}
346
- """
347
- )
339
+ if self.api_key == DUMMY_API_KEY:
340
+ keys_dict = litellm.utils.validate_environment(self.chat_model)
341
+ missing_keys = keys_dict.get("missing_keys", [])
342
+ if len(missing_keys) > 0:
343
+ raise ValueError(
344
+ f"""
345
+ Missing environment variables for litellm-proxied model:
346
+ {missing_keys}
347
+ """
348
+ )
348
349
 
349
350
  @classmethod
350
351
  def create(cls, prefix: str) -> Type["OpenAIGPTConfig"]:
@@ -1504,6 +1505,9 @@ class OpenAIGPT(LanguageModel):
1504
1505
  from litellm import completion as litellm_completion
1505
1506
 
1506
1507
  completion_call = litellm_completion
1508
+
1509
+ if self.api_key != DUMMY_API_KEY:
1510
+ kwargs["api_key"] = self.api_key
1507
1511
  else:
1508
1512
  if self.client is None:
1509
1513
  raise ValueError(
@@ -1587,6 +1591,10 @@ class OpenAIGPT(LanguageModel):
1587
1591
  else:
1588
1592
  if self.config.litellm:
1589
1593
  from litellm import acompletion as litellm_acompletion
1594
+
1595
+ if self.api_key != DUMMY_API_KEY:
1596
+ kwargs["api_key"] = self.api_key
1597
+
1590
1598
  # TODO this may not work: text_completion is not async,
1591
1599
  # and we didn't find an async version in litellm
1592
1600
  assert isinstance(self.async_client, AsyncOpenAI)
@@ -1734,6 +1742,9 @@ class OpenAIGPT(LanguageModel):
1734
1742
  from litellm import completion as litellm_completion
1735
1743
 
1736
1744
  completion_call = litellm_completion
1745
+
1746
+ if self.api_key != DUMMY_API_KEY:
1747
+ kwargs["api_key"] = self.api_key
1737
1748
  else:
1738
1749
  if self.client is None:
1739
1750
  raise ValueError("OpenAI/equivalent chat-completion client not set")
@@ -1792,6 +1803,9 @@ class OpenAIGPT(LanguageModel):
1792
1803
  from litellm import acompletion as litellm_acompletion
1793
1804
 
1794
1805
  acompletion_call = litellm_acompletion
1806
+
1807
+ if self.api_key != DUMMY_API_KEY:
1808
+ kwargs["api_key"] = self.api_key
1795
1809
  else:
1796
1810
  if self.async_client is None:
1797
1811
  raise ValueError(
@@ -1893,7 +1907,8 @@ class OpenAIGPT(LanguageModel):
1893
1907
  max_completion_tokens=max_tokens,
1894
1908
  stream=self.get_stream(),
1895
1909
  )
1896
- if self.get_stream():
1910
+ if self.get_stream() and "groq" not in self.chat_model_orig:
1911
+ # groq fails when we include stream_options in the request
1897
1912
  args.update(
1898
1913
  dict(
1899
1914
  # get token-usage numbers in stream mode from OpenAI API,
@@ -67,6 +67,7 @@ def retry_with_exponential_backoff(
67
67
  for err in [
68
68
  "BadRequestError",
69
69
  "ConnectionError",
70
+ "NotFoundError",
70
71
  ]
71
72
  ):
72
73
  logger.error(f"OpenAI API request failed with error: {e}.")
@@ -137,6 +138,19 @@ def async_retry_with_exponential_backoff(
137
138
  raise e
138
139
  # Retry on specified errors
139
140
  except errors as e:
141
+ # For certain types of errors that slip through here
142
+ # (e.g. when using proxies like LiteLLM, do not retry)
143
+ if any(
144
+ err in str(e)
145
+ for err in [
146
+ "BadRequestError",
147
+ "ConnectionError",
148
+ "NotFoundError",
149
+ ]
150
+ ):
151
+ logger.error(f"OpenAI API request failed with error: {e}.")
152
+ raise e
153
+
140
154
  # Increment retries
141
155
  num_retries += 1
142
156
 
@@ -2,7 +2,7 @@ import tempfile
2
2
  from io import BytesIO
3
3
  from pathlib import Path
4
4
  from tempfile import TemporaryDirectory
5
- from typing import TYPE_CHECKING, Any, BinaryIO, List, Tuple, Union
5
+ from typing import TYPE_CHECKING, Any, BinaryIO, List, Optional, Tuple, Union
6
6
 
7
7
  try:
8
8
  import fitz
@@ -18,16 +18,23 @@ if fitz is None:
18
18
 
19
19
  def pdf_split_pages(
20
20
  input_pdf: Union[BytesIO, BinaryIO, str],
21
+ splits: Optional[List[int]] = None,
21
22
  ) -> Tuple[List[Path], TemporaryDirectory[Any]]:
22
- """Splits a PDF into individual pages in a temporary directory.
23
+ """Splits a PDF into individual pages or chunks in a temporary directory.
23
24
 
24
25
  Args:
25
26
  input_pdf: Input PDF file in bytes, binary mode, or a file path
27
+ splits: Optional list of page numbers to split at.
28
+ If provided, pages will be grouped into chunks ending at
29
+ these page numbers.
30
+ For example, if splits = [4, 9], the result will have pages 1-4, 5-9,
31
+ and 10-end.
32
+ If not provided, default to splitting into individual pages.
26
33
  max_workers: Maximum number of concurrent workers for parallel processing
27
34
 
28
35
  Returns:
29
36
  Tuple containing:
30
- - List of paths to individual PDF pages
37
+ - List of paths to individual PDF pages or chunks
31
38
  - Temporary directory object (caller must call cleanup())
32
39
 
33
40
  Example:
@@ -42,13 +49,41 @@ def pdf_split_pages(
42
49
  doc = fitz.open(stream=input_pdf, filetype="pdf")
43
50
  paths = []
44
51
 
45
- for page_num in range(len(doc)):
46
- new_doc = fitz.open()
47
- new_doc.insert_pdf(doc, from_page=page_num, to_page=page_num)
48
- output = Path(tmp_dir.name) / f"page_{page_num + 1}.pdf"
49
- new_doc.save(str(output))
50
- new_doc.close()
51
- paths.append(output)
52
+ total_pages = len(doc)
53
+
54
+ if splits is None:
55
+ # Split into individual pages (original behavior)
56
+ for page_num in range(total_pages):
57
+ new_doc = fitz.open()
58
+ new_doc.insert_pdf(doc, from_page=page_num, to_page=page_num)
59
+ output = Path(tmp_dir.name) / f"page_{page_num + 1}.pdf"
60
+ new_doc.save(str(output))
61
+ new_doc.close()
62
+ paths.append(output)
63
+ else:
64
+ # Split according to specified page ranges
65
+ # Make sure the splits list is sorted and includes all valid splits
66
+ splits = sorted([s for s in splits if 1 <= s <= total_pages])
67
+
68
+ # Create the ranges to process
69
+ ranges = []
70
+ start_page = 0
71
+ for end_page in splits:
72
+ ranges.append((start_page, end_page - 1))
73
+ start_page = end_page
74
+
75
+ # Add the final range if there are pages after the last split
76
+ if start_page < total_pages:
77
+ ranges.append((start_page, total_pages - 1))
78
+
79
+ # Process each range
80
+ for i, (from_page, to_page) in enumerate(ranges):
81
+ new_doc = fitz.open()
82
+ new_doc.insert_pdf(doc, from_page=from_page, to_page=to_page)
83
+ output = Path(tmp_dir.name) / f"pages_{from_page + 1}_to_{to_page + 1}.pdf"
84
+ new_doc.save(str(output))
85
+ new_doc.close()
86
+ paths.append(output)
52
87
 
53
88
  doc.close()
54
89
  return paths, tmp_dir
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.52.6
3
+ Version: 0.52.8
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -14,7 +14,7 @@ langroid/agent/xml_tool_message.py,sha256=6SshYZJKIfi4mkE-gIoSwjkEYekQ8GwcSiCv7a
14
14
  langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  langroid/agent/callbacks/chainlit.py,sha256=UHB6P_J40vsVnssosqkpkOVWRf9NK4TOY0_G2g_Arsg,20900
16
16
  langroid/agent/special/__init__.py,sha256=gik_Xtm_zV7U9s30Mn8UX3Gyuy4jTjQe9zjiE3HWmEo,1273
17
- langroid/agent/special/doc_chat_agent.py,sha256=-ABgZiaIowFVqYMYlU1Nf8WfOqXOTmlMCEnafEXucPo,65564
17
+ langroid/agent/special/doc_chat_agent.py,sha256=7PvVKHrXHw2LoSgU2-3hE7mz46r5oKB3o_bFhWmfT_I,65642
18
18
  langroid/agent/special/doc_chat_task.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
19
  langroid/agent/special/lance_doc_chat_agent.py,sha256=s8xoRs0gGaFtDYFUSIRchsgDVbS5Q3C2b2mr3V1Fd-Q,10419
20
20
  langroid/agent/special/lance_tools.py,sha256=qS8x4wi8mrqfbYV2ztFzrcxyhHQ0ZWOc-zkYiH7awj0,2105
@@ -72,8 +72,8 @@ langroid/language_models/base.py,sha256=pfN3t-BktKmN_4K8pwmpjC9OdcHxsytM5s5TmsJ-
72
72
  langroid/language_models/config.py,sha256=9Q8wk5a7RQr8LGMT_0WkpjY8S4ywK06SalVRjXlfCiI,378
73
73
  langroid/language_models/mock_lm.py,sha256=5BgHKDVRWFbUwDT_PFgTZXz9-k8wJSA2e3PZmyDgQ1k,4022
74
74
  langroid/language_models/model_info.py,sha256=vOaTi-XFKnz-BvHUvgjnt0XfOtl21Apev3Zy7Rhckbw,14458
75
- langroid/language_models/openai_gpt.py,sha256=Hw4llY9Dbox7L0sVP06q10ayoem8FOfXSfbLm3u55v4,85317
76
- langroid/language_models/utils.py,sha256=hC5p61P_Qlrowkm5wMap1A1b5ZUCwK_XhPIzAQk1T1s,5483
75
+ langroid/language_models/openai_gpt.py,sha256=F28jqTEerN32m14q3K0oc3vnvBT8J7Q9xqXGZNKUjKU,85938
76
+ langroid/language_models/utils.py,sha256=n55Oe2_V_4VNGhytvPWLYC-0tFS07RTjN83KWl-p_MI,6032
77
77
  langroid/language_models/prompt_formatter/__init__.py,sha256=2-5cdE24XoFDhifOLl8yiscohil1ogbP1ECkYdBlBsk,372
78
78
  langroid/language_models/prompt_formatter/base.py,sha256=eDS1sgRNZVnoajwV_ZIha6cba5Dt8xjgzdRbPITwx3Q,1221
79
79
  langroid/language_models/prompt_formatter/hf_formatter.py,sha256=PVJppmjRvD-2DF-XNC6mE05vTZ9wbu37SmXwZBQhad0,5055
@@ -87,7 +87,7 @@ langroid/parsing/md_parser.py,sha256=JUgsUpCaeAuBndmtDaJR9HMZaje1gmtXtaLXJHst3i8
87
87
  langroid/parsing/para_sentence_split.py,sha256=AJBzZojP3zpB-_IMiiHismhqcvkrVBQ3ZINoQyx_bE4,2000
88
88
  langroid/parsing/parse_json.py,sha256=aADo38bAHQhC8on4aWZZzVzSDy-dK35vRLZsFI2ewh8,4756
89
89
  langroid/parsing/parser.py,sha256=uaAITarcGI2504zcP_dLhp3LjNdh9A6R_yS-o_VcaH8,15599
90
- langroid/parsing/pdf_utils.py,sha256=9HnwhbZvpBUhW8WjY9OpGPKaIt3oe_a1AuqhWKqNQ6s,1616
90
+ langroid/parsing/pdf_utils.py,sha256=QogxU_B1N3WSLyZ9PEcJDaJoZShKs7CPQRVyF1V2DiE,3143
91
91
  langroid/parsing/repo_loader.py,sha256=NpysuyzRHvgL3F4BB_wGo5sCUnZ3FOlVCJmZ7CaUdbs,30202
92
92
  langroid/parsing/routing.py,sha256=-FcnlqldzL4ZoxuDwXjQPNHgBe9F9-F4R6q7b_z9CvI,1232
93
93
  langroid/parsing/search.py,sha256=0NJ5-Rou_BbrHAD7O9b20bKjZJnbadjObvGm4Zq8Kis,9818
@@ -129,7 +129,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
129
129
  langroid/vector_store/postgres.py,sha256=wHPtIi2qM4fhO4pMQr95pz1ZCe7dTb2hxl4VYspGZoA,16104
130
130
  langroid/vector_store/qdrantdb.py,sha256=O6dSBoDZ0jzfeVBd7LLvsXu083xs2fxXtPa9gGX3JX4,18443
131
131
  langroid/vector_store/weaviatedb.py,sha256=Yn8pg139gOy3zkaPfoTbMXEEBCiLiYa1MU5d_3UA1K4,11847
132
- langroid-0.52.6.dist-info/METADATA,sha256=fOf2NTPNfbS1F9rSfa7oLtYHB66xFiN65MnRWybC9sU,63519
133
- langroid-0.52.6.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
134
- langroid-0.52.6.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
135
- langroid-0.52.6.dist-info/RECORD,,
132
+ langroid-0.52.8.dist-info/METADATA,sha256=v0NjlHl9pXUfWPYH4-Gobzo4fYN9fcCpDaFG3cPFX-A,63519
133
+ langroid-0.52.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
134
+ langroid-0.52.8.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
135
+ langroid-0.52.8.dist-info/RECORD,,