langroid 0.27.4__py3-none-any.whl → 0.28.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 +35 -15
- langroid/language_models/openai_gpt.py +32 -1
- {langroid-0.27.4.dist-info → langroid-0.28.1.dist-info}/METADATA +3 -2
- {langroid-0.27.4.dist-info → langroid-0.28.1.dist-info}/RECORD +7 -7
- pyproject.toml +3 -2
- {langroid-0.27.4.dist-info → langroid-0.28.1.dist-info}/LICENSE +0 -0
- {langroid-0.27.4.dist-info → langroid-0.28.1.dist-info}/WHEEL +0 -0
langroid/agent/base.py
CHANGED
@@ -282,17 +282,27 @@ class Agent(ABC):
|
|
282
282
|
if not issubclass(message_class, ToolMessage):
|
283
283
|
raise ValueError("message_class must be a subclass of ToolMessage")
|
284
284
|
tool = message_class.default_value("request")
|
285
|
+
|
286
|
+
"""
|
287
|
+
if tool has handler method explicitly defined - use it,
|
288
|
+
otherwise use the tool name as the handler
|
289
|
+
"""
|
290
|
+
if hasattr(message_class, "_handler"):
|
291
|
+
handler = getattr(message_class, "_handler", tool)
|
292
|
+
else:
|
293
|
+
handler = tool
|
294
|
+
|
285
295
|
self.llm_tools_map[tool] = message_class
|
286
296
|
if (
|
287
297
|
hasattr(message_class, "handle")
|
288
298
|
and inspect.isfunction(message_class.handle)
|
289
|
-
and not hasattr(self,
|
299
|
+
and not hasattr(self, handler)
|
290
300
|
):
|
291
301
|
"""
|
292
302
|
If the message class has a `handle` method,
|
293
|
-
and agent does NOT have a
|
303
|
+
and agent does NOT have a tool handler method,
|
294
304
|
then we create a method for the agent whose name
|
295
|
-
is the value of `
|
305
|
+
is the value of `handler`, and whose body is the `handle` method.
|
296
306
|
This removes a separate step of having to define this method
|
297
307
|
for the agent, and also keeps the tool definition AND handling
|
298
308
|
in one place, i.e. in the message class.
|
@@ -302,21 +312,23 @@ class Agent(ABC):
|
|
302
312
|
len(inspect.signature(message_class.handle).parameters) > 1
|
303
313
|
)
|
304
314
|
if has_chat_doc_arg:
|
305
|
-
setattr(self,
|
315
|
+
setattr(self, handler, lambda obj, chat_doc: obj.handle(chat_doc))
|
306
316
|
else:
|
307
|
-
setattr(self,
|
317
|
+
setattr(self, handler, lambda obj: obj.handle())
|
308
318
|
elif (
|
309
319
|
hasattr(message_class, "response")
|
310
320
|
and inspect.isfunction(message_class.response)
|
311
|
-
and not hasattr(self,
|
321
|
+
and not hasattr(self, handler)
|
312
322
|
):
|
313
323
|
has_chat_doc_arg = (
|
314
324
|
len(inspect.signature(message_class.response).parameters) > 2
|
315
325
|
)
|
316
326
|
if has_chat_doc_arg:
|
317
|
-
setattr(
|
327
|
+
setattr(
|
328
|
+
self, handler, lambda obj, chat_doc: obj.response(self, chat_doc)
|
329
|
+
)
|
318
330
|
else:
|
319
|
-
setattr(self,
|
331
|
+
setattr(self, handler, lambda obj: obj.response(self))
|
320
332
|
|
321
333
|
if hasattr(message_class, "handle_message_fallback") and (
|
322
334
|
inspect.isfunction(message_class.handle_message_fallback)
|
@@ -327,11 +339,11 @@ class Agent(ABC):
|
|
327
339
|
lambda msg: message_class.handle_message_fallback(self, msg),
|
328
340
|
)
|
329
341
|
|
330
|
-
|
342
|
+
async_handler_name = f"{handler}_async"
|
331
343
|
if (
|
332
344
|
hasattr(message_class, "handle_async")
|
333
345
|
and inspect.isfunction(message_class.handle_async)
|
334
|
-
and not hasattr(self,
|
346
|
+
and not hasattr(self, async_handler_name)
|
335
347
|
):
|
336
348
|
has_chat_doc_arg = (
|
337
349
|
len(inspect.signature(message_class.handle_async).parameters) > 1
|
@@ -349,11 +361,11 @@ class Agent(ABC):
|
|
349
361
|
async def handler(obj):
|
350
362
|
return await obj.handle_async()
|
351
363
|
|
352
|
-
setattr(self,
|
364
|
+
setattr(self, async_handler_name, handler)
|
353
365
|
elif (
|
354
366
|
hasattr(message_class, "response_async")
|
355
367
|
and inspect.isfunction(message_class.response_async)
|
356
|
-
and not hasattr(self,
|
368
|
+
and not hasattr(self, async_handler_name)
|
357
369
|
):
|
358
370
|
has_chat_doc_arg = (
|
359
371
|
len(inspect.signature(message_class.response_async).parameters) > 2
|
@@ -371,7 +383,7 @@ class Agent(ABC):
|
|
371
383
|
async def handler(obj):
|
372
384
|
return await obj.response_async(self)
|
373
385
|
|
374
|
-
setattr(self,
|
386
|
+
setattr(self, async_handler_name, handler)
|
375
387
|
|
376
388
|
return [tool]
|
377
389
|
|
@@ -1742,7 +1754,11 @@ class Agent(ABC):
|
|
1742
1754
|
Asynch version of `handle_tool_message`. See there for details.
|
1743
1755
|
"""
|
1744
1756
|
tool_name = tool.default_value("request")
|
1745
|
-
|
1757
|
+
if hasattr(tool, "_handler"):
|
1758
|
+
handler_name = getattr(tool, "_handler", tool_name)
|
1759
|
+
else:
|
1760
|
+
handler_name = tool_name
|
1761
|
+
handler_method = getattr(self, handler_name + "_async", None)
|
1746
1762
|
if handler_method is None:
|
1747
1763
|
return self.handle_tool_message(tool, chat_doc=chat_doc)
|
1748
1764
|
has_chat_doc_arg = (
|
@@ -1781,7 +1797,11 @@ class Agent(ABC):
|
|
1781
1797
|
|
1782
1798
|
"""
|
1783
1799
|
tool_name = tool.default_value("request")
|
1784
|
-
|
1800
|
+
if hasattr(tool, "_handler"):
|
1801
|
+
handler_name = getattr(tool, "_handler", tool_name)
|
1802
|
+
else:
|
1803
|
+
handler_name = tool_name
|
1804
|
+
handler_method = getattr(self, handler_name, None)
|
1785
1805
|
if handler_method is None:
|
1786
1806
|
return None
|
1787
1807
|
has_chat_doc_arg = (
|
@@ -105,6 +105,7 @@ class GeminiModel(str, Enum):
|
|
105
105
|
GEMINI_1_5_FLASH = "gemini/gemini-1.5-flash"
|
106
106
|
GEMINI_1_5_FLASH_8B = "gemini/gemini-1.5-flash-8b"
|
107
107
|
GEMINI_1_5_PRO = "gemini/gemini-1.5-pro"
|
108
|
+
GEMINI_2_FLASH = "gemini/gemini-2.0-flash-exp"
|
108
109
|
|
109
110
|
|
110
111
|
class OpenAICompletionModel(str, Enum):
|
@@ -443,6 +444,7 @@ class OpenAIGPT(LanguageModel):
|
|
443
444
|
config = config.copy()
|
444
445
|
super().__init__(config)
|
445
446
|
self.config: OpenAIGPTConfig = config
|
447
|
+
self.chat_model_orig = self.config.chat_model
|
446
448
|
|
447
449
|
# Run the first time the model is used
|
448
450
|
self.run_on_first_use = cache(self.config.run_on_first_use)
|
@@ -451,6 +453,7 @@ class OpenAIGPT(LanguageModel):
|
|
451
453
|
# to allow quick testing with other models
|
452
454
|
if settings.chat_model != "":
|
453
455
|
self.config.chat_model = settings.chat_model
|
456
|
+
self.chat_model_orig = settings.chat_model
|
454
457
|
self.config.completion_model = settings.chat_model
|
455
458
|
|
456
459
|
if len(parts := self.config.chat_model.split("//")) > 1:
|
@@ -565,7 +568,7 @@ class OpenAIGPT(LanguageModel):
|
|
565
568
|
|
566
569
|
self.is_groq = self.config.chat_model.startswith("groq/")
|
567
570
|
self.is_cerebras = self.config.chat_model.startswith("cerebras/")
|
568
|
-
self.is_gemini = self.
|
571
|
+
self.is_gemini = self.is_gemini_model()
|
569
572
|
self.is_glhf = self.config.chat_model.startswith("glhf/")
|
570
573
|
self.is_openrouter = self.config.chat_model.startswith("openrouter/")
|
571
574
|
|
@@ -690,6 +693,20 @@ class OpenAIGPT(LanguageModel):
|
|
690
693
|
openai_completion_models = [e.value for e in OpenAICompletionModel]
|
691
694
|
return self.config.completion_model in openai_completion_models
|
692
695
|
|
696
|
+
def is_gemini_model(self) -> bool:
|
697
|
+
gemini_models = [e.value for e in GeminiModel]
|
698
|
+
return self.chat_model_orig in gemini_models or self.chat_model_orig.startswith(
|
699
|
+
"gemini/"
|
700
|
+
)
|
701
|
+
|
702
|
+
def requires_first_user_message(self) -> bool:
|
703
|
+
"""
|
704
|
+
Does the chat_model require a non-empty first user message?
|
705
|
+
TODO: Add other models here; we know gemini requires a non-empty
|
706
|
+
user message, after the system message.
|
707
|
+
"""
|
708
|
+
return self.is_gemini_model()
|
709
|
+
|
693
710
|
def unsupported_params(self) -> List[str]:
|
694
711
|
"""
|
695
712
|
List of params that are not supported by the current model
|
@@ -1663,6 +1680,20 @@ class OpenAIGPT(LanguageModel):
|
|
1663
1680
|
]
|
1664
1681
|
else:
|
1665
1682
|
llm_messages = messages
|
1683
|
+
if (
|
1684
|
+
len(llm_messages) == 1
|
1685
|
+
and llm_messages[0].role == Role.SYSTEM
|
1686
|
+
and self.requires_first_user_message()
|
1687
|
+
):
|
1688
|
+
# some LLMs, notable Gemini as of 12/11/24,
|
1689
|
+
# require the first message to be from the user,
|
1690
|
+
# so insert a dummy user msg if needed.
|
1691
|
+
llm_messages.insert(
|
1692
|
+
1,
|
1693
|
+
LLMMessage(
|
1694
|
+
role=Role.USER, content="Follow the above instructions."
|
1695
|
+
),
|
1696
|
+
)
|
1666
1697
|
|
1667
1698
|
# Azure uses different parameters. It uses ``engine`` instead of ``model``
|
1668
1699
|
# and the value should be the deployment_name not ``self.config.chat_model``
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.28.1
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
License: MIT
|
6
6
|
Author: Prasad Chalasani
|
@@ -83,8 +83,9 @@ Requires-Dist: pygments (>=2.15.1,<3.0.0)
|
|
83
83
|
Requires-Dist: pymupdf (>=1.23.3,<2.0.0) ; extra == "doc-chat" or extra == "all" or extra == "pdf-parsers"
|
84
84
|
Requires-Dist: pymysql (>=1.1.0,<2.0.0) ; extra == "db" or extra == "all" or extra == "mysql" or extra == "sql"
|
85
85
|
Requires-Dist: pyparsing (>=3.0.9,<4.0.0)
|
86
|
-
Requires-Dist: pypdf (>=
|
86
|
+
Requires-Dist: pypdf (>=5.1.0) ; extra == "doc-chat" or extra == "all" or extra == "pdf-parsers"
|
87
87
|
Requires-Dist: pytesseract (>=0.3.10,<0.4.0) ; extra == "doc-chat" or extra == "all" or extra == "pdf-parsers"
|
88
|
+
Requires-Dist: pytest-rerunfailures (>=15.0,<16.0)
|
88
89
|
Requires-Dist: python-arango (>=8.1.2,<9.0.0) ; extra == "all" or extra == "arango"
|
89
90
|
Requires-Dist: python-docx (>=1.1.0,<2.0.0) ; extra == "doc-chat" or extra == "all" or extra == "docx"
|
90
91
|
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
@@ -1,6 +1,6 @@
|
|
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=
|
3
|
+
langroid/agent/base.py,sha256=ZgWsRBC9rugcWp9aZLAmFFteU47pqKIEoTy_dgkYtBI,77529
|
4
4
|
langroid/agent/batch.py,sha256=qK3ph6VNj_1sOhfXCZY4r6gh035DglDKU751p8BU0tY,14665
|
5
5
|
langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
langroid/agent/callbacks/chainlit.py,sha256=C6zzzYC30qC4eMA7al7eFpRoTgoe3475kaMKyXgQM0Q,20695
|
@@ -75,7 +75,7 @@ langroid/language_models/azure_openai.py,sha256=DdWq_pRFI9PT5LwUy9TXDox90TY-mkP5
|
|
75
75
|
langroid/language_models/base.py,sha256=6hXR-bclyPif-BvFbyXevP-gEwiawQAJHX3N1AKNei0,23786
|
76
76
|
langroid/language_models/config.py,sha256=9Q8wk5a7RQr8LGMT_0WkpjY8S4ywK06SalVRjXlfCiI,378
|
77
77
|
langroid/language_models/mock_lm.py,sha256=5BgHKDVRWFbUwDT_PFgTZXz9-k8wJSA2e3PZmyDgQ1k,4022
|
78
|
-
langroid/language_models/openai_gpt.py,sha256=
|
78
|
+
langroid/language_models/openai_gpt.py,sha256=IPZ5bOyfQO-pWHharQ5fMsBuW87Cpwfy1Ywce6ARbfQ,75204
|
79
79
|
langroid/language_models/prompt_formatter/__init__.py,sha256=2-5cdE24XoFDhifOLl8yiscohil1ogbP1ECkYdBlBsk,372
|
80
80
|
langroid/language_models/prompt_formatter/base.py,sha256=eDS1sgRNZVnoajwV_ZIha6cba5Dt8xjgzdRbPITwx3Q,1221
|
81
81
|
langroid/language_models/prompt_formatter/hf_formatter.py,sha256=PVJppmjRvD-2DF-XNC6mE05vTZ9wbu37SmXwZBQhad0,5055
|
@@ -142,8 +142,8 @@ langroid/vector_store/meilisearch.py,sha256=6frB7GFWeWmeKzRfLZIvzRjllniZ1cYj3Hmh
|
|
142
142
|
langroid/vector_store/momento.py,sha256=UNHGT6jXuQtqY9f6MdqGU14bVnS0zHgIJUa30ULpUJo,10474
|
143
143
|
langroid/vector_store/qdrant_cloud.py,sha256=3im4Mip0QXLkR6wiqVsjV1QvhSElfxdFSuDKddBDQ-4,188
|
144
144
|
langroid/vector_store/qdrantdb.py,sha256=v7mCsijc2GdRJyil-yFaUVAX4SX5D75mD3vzlpjCMuo,17393
|
145
|
-
pyproject.toml,sha256=
|
146
|
-
langroid-0.
|
147
|
-
langroid-0.
|
148
|
-
langroid-0.
|
149
|
-
langroid-0.
|
145
|
+
pyproject.toml,sha256=YO18NE-r-Ark_n-cjgvflJWmA3uqXXo2Ee5dQzT_Qw8,7532
|
146
|
+
langroid-0.28.1.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
147
|
+
langroid-0.28.1.dist-info/METADATA,sha256=IExM86cC-ymkK90BCMiULmuodg9E7amGcyi0g3aN_j0,57569
|
148
|
+
langroid-0.28.1.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
149
|
+
langroid-0.28.1.dist-info/RECORD,,
|
pyproject.toml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "langroid"
|
3
|
-
version = "0.
|
3
|
+
version = "0.28.1"
|
4
4
|
description = "Harness LLMs with Multi-Agent Programming"
|
5
5
|
authors = ["Prasad Chalasani <pchalasani@gmail.com>"]
|
6
6
|
readme = "README.md"
|
@@ -29,7 +29,7 @@ huggingface-hub = {version="^0.21.2", optional=true}
|
|
29
29
|
transformers = {version="^4.40.1", optional=true}
|
30
30
|
lancedb = {version="^0.8.2", optional=true}
|
31
31
|
tantivy = {version="^0.21.0", optional=true}
|
32
|
-
pypdf = {version="
|
32
|
+
pypdf = {version=">=5.1.0", optional=true}
|
33
33
|
pymupdf = {version="^1.23.3", optional=true}
|
34
34
|
pdf2image = {version="^1.17.0", optional=true}
|
35
35
|
pytesseract = {version="^0.3.10", optional=true}
|
@@ -93,6 +93,7 @@ gitpython = "^3.1.43"
|
|
93
93
|
python-arango = {version="^8.1.2", optional=true}
|
94
94
|
arango-datasets = {version="^1.2.2", optional=true}
|
95
95
|
adb-cloud-connector = "^1.0.2"
|
96
|
+
pytest-rerunfailures = "^15.0"
|
96
97
|
|
97
98
|
|
98
99
|
[tool.poetry.extras]
|
File without changes
|
File without changes
|