langroid 0.19.0__py3-none-any.whl → 0.19.2__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 +11 -1
- langroid/agent/xml_tool_message.py +59 -29
- langroid/exceptions.py +5 -0
- {langroid-0.19.0.dist-info → langroid-0.19.2.dist-info}/METADATA +3 -2
- {langroid-0.19.0.dist-info → langroid-0.19.2.dist-info}/RECORD +8 -8
- pyproject.toml +1 -1
- {langroid-0.19.0.dist-info → langroid-0.19.2.dist-info}/LICENSE +0 -0
- {langroid-0.19.0.dist-info → langroid-0.19.2.dist-info}/WHEEL +0 -0
langroid/agent/base.py
CHANGED
@@ -33,6 +33,7 @@ from rich.prompt import Prompt
|
|
33
33
|
from langroid.agent.chat_document import ChatDocMetaData, ChatDocument
|
34
34
|
from langroid.agent.tool_message import ToolMessage
|
35
35
|
from langroid.agent.xml_tool_message import XMLToolMessage
|
36
|
+
from langroid.exceptions import XMLException
|
36
37
|
from langroid.language_models.base import (
|
37
38
|
LanguageModel,
|
38
39
|
LLMConfig,
|
@@ -1120,6 +1121,8 @@ class Agent(ABC):
|
|
1120
1121
|
except ValidationError as ve:
|
1121
1122
|
# correct tool name but bad fields
|
1122
1123
|
return self.tool_validation_error(ve)
|
1124
|
+
except XMLException as xe: # from XMLToolMessage parsing
|
1125
|
+
return str(xe)
|
1123
1126
|
except ValueError:
|
1124
1127
|
# invalid tool name
|
1125
1128
|
# We return None since returning "invalid tool name" would
|
@@ -1242,7 +1245,14 @@ class Agent(ABC):
|
|
1242
1245
|
if is_json:
|
1243
1246
|
maybe_tool_dict = json.loads(tool_candidate_str)
|
1244
1247
|
else:
|
1245
|
-
|
1248
|
+
try:
|
1249
|
+
maybe_tool_dict = XMLToolMessage.extract_field_values(
|
1250
|
+
tool_candidate_str
|
1251
|
+
)
|
1252
|
+
except Exception as e:
|
1253
|
+
from langroid.exceptions import XMLException
|
1254
|
+
|
1255
|
+
raise XMLException(f"Error extracting XML fields:\n {str(e)}")
|
1246
1256
|
# check if the maybe_tool_dict contains a "properties" field
|
1247
1257
|
# which further contains the actual tool-call
|
1248
1258
|
# (some weak LLMs do this). E.g. gpt-4o sometimes generates this:
|
@@ -1,4 +1,5 @@
|
|
1
|
-
from
|
1
|
+
from collections.abc import Mapping
|
2
|
+
from typing import Any, Dict, List, Optional, get_args, get_origin
|
2
3
|
|
3
4
|
from lxml import etree
|
4
5
|
|
@@ -106,12 +107,17 @@ class XMLToolMessage(ToolMessage):
|
|
106
107
|
Optional["XMLToolMessage"]: An instance of the class if parsing succeeds,
|
107
108
|
None otherwise.
|
108
109
|
"""
|
109
|
-
|
110
|
-
|
111
|
-
|
110
|
+
try:
|
111
|
+
parsed_data = cls.extract_field_values(formatted_string)
|
112
|
+
if parsed_data is None:
|
113
|
+
return None
|
114
|
+
|
115
|
+
# Use Pydantic's parse_obj to create and validate the instance
|
116
|
+
return cls.parse_obj(parsed_data)
|
117
|
+
except Exception as e:
|
118
|
+
from langroid.exceptions import XMLException
|
112
119
|
|
113
|
-
|
114
|
-
return cls.parse_obj(parsed_data)
|
120
|
+
raise XMLException(f"Error parsing XML: {str(e)}")
|
115
121
|
|
116
122
|
@classmethod
|
117
123
|
def find_verbatim_fields(
|
@@ -133,15 +139,6 @@ class XMLToolMessage(ToolMessage):
|
|
133
139
|
|
134
140
|
@classmethod
|
135
141
|
def format_instructions(cls, tool: bool = False) -> str:
|
136
|
-
"""
|
137
|
-
Instructions to the LLM showing how to use the XML tool.
|
138
|
-
|
139
|
-
Args:
|
140
|
-
tool: Not used in this implementation, kept for compatibility.
|
141
|
-
|
142
|
-
Returns:
|
143
|
-
str: instructions on how to use the XML message
|
144
|
-
"""
|
145
142
|
fields = [
|
146
143
|
f
|
147
144
|
for f in cls.__fields__.keys()
|
@@ -165,35 +162,62 @@ class XMLToolMessage(ToolMessage):
|
|
165
162
|
nonlocal preamble, xml_format
|
166
163
|
current_path = f"{path}.{field_name}" if path else field_name
|
167
164
|
|
168
|
-
|
165
|
+
origin = get_origin(field_type)
|
166
|
+
args = get_args(field_type)
|
167
|
+
|
168
|
+
if (
|
169
|
+
origin is None
|
170
|
+
and isinstance(field_type, type)
|
171
|
+
and issubclass(field_type, BaseModel)
|
172
|
+
):
|
169
173
|
preamble += (
|
170
174
|
f"{field_name.upper()} = [nested structure for {field_name}]\n"
|
171
175
|
)
|
172
176
|
xml_format += f"{indent}<{field_name}>\n"
|
173
177
|
for sub_field, sub_field_info in field_type.__fields__.items():
|
174
178
|
format_field(
|
175
|
-
sub_field,
|
179
|
+
sub_field,
|
180
|
+
sub_field_info.outer_type_,
|
181
|
+
indent + " ",
|
182
|
+
current_path,
|
176
183
|
)
|
177
184
|
xml_format += f"{indent}</{field_name}>\n"
|
178
|
-
elif
|
179
|
-
item_type =
|
180
|
-
|
185
|
+
elif origin in (list, List) or (field_type is list):
|
186
|
+
item_type = args[0] if args else Any
|
187
|
+
if isinstance(item_type, type) and issubclass(item_type, BaseModel):
|
188
|
+
preamble += (
|
189
|
+
f"{field_name.upper()} = "
|
190
|
+
f"[list of nested structures for {field_name}]\n"
|
191
|
+
)
|
192
|
+
else:
|
193
|
+
preamble += (
|
194
|
+
f"{field_name.upper()} = "
|
195
|
+
f"[list of {getattr(item_type, '__name__', str(item_type))} "
|
196
|
+
f"for {field_name}]\n"
|
197
|
+
)
|
181
198
|
xml_format += f"{indent}<{field_name}>\n"
|
182
|
-
xml_format +=
|
199
|
+
xml_format += (
|
200
|
+
f"{indent} <item>"
|
201
|
+
f"[{getattr(item_type, '__name__', str(item_type))} value]"
|
202
|
+
f"</item>\n"
|
203
|
+
)
|
183
204
|
xml_format += f"{indent} ...\n"
|
184
205
|
xml_format += f"{indent}</{field_name}>\n"
|
185
|
-
elif
|
186
|
-
|
206
|
+
elif origin in (dict, Dict) or (
|
207
|
+
isinstance(field_type, type) and issubclass(field_type, Mapping)
|
208
|
+
):
|
209
|
+
key_type, value_type = args if len(args) == 2 else (Any, Any)
|
187
210
|
preamble += (
|
188
211
|
f"{field_name.upper()} = "
|
189
|
-
f"[dictionary with
|
190
|
-
f"{
|
212
|
+
f"[dictionary with "
|
213
|
+
f"{getattr(key_type, '__name__', str(key_type))} keys and "
|
214
|
+
f"{getattr(value_type, '__name__', str(value_type))} values]\n"
|
191
215
|
)
|
192
216
|
xml_format += f"{indent}<{field_name}>\n"
|
193
217
|
xml_format += (
|
194
|
-
f"{indent} <{key_type
|
195
|
-
f"[{value_type
|
196
|
-
f"</{key_type
|
218
|
+
f"{indent} <{getattr(key_type, '__name__', str(key_type))}>"
|
219
|
+
f"[{getattr(value_type, '__name__', str(value_type))} value]"
|
220
|
+
f"</{getattr(key_type, '__name__', str(key_type))}>\n"
|
197
221
|
)
|
198
222
|
xml_format += f"{indent} ...\n"
|
199
223
|
xml_format += f"{indent}</{field_name}>\n"
|
@@ -213,7 +237,10 @@ class XMLToolMessage(ToolMessage):
|
|
213
237
|
verbatim_fields = cls.find_verbatim_fields()
|
214
238
|
|
215
239
|
for field in fields:
|
216
|
-
|
240
|
+
field_info = cls.__fields__[field]
|
241
|
+
field_type = (
|
242
|
+
field_info.outer_type_
|
243
|
+
) # Use outer_type_ to get the actual type including List, etc.
|
217
244
|
format_field(field, field_type)
|
218
245
|
|
219
246
|
xml_format += f"</{cls.Config.root_element}>"
|
@@ -260,6 +287,9 @@ class XMLToolMessage(ToolMessage):
|
|
260
287
|
def create_element(
|
261
288
|
parent: etree._Element, name: str, value: Any, path: str = ""
|
262
289
|
) -> None:
|
290
|
+
if value is None:
|
291
|
+
return
|
292
|
+
|
263
293
|
elem = etree.SubElement(parent, name)
|
264
294
|
current_path = f"{path}.{name}" if path else name
|
265
295
|
|
langroid/exceptions.py
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
from typing import Optional
|
2
2
|
|
3
3
|
|
4
|
+
class XMLException(Exception):
|
5
|
+
def __init__(self, message: str) -> None:
|
6
|
+
super().__init__(message)
|
7
|
+
|
8
|
+
|
4
9
|
class InfiniteLoopException(Exception):
|
5
10
|
def __init__(self, message: str = "Infinite loop detected", *args: object) -> None:
|
6
11
|
super().__init__(message, *args)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.19.
|
3
|
+
Version: 0.19.2
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
License: MIT
|
6
6
|
Author: Prasad Chalasani
|
@@ -153,7 +153,8 @@ This Multi-Agent paradigm is inspired by the
|
|
153
153
|
(but you do not need to know anything about this!).
|
154
154
|
|
155
155
|
`Langroid` is a fresh take on LLM app-development, where considerable thought has gone
|
156
|
-
into simplifying the developer experience;
|
156
|
+
into simplifying the developer experience;
|
157
|
+
it does not use `Langchain`, or any other LLM framework.
|
157
158
|
|
158
159
|
:fire: Read the (WIP) [overview of the langroid architecture](https://langroid.github.io/langroid/blog/2024/08/15/overview-of-langroids-multi-agent-architecture-prelim/)
|
159
160
|
|
@@ -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=swttuRRJKdDt-GUFsBNKHn1MQRj29b2f2U7Tuw5V5pk,64793
|
4
4
|
langroid/agent/batch.py,sha256=QZdlt1563hx4l3AXrCaGovE-PNG93M3DsvQAbDzdiS8,13705
|
5
5
|
langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
langroid/agent/callbacks/chainlit.py,sha256=JJXI3UGTyTDg2FFath4rqY1GyUo_0pbVBt8CZpvdtn4,23289
|
@@ -46,7 +46,7 @@ langroid/agent/tools/retrieval_tool.py,sha256=2q2pfoYbZNfbWQ0McxrtmfF0ekGglIgRl-
|
|
46
46
|
langroid/agent/tools/rewind_tool.py,sha256=XAXL3BpNhCmBGYq_qi_sZfHJuIw7NY2jp4wnojJ7WRs,5606
|
47
47
|
langroid/agent/tools/segment_extract_tool.py,sha256=__srZ_VGYLVOdPrITUM8S0HpmX4q7r5FHWMDdHdEv8w,1440
|
48
48
|
langroid/agent/typed_task.py,sha256=oxja0Z3uLTv0BcR1xIMqDpo85MIGOruz4XsZ4ghjsW4,689
|
49
|
-
langroid/agent/xml_tool_message.py,sha256=
|
49
|
+
langroid/agent/xml_tool_message.py,sha256=4Haxz_B2hyqKQmnHSf_BlloSqCNVjmBPeHzQaXlJq5c,14614
|
50
50
|
langroid/agent_config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
51
51
|
langroid/cachedb/__init__.py,sha256=icAT2s7Vhf-ZGUeqpDQGNU6ob6o0aFEyjwcxxUGRFjg,225
|
52
52
|
langroid/cachedb/base.py,sha256=ztVjB1DtN6pLCujCWnR6xruHxwVj3XkYniRTYAKKqk0,1354
|
@@ -62,7 +62,7 @@ langroid/embedding_models/protoc/embeddings_pb2.py,sha256=4Q57PhOunv-uZNJrxYrWBX
|
|
62
62
|
langroid/embedding_models/protoc/embeddings_pb2.pyi,sha256=UkNy7BrNsmQm0vLb3NtGXy8jVtz-kPWwwFsX-QbQBhQ,1475
|
63
63
|
langroid/embedding_models/protoc/embeddings_pb2_grpc.py,sha256=9dYQqkW3JPyBpSEjeGXTNpSqAkC-6FPtBHyteVob2Y8,2452
|
64
64
|
langroid/embedding_models/remote_embeds.py,sha256=6_kjXByVbqhY9cGwl9R83ZcYC2km-nGieNNAo1McHaY,5151
|
65
|
-
langroid/exceptions.py,sha256=
|
65
|
+
langroid/exceptions.py,sha256=G60UVDChkUlBDVWHFr_43zUUszZHSejoU00tX_dfD68,2322
|
66
66
|
langroid/language_models/.chainlit/config.toml,sha256=1t5lHORGzc2E6dkaO9P15jYHu2w-4Kl9pYjpDPc84vs,3716
|
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
|
@@ -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=
|
141
|
-
langroid-0.19.
|
142
|
-
langroid-0.19.
|
143
|
-
langroid-0.19.
|
144
|
-
langroid-0.19.
|
140
|
+
pyproject.toml,sha256=ohzAmHj2gSfswmziqzEjvvD2xjI8EK4LnNuQu89no3E,7251
|
141
|
+
langroid-0.19.2.dist-info/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
142
|
+
langroid-0.19.2.dist-info/METADATA,sha256=ouU8iCZbMm7HZbuKEOyTYcKcPgC024YxBkRBNFLCaAM,56513
|
143
|
+
langroid-0.19.2.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
|
144
|
+
langroid-0.19.2.dist-info/RECORD,,
|
pyproject.toml
CHANGED
File without changes
|
File without changes
|