langroid 0.58.2__py3-none-any.whl → 0.59.0b1__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 +39 -17
- langroid/agent/base.py-e +2216 -0
- langroid/agent/callbacks/chainlit.py +2 -1
- langroid/agent/chat_agent.py +73 -55
- langroid/agent/chat_agent.py-e +2086 -0
- langroid/agent/chat_document.py +7 -7
- langroid/agent/chat_document.py-e +513 -0
- langroid/agent/openai_assistant.py +9 -9
- langroid/agent/openai_assistant.py-e +882 -0
- langroid/agent/special/arangodb/arangodb_agent.py +10 -18
- langroid/agent/special/arangodb/arangodb_agent.py-e +648 -0
- langroid/agent/special/arangodb/tools.py +3 -3
- langroid/agent/special/doc_chat_agent.py +16 -14
- langroid/agent/special/lance_rag/critic_agent.py +2 -2
- langroid/agent/special/lance_rag/query_planner_agent.py +4 -4
- langroid/agent/special/lance_tools.py +6 -5
- langroid/agent/special/lance_tools.py-e +61 -0
- langroid/agent/special/neo4j/neo4j_chat_agent.py +3 -7
- langroid/agent/special/neo4j/neo4j_chat_agent.py-e +430 -0
- langroid/agent/special/relevance_extractor_agent.py +1 -1
- langroid/agent/special/sql/sql_chat_agent.py +11 -3
- langroid/agent/task.py +9 -87
- langroid/agent/task.py-e +2418 -0
- langroid/agent/tool_message.py +33 -17
- langroid/agent/tool_message.py-e +400 -0
- langroid/agent/tools/file_tools.py +4 -2
- langroid/agent/tools/file_tools.py-e +234 -0
- langroid/agent/tools/mcp/fastmcp_client.py +19 -6
- langroid/agent/tools/mcp/fastmcp_client.py-e +584 -0
- langroid/agent/tools/orchestration.py +22 -17
- langroid/agent/tools/orchestration.py-e +301 -0
- langroid/agent/tools/recipient_tool.py +3 -3
- langroid/agent/tools/task_tool.py +22 -16
- langroid/agent/tools/task_tool.py-e +249 -0
- langroid/agent/xml_tool_message.py +90 -35
- langroid/agent/xml_tool_message.py-e +392 -0
- langroid/cachedb/base.py +1 -1
- langroid/embedding_models/base.py +2 -2
- langroid/embedding_models/models.py +3 -7
- langroid/embedding_models/models.py-e +563 -0
- langroid/exceptions.py +4 -1
- langroid/language_models/azure_openai.py +2 -2
- langroid/language_models/azure_openai.py-e +134 -0
- langroid/language_models/base.py +6 -4
- langroid/language_models/base.py-e +812 -0
- langroid/language_models/client_cache.py +64 -0
- langroid/language_models/config.py +2 -4
- langroid/language_models/config.py-e +18 -0
- langroid/language_models/model_info.py +9 -1
- langroid/language_models/model_info.py-e +483 -0
- langroid/language_models/openai_gpt.py +119 -20
- langroid/language_models/openai_gpt.py-e +2280 -0
- langroid/language_models/provider_params.py +3 -22
- langroid/language_models/provider_params.py-e +153 -0
- langroid/mytypes.py +11 -4
- langroid/mytypes.py-e +132 -0
- langroid/parsing/code_parser.py +1 -1
- langroid/parsing/file_attachment.py +1 -1
- langroid/parsing/file_attachment.py-e +246 -0
- langroid/parsing/md_parser.py +14 -4
- langroid/parsing/md_parser.py-e +574 -0
- langroid/parsing/parser.py +22 -7
- langroid/parsing/parser.py-e +410 -0
- langroid/parsing/repo_loader.py +3 -1
- langroid/parsing/repo_loader.py-e +812 -0
- langroid/parsing/search.py +1 -1
- langroid/parsing/url_loader.py +17 -51
- langroid/parsing/url_loader.py-e +683 -0
- langroid/parsing/urls.py +5 -4
- langroid/parsing/urls.py-e +279 -0
- langroid/prompts/prompts_config.py +1 -1
- langroid/pydantic_v1/__init__.py +45 -6
- langroid/pydantic_v1/__init__.py-e +36 -0
- langroid/pydantic_v1/main.py +11 -4
- langroid/pydantic_v1/main.py-e +11 -0
- langroid/utils/configuration.py +13 -11
- langroid/utils/configuration.py-e +141 -0
- langroid/utils/constants.py +1 -1
- langroid/utils/constants.py-e +32 -0
- langroid/utils/globals.py +21 -5
- langroid/utils/globals.py-e +49 -0
- langroid/utils/html_logger.py +2 -1
- langroid/utils/html_logger.py-e +825 -0
- langroid/utils/object_registry.py +1 -1
- langroid/utils/object_registry.py-e +66 -0
- langroid/utils/pydantic_utils.py +55 -28
- langroid/utils/pydantic_utils.py-e +602 -0
- langroid/utils/types.py +2 -2
- langroid/utils/types.py-e +113 -0
- langroid/vector_store/base.py +3 -3
- langroid/vector_store/lancedb.py +5 -5
- langroid/vector_store/lancedb.py-e +404 -0
- langroid/vector_store/meilisearch.py +2 -2
- langroid/vector_store/pineconedb.py +4 -4
- langroid/vector_store/pineconedb.py-e +427 -0
- langroid/vector_store/postgres.py +1 -1
- langroid/vector_store/qdrantdb.py +3 -3
- langroid/vector_store/weaviatedb.py +1 -1
- {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/METADATA +3 -2
- langroid-0.59.0b1.dist-info/RECORD +181 -0
- langroid/agent/special/doc_chat_task.py +0 -0
- langroid/mcp/__init__.py +0 -1
- langroid/mcp/server/__init__.py +0 -1
- langroid-0.58.2.dist-info/RECORD +0 -145
- {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/WHEEL +0 -0
- {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
import time
|
2
|
+
from typing import TYPE_CHECKING, Dict, Optional, TypeAlias, TypeVar
|
3
|
+
from uuid import uuid4
|
4
|
+
|
5
|
+
from pydantic import BaseModel
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from langroid.agent.base import Agent
|
9
|
+
from langroid.agent.chat_agent import ChatAgent
|
10
|
+
from langroid.agent.chat_document import ChatDocument
|
11
|
+
|
12
|
+
# any derivative of BaseModel that has an id() method or an id attribute
|
13
|
+
ObjWithId: TypeAlias = ChatDocument | ChatAgent | Agent
|
14
|
+
else:
|
15
|
+
ObjWithId = BaseModel
|
16
|
+
|
17
|
+
# Define a type variable that can be any subclass of BaseModel
|
18
|
+
T = TypeVar("T", bound=BaseModel)
|
19
|
+
|
20
|
+
|
21
|
+
class ObjectRegistry:
|
22
|
+
"""A global registry to hold id -> object mappings."""
|
23
|
+
|
24
|
+
registry: Dict[str, ObjWithId] = {}
|
25
|
+
|
26
|
+
@classmethod
|
27
|
+
def add(cls, obj: ObjWithId) -> str:
|
28
|
+
"""Adds an object to the registry, returning the object's ID."""
|
29
|
+
object_id = obj.id() if callable(obj.id) else obj.id
|
30
|
+
cls.registry[object_id] = obj
|
31
|
+
return object_id
|
32
|
+
|
33
|
+
@classmethod
|
34
|
+
def get(cls, obj_id: str) -> Optional[ObjWithId]:
|
35
|
+
"""Retrieves an object by ID if it still exists."""
|
36
|
+
return cls.registry.get(obj_id)
|
37
|
+
|
38
|
+
@classmethod
|
39
|
+
def register_object(cls, obj: ObjWithId) -> str:
|
40
|
+
"""Registers an object in the registry, returning the object's ID."""
|
41
|
+
return cls.add(obj)
|
42
|
+
|
43
|
+
@classmethod
|
44
|
+
def remove(cls, obj_id: str) -> None:
|
45
|
+
"""Removes an object from the registry."""
|
46
|
+
if obj_id in cls.registry:
|
47
|
+
del cls.registry[obj_id]
|
48
|
+
|
49
|
+
@classmethod
|
50
|
+
def cleanup(cls) -> None:
|
51
|
+
"""Cleans up the registry by removing entries where the object is None."""
|
52
|
+
to_remove = [key for key, value in cls.registry.items() if value is None]
|
53
|
+
for key in to_remove:
|
54
|
+
del cls.registry[key]
|
55
|
+
|
56
|
+
@staticmethod
|
57
|
+
def new_id() -> str:
|
58
|
+
"""Generates a new unique ID."""
|
59
|
+
return str(uuid4())
|
60
|
+
|
61
|
+
|
62
|
+
def scheduled_cleanup(interval: int = 600) -> None:
|
63
|
+
"""Periodically cleans up the global registry every 'interval' seconds."""
|
64
|
+
while True:
|
65
|
+
ObjectRegistry.cleanup()
|
66
|
+
time.sleep(interval)
|
langroid/utils/pydantic_utils.py
CHANGED
@@ -15,9 +15,9 @@ from typing import (
|
|
15
15
|
|
16
16
|
import numpy as np
|
17
17
|
import pandas as pd
|
18
|
+
from pydantic import BaseModel, ValidationError, create_model
|
18
19
|
|
19
20
|
from langroid.mytypes import DocMetaData, Document
|
20
|
-
from langroid.pydantic_v1 import BaseModel, ValidationError, create_model
|
21
21
|
|
22
22
|
logger = logging.getLogger(__name__)
|
23
23
|
|
@@ -42,7 +42,7 @@ def flatten_dict(
|
|
42
42
|
|
43
43
|
def has_field(model_class: Type[BaseModel], field_name: str) -> bool:
|
44
44
|
"""Check if a Pydantic model class has a field with the given name."""
|
45
|
-
return field_name in model_class.
|
45
|
+
return field_name in model_class.model_fields
|
46
46
|
|
47
47
|
|
48
48
|
def _recursive_purge_dict_key(d: Dict[str, Any], k: str) -> None:
|
@@ -125,29 +125,31 @@ def flatten_pydantic_model(
|
|
125
125
|
while models_to_process:
|
126
126
|
current_model, current_prefix = models_to_process.pop()
|
127
127
|
|
128
|
-
for name, field in current_model.
|
129
|
-
if
|
130
|
-
|
131
|
-
):
|
128
|
+
for name, field in current_model.model_fields.items():
|
129
|
+
field_type = field.annotation if hasattr(field, "annotation") else field
|
130
|
+
if isinstance(field_type, type) and issubclass(field_type, BaseModel):
|
132
131
|
new_prefix = (
|
133
132
|
f"{current_prefix}{name}__" if current_prefix else f"{name}__"
|
134
133
|
)
|
135
|
-
models_to_process.append((
|
134
|
+
models_to_process.append((field_type, new_prefix))
|
136
135
|
else:
|
137
136
|
flattened_name = f"{current_prefix}{name}"
|
138
137
|
|
139
|
-
if
|
138
|
+
if (
|
139
|
+
hasattr(field, "default_factory")
|
140
|
+
and field.default_factory is not None
|
141
|
+
):
|
140
142
|
flattened_fields[flattened_name] = (
|
141
|
-
|
143
|
+
field_type,
|
142
144
|
field.default_factory,
|
143
145
|
)
|
144
|
-
elif field.default is not
|
146
|
+
elif hasattr(field, "default") and field.default is not ...:
|
145
147
|
flattened_fields[flattened_name] = (
|
146
|
-
|
148
|
+
field_type,
|
147
149
|
field.default,
|
148
150
|
)
|
149
151
|
else:
|
150
|
-
flattened_fields[flattened_name] = (
|
152
|
+
flattened_fields[flattened_name] = (field_type, ...)
|
151
153
|
|
152
154
|
return create_model("FlatModel", __base__=base_model, **flattened_fields)
|
153
155
|
|
@@ -155,7 +157,7 @@ def flatten_pydantic_model(
|
|
155
157
|
def get_field_names(model: Type[BaseModel]) -> List[str]:
|
156
158
|
"""Get all field names from a possibly nested Pydantic model."""
|
157
159
|
mdl = flatten_pydantic_model(model)
|
158
|
-
fields = list(mdl.
|
160
|
+
fields = list(mdl.model_fields.keys())
|
159
161
|
# fields may be like a__b__c , so we only want the last part
|
160
162
|
return [f.split("__")[-1] for f in fields]
|
161
163
|
|
@@ -180,19 +182,22 @@ def generate_simple_schema(
|
|
180
182
|
Dict[str, Any]: A dictionary representing the JSON schema of the provided model,
|
181
183
|
with specified fields excluded.
|
182
184
|
"""
|
183
|
-
if hasattr(model, "
|
185
|
+
if hasattr(model, "model_fields"):
|
184
186
|
output: Dict[str, Any] = {}
|
185
|
-
for field_name, field in model.
|
187
|
+
for field_name, field in model.model_fields.items():
|
186
188
|
if field_name in exclude:
|
187
189
|
continue # Skip excluded fields
|
188
190
|
|
189
|
-
field_type = field.
|
190
|
-
if issubclass(field_type, BaseModel):
|
191
|
+
field_type = field.annotation if hasattr(field, "annotation") else field
|
192
|
+
if isinstance(field_type, type) and issubclass(field_type, BaseModel):
|
191
193
|
# Recursively generate schema for nested models
|
192
194
|
output[field_name] = generate_simple_schema(field_type, exclude)
|
193
|
-
|
195
|
+
elif field_type is not None and hasattr(field_type, "__name__"):
|
194
196
|
# Represent the type as a string here
|
195
197
|
output[field_name] = {"type": field_type.__name__}
|
198
|
+
else:
|
199
|
+
# Fallback for complex types
|
200
|
+
output[field_name] = {"type": str(field_type)}
|
196
201
|
return output
|
197
202
|
else:
|
198
203
|
# Non-model type, return a simplified representation
|
@@ -218,14 +223,28 @@ def flatten_pydantic_instance(
|
|
218
223
|
|
219
224
|
"""
|
220
225
|
flat_data: Dict[str, Any] = {}
|
221
|
-
for name, value in instance.
|
226
|
+
for name, value in instance.model_dump().items():
|
222
227
|
# Assuming nested pydantic model will be a dict here
|
223
228
|
if isinstance(value, dict):
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
229
|
+
# Get field info from model_fields
|
230
|
+
field_info = instance.model_fields[name]
|
231
|
+
# Try to get the nested model type from field annotation
|
232
|
+
field_type = (
|
233
|
+
field_info.annotation if hasattr(field_info, "annotation") else None
|
228
234
|
)
|
235
|
+
if (
|
236
|
+
field_type
|
237
|
+
and isinstance(field_type, type)
|
238
|
+
and issubclass(field_type, BaseModel)
|
239
|
+
):
|
240
|
+
nested_flat_data = flatten_pydantic_instance(
|
241
|
+
field_type(**value),
|
242
|
+
prefix=f"{prefix}{name}__",
|
243
|
+
force_str=force_str,
|
244
|
+
)
|
245
|
+
else:
|
246
|
+
# Skip non-Pydantic nested fields for safety
|
247
|
+
continue
|
229
248
|
flat_data.update(nested_flat_data)
|
230
249
|
else:
|
231
250
|
flat_data[f"{prefix}{name}"] = str(value) if force_str else value
|
@@ -531,10 +550,19 @@ def extra_metadata(document: Document, doc_cls: Type[Document] = Document) -> Li
|
|
531
550
|
in the document's metadata.
|
532
551
|
"""
|
533
552
|
# Convert metadata to dict, including extra fields.
|
534
|
-
metadata_fields = set(document.metadata.
|
553
|
+
metadata_fields = set(document.metadata.model_dump().keys())
|
535
554
|
|
536
555
|
# Get defined fields in the metadata of doc_cls
|
537
|
-
|
556
|
+
metadata_field = doc_cls.model_fields["metadata"]
|
557
|
+
metadata_type = (
|
558
|
+
metadata_field.annotation
|
559
|
+
if hasattr(metadata_field, "annotation")
|
560
|
+
else metadata_field
|
561
|
+
)
|
562
|
+
if isinstance(metadata_type, type) and hasattr(metadata_type, "model_fields"):
|
563
|
+
defined_fields = set(metadata_type.model_fields.keys())
|
564
|
+
else:
|
565
|
+
defined_fields = set()
|
538
566
|
|
539
567
|
# Identify extra fields not in defined fields.
|
540
568
|
extra_fields = list(metadata_fields - defined_fields)
|
@@ -561,14 +589,13 @@ def extend_document_class(d: Document) -> Type[Document]:
|
|
561
589
|
# Extract the fields from the original metadata class, including types,
|
562
590
|
# correctly handling special types like List[str].
|
563
591
|
original_metadata_fields = {
|
564
|
-
k: (v.
|
565
|
-
for k, v in DocMetaData.__fields__.items()
|
592
|
+
k: (v.annotation, ...) for k, v in DocMetaData.model_fields.items()
|
566
593
|
}
|
567
594
|
# Extract extra fields from the metadata instance with their types
|
568
595
|
extra_fields = {
|
569
596
|
k: (type(v), ...)
|
570
597
|
for k, v in d.metadata.__dict__.items()
|
571
|
-
if k not in DocMetaData.
|
598
|
+
if k not in DocMetaData.model_fields
|
572
599
|
}
|
573
600
|
|
574
601
|
# Combine original and extra fields for the new metadata class
|