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.
Files changed (106) hide show
  1. langroid/agent/base.py +39 -17
  2. langroid/agent/base.py-e +2216 -0
  3. langroid/agent/callbacks/chainlit.py +2 -1
  4. langroid/agent/chat_agent.py +73 -55
  5. langroid/agent/chat_agent.py-e +2086 -0
  6. langroid/agent/chat_document.py +7 -7
  7. langroid/agent/chat_document.py-e +513 -0
  8. langroid/agent/openai_assistant.py +9 -9
  9. langroid/agent/openai_assistant.py-e +882 -0
  10. langroid/agent/special/arangodb/arangodb_agent.py +10 -18
  11. langroid/agent/special/arangodb/arangodb_agent.py-e +648 -0
  12. langroid/agent/special/arangodb/tools.py +3 -3
  13. langroid/agent/special/doc_chat_agent.py +16 -14
  14. langroid/agent/special/lance_rag/critic_agent.py +2 -2
  15. langroid/agent/special/lance_rag/query_planner_agent.py +4 -4
  16. langroid/agent/special/lance_tools.py +6 -5
  17. langroid/agent/special/lance_tools.py-e +61 -0
  18. langroid/agent/special/neo4j/neo4j_chat_agent.py +3 -7
  19. langroid/agent/special/neo4j/neo4j_chat_agent.py-e +430 -0
  20. langroid/agent/special/relevance_extractor_agent.py +1 -1
  21. langroid/agent/special/sql/sql_chat_agent.py +11 -3
  22. langroid/agent/task.py +9 -87
  23. langroid/agent/task.py-e +2418 -0
  24. langroid/agent/tool_message.py +33 -17
  25. langroid/agent/tool_message.py-e +400 -0
  26. langroid/agent/tools/file_tools.py +4 -2
  27. langroid/agent/tools/file_tools.py-e +234 -0
  28. langroid/agent/tools/mcp/fastmcp_client.py +19 -6
  29. langroid/agent/tools/mcp/fastmcp_client.py-e +584 -0
  30. langroid/agent/tools/orchestration.py +22 -17
  31. langroid/agent/tools/orchestration.py-e +301 -0
  32. langroid/agent/tools/recipient_tool.py +3 -3
  33. langroid/agent/tools/task_tool.py +22 -16
  34. langroid/agent/tools/task_tool.py-e +249 -0
  35. langroid/agent/xml_tool_message.py +90 -35
  36. langroid/agent/xml_tool_message.py-e +392 -0
  37. langroid/cachedb/base.py +1 -1
  38. langroid/embedding_models/base.py +2 -2
  39. langroid/embedding_models/models.py +3 -7
  40. langroid/embedding_models/models.py-e +563 -0
  41. langroid/exceptions.py +4 -1
  42. langroid/language_models/azure_openai.py +2 -2
  43. langroid/language_models/azure_openai.py-e +134 -0
  44. langroid/language_models/base.py +6 -4
  45. langroid/language_models/base.py-e +812 -0
  46. langroid/language_models/client_cache.py +64 -0
  47. langroid/language_models/config.py +2 -4
  48. langroid/language_models/config.py-e +18 -0
  49. langroid/language_models/model_info.py +9 -1
  50. langroid/language_models/model_info.py-e +483 -0
  51. langroid/language_models/openai_gpt.py +119 -20
  52. langroid/language_models/openai_gpt.py-e +2280 -0
  53. langroid/language_models/provider_params.py +3 -22
  54. langroid/language_models/provider_params.py-e +153 -0
  55. langroid/mytypes.py +11 -4
  56. langroid/mytypes.py-e +132 -0
  57. langroid/parsing/code_parser.py +1 -1
  58. langroid/parsing/file_attachment.py +1 -1
  59. langroid/parsing/file_attachment.py-e +246 -0
  60. langroid/parsing/md_parser.py +14 -4
  61. langroid/parsing/md_parser.py-e +574 -0
  62. langroid/parsing/parser.py +22 -7
  63. langroid/parsing/parser.py-e +410 -0
  64. langroid/parsing/repo_loader.py +3 -1
  65. langroid/parsing/repo_loader.py-e +812 -0
  66. langroid/parsing/search.py +1 -1
  67. langroid/parsing/url_loader.py +17 -51
  68. langroid/parsing/url_loader.py-e +683 -0
  69. langroid/parsing/urls.py +5 -4
  70. langroid/parsing/urls.py-e +279 -0
  71. langroid/prompts/prompts_config.py +1 -1
  72. langroid/pydantic_v1/__init__.py +45 -6
  73. langroid/pydantic_v1/__init__.py-e +36 -0
  74. langroid/pydantic_v1/main.py +11 -4
  75. langroid/pydantic_v1/main.py-e +11 -0
  76. langroid/utils/configuration.py +13 -11
  77. langroid/utils/configuration.py-e +141 -0
  78. langroid/utils/constants.py +1 -1
  79. langroid/utils/constants.py-e +32 -0
  80. langroid/utils/globals.py +21 -5
  81. langroid/utils/globals.py-e +49 -0
  82. langroid/utils/html_logger.py +2 -1
  83. langroid/utils/html_logger.py-e +825 -0
  84. langroid/utils/object_registry.py +1 -1
  85. langroid/utils/object_registry.py-e +66 -0
  86. langroid/utils/pydantic_utils.py +55 -28
  87. langroid/utils/pydantic_utils.py-e +602 -0
  88. langroid/utils/types.py +2 -2
  89. langroid/utils/types.py-e +113 -0
  90. langroid/vector_store/base.py +3 -3
  91. langroid/vector_store/lancedb.py +5 -5
  92. langroid/vector_store/lancedb.py-e +404 -0
  93. langroid/vector_store/meilisearch.py +2 -2
  94. langroid/vector_store/pineconedb.py +4 -4
  95. langroid/vector_store/pineconedb.py-e +427 -0
  96. langroid/vector_store/postgres.py +1 -1
  97. langroid/vector_store/qdrantdb.py +3 -3
  98. langroid/vector_store/weaviatedb.py +1 -1
  99. {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/METADATA +3 -2
  100. langroid-0.59.0b1.dist-info/RECORD +181 -0
  101. langroid/agent/special/doc_chat_task.py +0 -0
  102. langroid/mcp/__init__.py +0 -1
  103. langroid/mcp/server/__init__.py +0 -1
  104. langroid-0.58.2.dist-info/RECORD +0 -145
  105. {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/WHEEL +0 -0
  106. {langroid-0.58.2.dist-info → langroid-0.59.0b1.dist-info}/licenses/LICENSE +0 -0
@@ -2,7 +2,7 @@ import time
2
2
  from typing import TYPE_CHECKING, Dict, Optional, TypeAlias, TypeVar
3
3
  from uuid import uuid4
4
4
 
5
- from langroid.pydantic_v1 import BaseModel
5
+ from pydantic import BaseModel
6
6
 
7
7
  if TYPE_CHECKING:
8
8
  from langroid.agent.base import Agent
@@ -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)
@@ -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.__fields__
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.__fields__.items():
129
- if isinstance(field.outer_type_, type) and issubclass(
130
- field.outer_type_, BaseModel
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((field.outer_type_, new_prefix))
134
+ models_to_process.append((field_type, new_prefix))
136
135
  else:
137
136
  flattened_name = f"{current_prefix}{name}"
138
137
 
139
- if field.default_factory is not field.default_factory:
138
+ if (
139
+ hasattr(field, "default_factory")
140
+ and field.default_factory is not None
141
+ ):
140
142
  flattened_fields[flattened_name] = (
141
- field.outer_type_,
143
+ field_type,
142
144
  field.default_factory,
143
145
  )
144
- elif field.default is not field.default:
146
+ elif hasattr(field, "default") and field.default is not ...:
145
147
  flattened_fields[flattened_name] = (
146
- field.outer_type_,
148
+ field_type,
147
149
  field.default,
148
150
  )
149
151
  else:
150
- flattened_fields[flattened_name] = (field.outer_type_, ...)
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.__fields__.keys())
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, "__fields__"):
185
+ if hasattr(model, "model_fields"):
184
186
  output: Dict[str, Any] = {}
185
- for field_name, field in model.__fields__.items():
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.type_
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
- else:
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.dict().items():
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
- nested_flat_data = flatten_pydantic_instance(
225
- instance.__fields__[name].type_(**value),
226
- prefix=f"{prefix}{name}__",
227
- force_str=force_str,
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.dict().keys())
553
+ metadata_fields = set(document.metadata.model_dump().keys())
535
554
 
536
555
  # Get defined fields in the metadata of doc_cls
537
- defined_fields = set(doc_cls.__fields__["metadata"].type_.__fields__.keys())
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.outer_type_ if v.shape != 1 else v.type_, ...)
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.__fields__
598
+ if k not in DocMetaData.model_fields
572
599
  }
573
600
 
574
601
  # Combine original and extra fields for the new metadata class