langroid 0.42.9__py3-none-any.whl → 0.42.10__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 +23 -13
- langroid/agent/chat_agent.py +6 -1
- langroid/agent/task.py +4 -1
- langroid/language_models/openai_gpt.py +15 -17
- langroid/parsing/document_parser.py +4 -2
- {langroid-0.42.9.dist-info → langroid-0.42.10.dist-info}/METADATA +1 -1
- {langroid-0.42.9.dist-info → langroid-0.42.10.dist-info}/RECORD +9 -9
- {langroid-0.42.9.dist-info → langroid-0.42.10.dist-info}/WHEEL +0 -0
- {langroid-0.42.9.dist-info → langroid-0.42.10.dist-info}/licenses/LICENSE +0 -0
langroid/agent/base.py
CHANGED
@@ -1148,7 +1148,9 @@ class Agent(ABC):
|
|
1148
1148
|
and msg.function_call is None
|
1149
1149
|
):
|
1150
1150
|
|
1151
|
-
tools = self.get_formatted_tool_messages(
|
1151
|
+
tools = self.get_formatted_tool_messages(
|
1152
|
+
msg.content, from_llm=msg.metadata.sender == Entity.LLM
|
1153
|
+
)
|
1152
1154
|
msg.all_tool_messages = tools
|
1153
1155
|
# filter for actually handle-able tools, and recipient is this agent
|
1154
1156
|
my_tools = [t for t in tools if self._tool_recipient_match(t)]
|
@@ -1177,7 +1179,9 @@ class Agent(ABC):
|
|
1177
1179
|
else:
|
1178
1180
|
return my_tools
|
1179
1181
|
|
1180
|
-
def get_formatted_tool_messages(
|
1182
|
+
def get_formatted_tool_messages(
|
1183
|
+
self, input_str: str, from_llm: bool = True
|
1184
|
+
) -> List[ToolMessage]:
|
1181
1185
|
"""
|
1182
1186
|
Returns ToolMessage objects (tools) corresponding to
|
1183
1187
|
tool-formatted substrings, if any.
|
@@ -1190,6 +1194,8 @@ class Agent(ABC):
|
|
1190
1194
|
|
1191
1195
|
Args:
|
1192
1196
|
input_str (str): input string, typically a message sent by an LLM
|
1197
|
+
from_llm (bool): whether the input was generated by the LLM. If so,
|
1198
|
+
we track malformed tool calls.
|
1193
1199
|
|
1194
1200
|
Returns:
|
1195
1201
|
List[ToolMessage]: list of ToolMessage objects
|
@@ -1203,7 +1209,7 @@ class Agent(ABC):
|
|
1203
1209
|
if not is_json:
|
1204
1210
|
return []
|
1205
1211
|
|
1206
|
-
results = [self._get_one_tool_message(j, is_json) for j in substrings]
|
1212
|
+
results = [self._get_one_tool_message(j, is_json, from_llm) for j in substrings]
|
1207
1213
|
valid_results = [r for r in results if r is not None]
|
1208
1214
|
# If any tool is correctly formed we do not set the flag
|
1209
1215
|
if len(valid_results) > 0:
|
@@ -1219,6 +1225,7 @@ class Agent(ABC):
|
|
1219
1225
|
return None
|
1220
1226
|
tool_name = msg.function_call.name
|
1221
1227
|
tool_msg = msg.function_call.arguments or {}
|
1228
|
+
self.tool_error = False
|
1222
1229
|
if tool_name not in self.llm_tools_handled:
|
1223
1230
|
logger.warning(
|
1224
1231
|
f"""
|
@@ -1230,10 +1237,12 @@ class Agent(ABC):
|
|
1230
1237
|
or you need to enable this agent to handle this fn-call.
|
1231
1238
|
"""
|
1232
1239
|
)
|
1233
|
-
if
|
1240
|
+
if (
|
1241
|
+
tool_name not in self.all_llm_tools_known
|
1242
|
+
and msg.metadata.sender == Entity.LLM
|
1243
|
+
):
|
1234
1244
|
self.tool_error = True
|
1235
1245
|
return None
|
1236
|
-
self.tool_error = False
|
1237
1246
|
tool_class = self.llm_tools_map[tool_name]
|
1238
1247
|
tool_msg.update(dict(request=tool_name))
|
1239
1248
|
tool = tool_class.parse_obj(tool_msg)
|
@@ -1272,8 +1281,9 @@ class Agent(ABC):
|
|
1272
1281
|
tool = tool_class.parse_obj(tool_msg)
|
1273
1282
|
tool.id = tc.id or ""
|
1274
1283
|
tools.append(tool)
|
1275
|
-
# When no tool is valid
|
1276
|
-
|
1284
|
+
# When no tool is valid and the message was produced
|
1285
|
+
# by the LLM, set the recovery flag
|
1286
|
+
self.tool_error = all_errors and msg.metadata.sender == Entity.LLM
|
1277
1287
|
return tools
|
1278
1288
|
|
1279
1289
|
def tool_validation_error(self, ve: ValidationError) -> str:
|
@@ -1508,7 +1518,7 @@ class Agent(ABC):
|
|
1508
1518
|
return None
|
1509
1519
|
|
1510
1520
|
def _get_one_tool_message(
|
1511
|
-
self, tool_candidate_str: str, is_json: bool = True
|
1521
|
+
self, tool_candidate_str: str, is_json: bool = True, from_llm: bool = True
|
1512
1522
|
) -> Optional[ToolMessage]:
|
1513
1523
|
"""
|
1514
1524
|
Parse the tool_candidate_str into ANY ToolMessage KNOWN to agent --
|
@@ -1545,7 +1555,7 @@ class Agent(ABC):
|
|
1545
1555
|
# }
|
1546
1556
|
|
1547
1557
|
if not isinstance(maybe_tool_dict, dict):
|
1548
|
-
self.tool_error =
|
1558
|
+
self.tool_error = from_llm
|
1549
1559
|
return None
|
1550
1560
|
|
1551
1561
|
properties = maybe_tool_dict.get("properties")
|
@@ -1593,23 +1603,23 @@ class Agent(ABC):
|
|
1593
1603
|
if len(candidate_tools) == 1:
|
1594
1604
|
return candidate_tools[0]
|
1595
1605
|
else:
|
1596
|
-
self.tool_error =
|
1606
|
+
self.tool_error = from_llm
|
1597
1607
|
return None
|
1598
1608
|
|
1599
1609
|
if not isinstance(request, str) or request not in self.all_llm_tools_known:
|
1600
|
-
self.tool_error =
|
1610
|
+
self.tool_error = from_llm
|
1601
1611
|
return None
|
1602
1612
|
|
1603
1613
|
message_class = self.llm_tools_map.get(request)
|
1604
1614
|
if message_class is None:
|
1605
1615
|
logger.warning(f"No message class found for request '{request}'")
|
1606
|
-
self.tool_error =
|
1616
|
+
self.tool_error = from_llm
|
1607
1617
|
return None
|
1608
1618
|
|
1609
1619
|
try:
|
1610
1620
|
message = message_class.parse_obj(maybe_tool_dict)
|
1611
1621
|
except ValidationError as ve:
|
1612
|
-
self.tool_error =
|
1622
|
+
self.tool_error = from_llm
|
1613
1623
|
raise ve
|
1614
1624
|
return message
|
1615
1625
|
|
langroid/agent/chat_agent.py
CHANGED
@@ -1096,7 +1096,10 @@ class ChatAgent(Agent):
|
|
1096
1096
|
else:
|
1097
1097
|
# We will trigger the strict recovery mechanism to force
|
1098
1098
|
# the LLM to correct its output, allowing us to parse
|
1099
|
-
|
1099
|
+
if isinstance(msg, ChatDocument):
|
1100
|
+
self.tool_error = msg.metadata.sender == Entity.LLM
|
1101
|
+
else:
|
1102
|
+
self.tool_error = True
|
1100
1103
|
|
1101
1104
|
raise ve
|
1102
1105
|
|
@@ -1265,6 +1268,7 @@ class ChatAgent(Agent):
|
|
1265
1268
|
and self._json_schema_available()
|
1266
1269
|
and self.config.strict_recovery
|
1267
1270
|
):
|
1271
|
+
self.tool_error = False
|
1268
1272
|
AnyTool = self._get_any_tool_message()
|
1269
1273
|
if AnyTool is None:
|
1270
1274
|
return None
|
@@ -1352,6 +1356,7 @@ class ChatAgent(Agent):
|
|
1352
1356
|
and self._json_schema_available()
|
1353
1357
|
and self.config.strict_recovery
|
1354
1358
|
):
|
1359
|
+
self.tool_error = False
|
1355
1360
|
AnyTool = self._get_any_tool_message()
|
1356
1361
|
self.set_output_format(
|
1357
1362
|
AnyTool,
|
langroid/agent/task.py
CHANGED
@@ -1572,7 +1572,10 @@ class Task:
|
|
1572
1572
|
response_fn = self._entity_responder_async_map[cast(Entity, e)]
|
1573
1573
|
result = await response_fn(self.pending_message)
|
1574
1574
|
# update result.tool_messages if any
|
1575
|
-
if
|
1575
|
+
if (
|
1576
|
+
isinstance(result, ChatDocument)
|
1577
|
+
and result.metadata.sender == Entity.LLM
|
1578
|
+
):
|
1576
1579
|
self.agent.try_get_tool_messages(result)
|
1577
1580
|
|
1578
1581
|
result_chat_doc = self.agent.to_ChatDocument(
|
@@ -85,9 +85,6 @@ GLHF_BASE_URL = "https://glhf.chat/api/openai/v1"
|
|
85
85
|
OLLAMA_API_KEY = "ollama"
|
86
86
|
DUMMY_API_KEY = "xxx"
|
87
87
|
|
88
|
-
VLLM_API_KEY = os.environ.get("VLLM_API_KEY", DUMMY_API_KEY)
|
89
|
-
LLAMACPP_API_KEY = os.environ.get("LLAMA_API_KEY", DUMMY_API_KEY)
|
90
|
-
|
91
88
|
|
92
89
|
openai_chat_model_pref_list = [
|
93
90
|
OpenAIChatModel.GPT4o,
|
@@ -421,6 +418,9 @@ class OpenAIGPT(LanguageModel):
|
|
421
418
|
self.supports_json_schema: bool = self.config.supports_json_schema or False
|
422
419
|
self.supports_strict_tools: bool = self.config.supports_strict_tools or False
|
423
420
|
|
421
|
+
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", DUMMY_API_KEY)
|
422
|
+
self.api_key = config.api_key
|
423
|
+
|
424
424
|
# if model name starts with "litellm",
|
425
425
|
# set the actual model name by stripping the "litellm/" prefix
|
426
426
|
# and set the litellm flag to True
|
@@ -449,12 +449,14 @@ class OpenAIGPT(LanguageModel):
|
|
449
449
|
|
450
450
|
# use api_base from config if set, else fall back on OLLAMA_BASE_URL
|
451
451
|
self.api_base = self.config.api_base or OLLAMA_BASE_URL
|
452
|
-
self.api_key
|
452
|
+
if self.api_key == OPENAI_API_KEY:
|
453
|
+
self.api_key = OLLAMA_API_KEY
|
453
454
|
self.config.chat_model = self.config.chat_model.replace("ollama/", "")
|
454
455
|
elif self.config.chat_model.startswith("vllm/"):
|
455
456
|
self.supports_json_schema = True
|
456
457
|
self.config.chat_model = self.config.chat_model.replace("vllm/", "")
|
457
|
-
self.api_key
|
458
|
+
if self.api_key == OPENAI_API_KEY:
|
459
|
+
self.api_key = os.environ.get("VLLM_API_KEY", DUMMY_API_KEY)
|
458
460
|
self.api_base = self.config.api_base or "http://localhost:8000/v1"
|
459
461
|
if not self.api_base.startswith("http"):
|
460
462
|
self.api_base = "http://" + self.api_base
|
@@ -465,7 +467,8 @@ class OpenAIGPT(LanguageModel):
|
|
465
467
|
self.api_base = self.config.chat_model.split("/", 1)[1]
|
466
468
|
if not self.api_base.startswith("http"):
|
467
469
|
self.api_base = "http://" + self.api_base
|
468
|
-
self.api_key
|
470
|
+
if self.api_key == OPENAI_API_KEY:
|
471
|
+
self.api_key = os.environ.get("LLAMA_API_KEY", DUMMY_API_KEY)
|
469
472
|
else:
|
470
473
|
self.api_base = self.config.api_base
|
471
474
|
# If api_base is unset we use OpenAI's endpoint, which supports
|
@@ -487,11 +490,6 @@ class OpenAIGPT(LanguageModel):
|
|
487
490
|
if self.config.use_completion_for_chat:
|
488
491
|
self.config.use_chat_for_completion = False
|
489
492
|
|
490
|
-
self.api_key = config.api_key
|
491
|
-
if self.is_openai_completion_model() or self.is_openai_chat_model():
|
492
|
-
if self.api_key == DUMMY_API_KEY:
|
493
|
-
self.api_key = os.getenv("OPENAI_API_KEY", DUMMY_API_KEY)
|
494
|
-
|
495
493
|
self.is_groq = self.config.chat_model.startswith("groq/")
|
496
494
|
self.is_cerebras = self.config.chat_model.startswith("cerebras/")
|
497
495
|
self.is_gemini = self.is_gemini_model()
|
@@ -502,7 +500,7 @@ class OpenAIGPT(LanguageModel):
|
|
502
500
|
if self.is_groq:
|
503
501
|
# use groq-specific client
|
504
502
|
self.config.chat_model = self.config.chat_model.replace("groq/", "")
|
505
|
-
if self.api_key ==
|
503
|
+
if self.api_key == OPENAI_API_KEY:
|
506
504
|
self.api_key = os.getenv("GROQ_API_KEY", DUMMY_API_KEY)
|
507
505
|
self.client = Groq(
|
508
506
|
api_key=self.api_key,
|
@@ -513,7 +511,7 @@ class OpenAIGPT(LanguageModel):
|
|
513
511
|
elif self.is_cerebras:
|
514
512
|
# use cerebras-specific client
|
515
513
|
self.config.chat_model = self.config.chat_model.replace("cerebras/", "")
|
516
|
-
if self.api_key ==
|
514
|
+
if self.api_key == OPENAI_API_KEY:
|
517
515
|
self.api_key = os.getenv("CEREBRAS_API_KEY", DUMMY_API_KEY)
|
518
516
|
self.client = Cerebras(
|
519
517
|
api_key=self.api_key,
|
@@ -526,25 +524,25 @@ class OpenAIGPT(LanguageModel):
|
|
526
524
|
# in these cases, there's no specific client: OpenAI python client suffices
|
527
525
|
if self.is_gemini:
|
528
526
|
self.config.chat_model = self.config.chat_model.replace("gemini/", "")
|
529
|
-
if self.api_key ==
|
527
|
+
if self.api_key == OPENAI_API_KEY:
|
530
528
|
self.api_key = os.getenv("GEMINI_API_KEY", DUMMY_API_KEY)
|
531
529
|
self.api_base = GEMINI_BASE_URL
|
532
530
|
elif self.is_glhf:
|
533
531
|
self.config.chat_model = self.config.chat_model.replace("glhf/", "")
|
534
|
-
if self.api_key ==
|
532
|
+
if self.api_key == OPENAI_API_KEY:
|
535
533
|
self.api_key = os.getenv("GLHF_API_KEY", DUMMY_API_KEY)
|
536
534
|
self.api_base = GLHF_BASE_URL
|
537
535
|
elif self.is_openrouter:
|
538
536
|
self.config.chat_model = self.config.chat_model.replace(
|
539
537
|
"openrouter/", ""
|
540
538
|
)
|
541
|
-
if self.api_key ==
|
539
|
+
if self.api_key == OPENAI_API_KEY:
|
542
540
|
self.api_key = os.getenv("OPENROUTER_API_KEY", DUMMY_API_KEY)
|
543
541
|
self.api_base = OPENROUTER_BASE_URL
|
544
542
|
elif self.is_deepseek:
|
545
543
|
self.config.chat_model = self.config.chat_model.replace("deepseek/", "")
|
546
544
|
self.api_base = DEEPSEEK_BASE_URL
|
547
|
-
if self.api_key ==
|
545
|
+
if self.api_key == OPENAI_API_KEY:
|
548
546
|
self.api_key = os.getenv("DEEPSEEK_API_KEY", DUMMY_API_KEY)
|
549
547
|
|
550
548
|
self.client = OpenAI(
|
@@ -415,13 +415,15 @@ class DocumentParser(Parser):
|
|
415
415
|
# that it needs to be combined with the next chunk.
|
416
416
|
while len(split) > self.config.chunk_size:
|
417
417
|
# pretty formatting of pages (e.g. 1-3, 4, 5-7)
|
418
|
-
|
418
|
+
p_0 = int(pages[0])
|
419
|
+
p_n = int(pages[-1])
|
420
|
+
page_str = f"pages {p_0}-{p_n}" if p_0 != p_n else f"page {p_0}"
|
419
421
|
text = self.tokenizer.decode(split[: self.config.chunk_size])
|
420
422
|
docs.append(
|
421
423
|
Document(
|
422
424
|
content=text,
|
423
425
|
metadata=DocMetaData(
|
424
|
-
source=f"{self.source}
|
426
|
+
source=f"{self.source} {page_str}",
|
425
427
|
is_chunk=True,
|
426
428
|
id=common_id,
|
427
429
|
),
|
@@ -3,12 +3,12 @@ langroid/exceptions.py,sha256=OPjece_8cwg94DLPcOGA1ddzy5bGh65pxzcHMnssTz8,2995
|
|
3
3
|
langroid/mytypes.py,sha256=FXSH62MUCeMCJP-66RVmbNaHCDLMxllEShZ-xEeTn9A,2833
|
4
4
|
langroid/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
5
|
langroid/agent/__init__.py,sha256=ll0Cubd2DZ-fsCMl7e10hf9ZjFGKzphfBco396IKITY,786
|
6
|
-
langroid/agent/base.py,sha256=
|
6
|
+
langroid/agent/base.py,sha256=0szJ5ZxNSmobFO5805ur2cqKfD6vUP4ooN76Z5qAeyw,78677
|
7
7
|
langroid/agent/batch.py,sha256=vi1r5i1-vN80WfqHDSwjEym_KfGsqPGUtwktmiK1nuk,20635
|
8
|
-
langroid/agent/chat_agent.py,sha256=
|
8
|
+
langroid/agent/chat_agent.py,sha256=yuuEWVFLIN71XUpxdbhwZxEKAbOWG7zAV3ofYX4lCWg,84443
|
9
9
|
langroid/agent/chat_document.py,sha256=xzMtrPbaW-Y-BnF7kuhr2dorsD-D5rMWzfOqJ8HAoo8,17885
|
10
10
|
langroid/agent/openai_assistant.py,sha256=JkAcs02bIrgPNVvUWVR06VCthc5-ulla2QMBzux_q6o,34340
|
11
|
-
langroid/agent/task.py,sha256=
|
11
|
+
langroid/agent/task.py,sha256=HB6N-Jn80HFqCf0ZYOC1v3Bn3oO7NLjShHQJJFwW0q4,90557
|
12
12
|
langroid/agent/tool_message.py,sha256=BhjP-_TfQ2tgxuY4Yo_JHLOwwt0mJ4BwjPnREvEY4vk,14744
|
13
13
|
langroid/agent/xml_tool_message.py,sha256=6SshYZJKIfi4mkE-gIoSwjkEYekQ8GwcSiCv7a5uO9E,15054
|
14
14
|
langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -72,7 +72,7 @@ langroid/language_models/base.py,sha256=is4l3x858tdPHbrJU2jxJXe2j9PCGb9kk_c5nyfS
|
|
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=_EidEMIgAMx0RuELAf5Ans0yiE1QllybZALw5o-1HJg,12265
|
75
|
-
langroid/language_models/openai_gpt.py,sha256=
|
75
|
+
langroid/language_models/openai_gpt.py,sha256=lOQcExZO5Tja35Xi4F2HcG8pE-2LEnGrHwLTXLOOagk,77367
|
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
|
@@ -81,7 +81,7 @@ langroid/language_models/prompt_formatter/llama2_formatter.py,sha256=YdcO88qyBeu
|
|
81
81
|
langroid/parsing/__init__.py,sha256=2oUWJJAxIavq9Wtw5RGlkXLq3GF3zgXeVLLW4j7yeb8,1138
|
82
82
|
langroid/parsing/agent_chats.py,sha256=sbZRV9ujdM5QXvvuHVjIi2ysYSYlap-uqfMMUKulrW0,1068
|
83
83
|
langroid/parsing/code_parser.py,sha256=5ze0MBytrGGkU69pA_bJDjRm6QZz_QYfPcIwkagUa7U,3796
|
84
|
-
langroid/parsing/document_parser.py,sha256=
|
84
|
+
langroid/parsing/document_parser.py,sha256=Gyz-xEMuPZSFiJhH8MSXDAVOWV2EMP50eTvOIj7rB_s,33733
|
85
85
|
langroid/parsing/para_sentence_split.py,sha256=AJBzZojP3zpB-_IMiiHismhqcvkrVBQ3ZINoQyx_bE4,2000
|
86
86
|
langroid/parsing/parse_json.py,sha256=aADo38bAHQhC8on4aWZZzVzSDy-dK35vRLZsFI2ewh8,4756
|
87
87
|
langroid/parsing/parser.py,sha256=moJKI5Cn_Pxd7xbNrY220dqQu-0FeEWUI7ogeq63Kec,12842
|
@@ -127,7 +127,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
|
|
127
127
|
langroid/vector_store/postgres.py,sha256=DQHd6dt-OcV_QVNm-ymn28rlTfhI6hqgcpLTPCsm0jI,15990
|
128
128
|
langroid/vector_store/qdrantdb.py,sha256=v7TAsIoj_vxeKDYS9tpwJLBZA8fuTweTYxHo0X_uawM,17949
|
129
129
|
langroid/vector_store/weaviatedb.py,sha256=tjlqEtkwrhykelt-nbr2WIuHWJBuSAGjZuG6gsAMBsc,11753
|
130
|
-
langroid-0.42.
|
131
|
-
langroid-0.42.
|
132
|
-
langroid-0.42.
|
133
|
-
langroid-0.42.
|
130
|
+
langroid-0.42.10.dist-info/METADATA,sha256=NVYWP1mCWLiImvi6R5BVjxX-j-jDv02eGnoUOAg1aqE,61700
|
131
|
+
langroid-0.42.10.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
132
|
+
langroid-0.42.10.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
133
|
+
langroid-0.42.10.dist-info/RECORD,,
|
File without changes
|
File without changes
|