langroid 0.48.3__py3-none-any.whl → 0.49.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/language_models/openai_gpt.py +76 -8
- langroid/mytypes.py +25 -1
- langroid/utils/output/citations.py +2 -2
- {langroid-0.48.3.dist-info → langroid-0.49.1.dist-info}/METADATA +1 -1
- {langroid-0.48.3.dist-info → langroid-0.49.1.dist-info}/RECORD +7 -7
- {langroid-0.48.3.dist-info → langroid-0.49.1.dist-info}/WHEEL +0 -0
- {langroid-0.48.3.dist-info → langroid-0.49.1.dist-info}/licenses/LICENSE +0 -0
@@ -227,6 +227,16 @@ class OpenAICallParams(BaseModel):
|
|
227
227
|
return {k: v for k, v in self.dict().items() if v is not None}
|
228
228
|
|
229
229
|
|
230
|
+
class LiteLLMProxyConfig(BaseSettings):
|
231
|
+
"""Configuration for LiteLLM proxy connection."""
|
232
|
+
|
233
|
+
api_key: str = "" # read from env var LITELLM_API_KEY if set
|
234
|
+
api_base: str = "" # read from env var LITELLM_API_BASE if set
|
235
|
+
|
236
|
+
class Config:
|
237
|
+
env_prefix = "LITELLM_"
|
238
|
+
|
239
|
+
|
230
240
|
class OpenAIGPTConfig(LLMConfig):
|
231
241
|
"""
|
232
242
|
Class for any LLM with an OpenAI-like API: besides the OpenAI models this includes:
|
@@ -250,6 +260,7 @@ class OpenAIGPTConfig(LLMConfig):
|
|
250
260
|
organization: str = ""
|
251
261
|
api_base: str | None = None # used for local or other non-OpenAI models
|
252
262
|
litellm: bool = False # use litellm api?
|
263
|
+
litellm_proxy: LiteLLMProxyConfig = LiteLLMProxyConfig()
|
253
264
|
ollama: bool = False # use ollama's OpenAI-compatible endpoint?
|
254
265
|
min_output_tokens: int = 1
|
255
266
|
use_chat_for_completion = True # do not change this, for OpenAI models!
|
@@ -520,6 +531,7 @@ class OpenAIGPT(LanguageModel):
|
|
520
531
|
self.is_glhf = self.config.chat_model.startswith("glhf/")
|
521
532
|
self.is_openrouter = self.config.chat_model.startswith("openrouter/")
|
522
533
|
self.is_langdb = self.config.chat_model.startswith("langdb/")
|
534
|
+
self.is_litellm_proxy = self.config.chat_model.startswith("litellm-proxy/")
|
523
535
|
|
524
536
|
if self.is_groq:
|
525
537
|
# use groq-specific client
|
@@ -546,7 +558,14 @@ class OpenAIGPT(LanguageModel):
|
|
546
558
|
)
|
547
559
|
else:
|
548
560
|
# in these cases, there's no specific client: OpenAI python client suffices
|
549
|
-
if self.
|
561
|
+
if self.is_litellm_proxy:
|
562
|
+
self.config.chat_model = self.config.chat_model.replace(
|
563
|
+
"litellm-proxy/", ""
|
564
|
+
)
|
565
|
+
if self.api_key == OPENAI_API_KEY:
|
566
|
+
self.api_key = self.config.litellm_proxy.api_key or self.api_key
|
567
|
+
self.api_base = self.config.litellm_proxy.api_base or self.api_base
|
568
|
+
elif self.is_gemini:
|
550
569
|
self.config.chat_model = self.config.chat_model.replace("gemini/", "")
|
551
570
|
if self.api_key == OPENAI_API_KEY:
|
552
571
|
self.api_key = os.getenv("GEMINI_API_KEY", DUMMY_API_KEY)
|
@@ -1040,8 +1059,8 @@ class OpenAIGPT(LanguageModel):
|
|
1040
1059
|
)
|
1041
1060
|
if is_break:
|
1042
1061
|
break
|
1043
|
-
except Exception:
|
1044
|
-
|
1062
|
+
except Exception as e:
|
1063
|
+
logging.warning("Error while processing stream response: %s", str(e))
|
1045
1064
|
|
1046
1065
|
print("")
|
1047
1066
|
# TODO- get usage info in stream mode (?)
|
@@ -1102,8 +1121,8 @@ class OpenAIGPT(LanguageModel):
|
|
1102
1121
|
)
|
1103
1122
|
if is_break:
|
1104
1123
|
break
|
1105
|
-
except Exception:
|
1106
|
-
|
1124
|
+
except Exception as e:
|
1125
|
+
logging.warning("Error while processing stream response: %s", str(e))
|
1107
1126
|
|
1108
1127
|
print("")
|
1109
1128
|
# TODO- get usage info in stream mode (?)
|
@@ -1684,11 +1703,32 @@ class OpenAIGPT(LanguageModel):
|
|
1684
1703
|
if self.config.litellm and settings.debug:
|
1685
1704
|
kwargs["logger_fn"] = litellm_logging_fn
|
1686
1705
|
result = completion_call(**kwargs)
|
1687
|
-
|
1688
|
-
|
1706
|
+
|
1707
|
+
if self.get_stream():
|
1708
|
+
# If streaming, cannot cache result
|
1689
1709
|
# since it is a generator. Instead,
|
1690
1710
|
# we hold on to the hashed_key and
|
1691
1711
|
# cache the result later
|
1712
|
+
|
1713
|
+
# Test if this is a stream with an exception by
|
1714
|
+
# trying to get first chunk: Some providers like LiteLLM
|
1715
|
+
# produce a valid stream object `result` instead of throwing a
|
1716
|
+
# rate-limit error, and if we don't catch it here,
|
1717
|
+
# we end up returning an empty response and not
|
1718
|
+
# using the retry mechanism in the decorator.
|
1719
|
+
try:
|
1720
|
+
# try to get the first chunk to check for errors
|
1721
|
+
test_iter = iter(result)
|
1722
|
+
first_chunk = next(test_iter)
|
1723
|
+
# If we get here without error, recreate the stream
|
1724
|
+
result = chain([first_chunk], test_iter)
|
1725
|
+
except StopIteration:
|
1726
|
+
# Empty stream is fine
|
1727
|
+
pass
|
1728
|
+
except Exception as e:
|
1729
|
+
# Propagate any errors in the stream
|
1730
|
+
raise e
|
1731
|
+
else:
|
1692
1732
|
self._cache_store(hashed_key, result.model_dump())
|
1693
1733
|
return cached, hashed_key, result
|
1694
1734
|
|
@@ -1715,7 +1755,35 @@ class OpenAIGPT(LanguageModel):
|
|
1715
1755
|
kwargs["logger_fn"] = litellm_logging_fn
|
1716
1756
|
# If it's not in the cache, call the API
|
1717
1757
|
result = await acompletion_call(**kwargs)
|
1718
|
-
if
|
1758
|
+
if self.get_stream():
|
1759
|
+
try:
|
1760
|
+
# Try to peek at the first chunk to immediately catch any errors
|
1761
|
+
# Store the original result (the stream)
|
1762
|
+
original_stream = result
|
1763
|
+
|
1764
|
+
# Manually create and advance the iterator to check for errors
|
1765
|
+
stream_iter = original_stream.__aiter__()
|
1766
|
+
try:
|
1767
|
+
# This will raise an exception if the stream is invalid
|
1768
|
+
first_chunk = await anext(stream_iter)
|
1769
|
+
|
1770
|
+
# If we reach here, the stream started successfully
|
1771
|
+
# Now recreate a fresh stream from the original API result
|
1772
|
+
# Otherwise, return a new stream that yields the first chunk
|
1773
|
+
# and remaining items
|
1774
|
+
async def combined_stream(): # type: ignore
|
1775
|
+
yield first_chunk
|
1776
|
+
async for chunk in stream_iter:
|
1777
|
+
yield chunk
|
1778
|
+
|
1779
|
+
result = combined_stream() # type: ignore
|
1780
|
+
except StopAsyncIteration:
|
1781
|
+
# Empty stream is normal - nothing to do
|
1782
|
+
pass
|
1783
|
+
except Exception as e:
|
1784
|
+
# Any exception here should be raised to trigger the retry mechanism
|
1785
|
+
raise e
|
1786
|
+
else:
|
1719
1787
|
self._cache_store(hashed_key, result.model_dump())
|
1720
1788
|
return cached, hashed_key, result
|
1721
1789
|
|
langroid/mytypes.py
CHANGED
@@ -65,6 +65,30 @@ class DocMetaData(BaseModel):
|
|
65
65
|
|
66
66
|
return original_dict
|
67
67
|
|
68
|
+
def __str__(self) -> str:
|
69
|
+
title_str = (
|
70
|
+
""
|
71
|
+
if "unknown" in self.title.lower() or self.title.strip() == ""
|
72
|
+
else f"Title: {self.title}"
|
73
|
+
)
|
74
|
+
date_str = ""
|
75
|
+
if (
|
76
|
+
"unknown" not in self.published_date.lower()
|
77
|
+
and self.published_date.strip() != ""
|
78
|
+
):
|
79
|
+
try:
|
80
|
+
from dateutil import parser
|
81
|
+
|
82
|
+
# Try to parse the date string
|
83
|
+
date_obj = parser.parse(self.published_date)
|
84
|
+
# Format to include only the date part (year-month-day)
|
85
|
+
date_only = date_obj.strftime("%Y-%m-%d")
|
86
|
+
date_str = f"Date: {date_only}"
|
87
|
+
except (ValueError, ImportError, TypeError):
|
88
|
+
# If parsing fails, just use the original date
|
89
|
+
date_str = f"Date: {self.published_date}"
|
90
|
+
return f"{self.source} {title_str} {date_str}".strip()
|
91
|
+
|
68
92
|
class Config:
|
69
93
|
extra = Extra.allow
|
70
94
|
|
@@ -93,7 +117,7 @@ class Document(BaseModel):
|
|
93
117
|
return dedent(
|
94
118
|
f"""
|
95
119
|
CONTENT: {self.content}
|
96
|
-
SOURCE:{self.metadata
|
120
|
+
SOURCE:{str(self.metadata)}
|
97
121
|
"""
|
98
122
|
)
|
99
123
|
|
@@ -97,7 +97,7 @@ def format_cited_references(
|
|
97
97
|
# source and content for each citation
|
98
98
|
full_citations_str = "\n".join(
|
99
99
|
[
|
100
|
-
f"[^{c}] {passages[c-1].metadata
|
100
|
+
f"[^{c}] {str(passages[c-1].metadata)}"
|
101
101
|
f"\n{format_footnote_text(passages[c-1].content)}"
|
102
102
|
for c in good_citations
|
103
103
|
]
|
@@ -105,6 +105,6 @@ def format_cited_references(
|
|
105
105
|
|
106
106
|
# source for each citation
|
107
107
|
citations_str = "\n".join(
|
108
|
-
[f"[^{c}] {passages[c-1].metadata
|
108
|
+
[f"[^{c}] {str(passages[c-1].metadata)}" for c in good_citations]
|
109
109
|
)
|
110
110
|
return full_citations_str, citations_str
|
@@ -1,6 +1,6 @@
|
|
1
1
|
langroid/__init__.py,sha256=z_fCOLQJPOw3LLRPBlFB5-2HyCjpPgQa4m4iY5Fvb8Y,1800
|
2
2
|
langroid/exceptions.py,sha256=OPjece_8cwg94DLPcOGA1ddzy5bGh65pxzcHMnssTz8,2995
|
3
|
-
langroid/mytypes.py,sha256=
|
3
|
+
langroid/mytypes.py,sha256=ezj_6FFDkJZiVx1SS9eJvh23dH76Ti7mJbePi8ldkAI,3919
|
4
4
|
langroid/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
langroid/agent/__init__.py,sha256=ll0Cubd2DZ-fsCMl7e10hf9ZjFGKzphfBco396IKITY,786
|
6
6
|
langroid/agent/base.py,sha256=U-UjdpxIFqkzRIB5-LYwHrhMSNI3sDbfnNRqIhrtsyI,79568
|
@@ -72,7 +72,7 @@ langroid/language_models/base.py,sha256=mDYmFCBCLdq8_Uvws4MiewwEgcOCP8Qb0e5yUXr3
|
|
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=tfBBxL0iUf2mVN6CjcvqflzFUVg2oZqOJZexZ8jHTYA,12216
|
75
|
-
langroid/language_models/openai_gpt.py,sha256=
|
75
|
+
langroid/language_models/openai_gpt.py,sha256=M_jp97Ksp5r3U-d0jCLPLjVmn7IK1mC8Ry4t7k6A5tc,82906
|
76
76
|
langroid/language_models/utils.py,sha256=L4_CbihDMTGcsg0TOG1Yd5JFEto46--h7CX_14m89sQ,5016
|
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
|
@@ -115,7 +115,7 @@ langroid/utils/types.py,sha256=-BvyIf_LmAJ5jR9NC7S4CSVNEr3XayAaxJ5o0TiIej0,2992
|
|
115
115
|
langroid/utils/algorithms/__init__.py,sha256=WylYoZymA0fnzpB4vrsH_0n7WsoLhmuZq8qxsOCjUpM,41
|
116
116
|
langroid/utils/algorithms/graph.py,sha256=JbdpPnUOhw4-D6O7ou101JLA3xPCD0Lr3qaPoFCaRfo,2866
|
117
117
|
langroid/utils/output/__init__.py,sha256=7P0f--4IZneNsTxXY5fd6d6iW-CeVe-KSsl-87sbBPc,340
|
118
|
-
langroid/utils/output/citations.py,sha256=
|
118
|
+
langroid/utils/output/citations.py,sha256=cEiqSH7DJ5q4M2z_6eFjCj9Ohnf68i6sivjeRFuFAtk,3862
|
119
119
|
langroid/utils/output/printing.py,sha256=yzPJZN-8_jyOJmI9N_oLwEDfjMwVgk3IDiwnZ4eK_AE,2962
|
120
120
|
langroid/utils/output/status.py,sha256=rzbE7mDJcgNNvdtylCseQcPGCGghtJvVq3lB-OPJ49E,1049
|
121
121
|
langroid/vector_store/__init__.py,sha256=8ktJUVsVUoc7FMmkUFpFBZu7VMWUqQY9zpm4kEJ8yTs,1537
|
@@ -127,7 +127,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
|
|
127
127
|
langroid/vector_store/postgres.py,sha256=wHPtIi2qM4fhO4pMQr95pz1ZCe7dTb2hxl4VYspGZoA,16104
|
128
128
|
langroid/vector_store/qdrantdb.py,sha256=O6dSBoDZ0jzfeVBd7LLvsXu083xs2fxXtPa9gGX3JX4,18443
|
129
129
|
langroid/vector_store/weaviatedb.py,sha256=Yn8pg139gOy3zkaPfoTbMXEEBCiLiYa1MU5d_3UA1K4,11847
|
130
|
-
langroid-0.
|
131
|
-
langroid-0.
|
132
|
-
langroid-0.
|
133
|
-
langroid-0.
|
130
|
+
langroid-0.49.1.dist-info/METADATA,sha256=a2cArSN5YfRq4GRH37MkO6h-fvXbXEFkoo-qDMyVTzA,63606
|
131
|
+
langroid-0.49.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
132
|
+
langroid-0.49.1.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
133
|
+
langroid-0.49.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|