langroid 0.53.11__py3-none-any.whl → 0.53.13__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/task.py +6 -1
- langroid/agent/tools/mcp/fastmcp_client.py +1 -1
- langroid/embedding_models/models.py +4 -2
- langroid/utils/logging.py +82 -16
- {langroid-0.53.11.dist-info → langroid-0.53.13.dist-info}/METADATA +1 -1
- {langroid-0.53.11.dist-info → langroid-0.53.13.dist-info}/RECORD +8 -8
- {langroid-0.53.11.dist-info → langroid-0.53.13.dist-info}/WHEEL +0 -0
- {langroid-0.53.11.dist-info → langroid-0.53.13.dist-info}/licenses/LICENSE +0 -0
langroid/agent/task.py
CHANGED
@@ -578,19 +578,24 @@ class Task:
|
|
578
578
|
return self.pending_message
|
579
579
|
|
580
580
|
def init_loggers(self) -> None:
|
581
|
+
"""Initialise per-task Rich and TSV loggers."""
|
582
|
+
from langroid.utils.logging import RichFileLogger
|
583
|
+
|
581
584
|
if self.caller is not None and self.caller.logger is not None:
|
582
585
|
self.logger = self.caller.logger
|
583
586
|
elif self.logger is None:
|
584
587
|
self.logger = RichFileLogger(
|
585
588
|
str(Path(self.config.logs_dir) / f"{self.name}.log"),
|
589
|
+
append=True,
|
586
590
|
color=self.color_log,
|
587
591
|
)
|
588
592
|
|
589
593
|
if self.caller is not None and self.caller.tsv_logger is not None:
|
590
594
|
self.tsv_logger = self.caller.tsv_logger
|
591
595
|
elif self.tsv_logger is None:
|
596
|
+
# unique logger name ensures a distinct `logging.Logger` object
|
592
597
|
self.tsv_logger = setup_file_logger(
|
593
|
-
"tsv_logger",
|
598
|
+
f"tsv_logger.{self.name}.{id(self)}",
|
594
599
|
str(Path(self.config.logs_dir) / f"{self.name}.tsv"),
|
595
600
|
)
|
596
601
|
header = ChatDocLoggerFields().tsv_header()
|
@@ -68,7 +68,7 @@ class FastMCPClient:
|
|
68
68
|
roots=self.roots,
|
69
69
|
log_handler=self.log_handler,
|
70
70
|
message_handler=self.message_handler,
|
71
|
-
|
71
|
+
timeout=self.read_timeout_seconds,
|
72
72
|
)
|
73
73
|
# actually enter it (opens the session)
|
74
74
|
self.client = await self._cm.__aenter__() # type: ignore
|
@@ -521,7 +521,7 @@ class GeminiEmbeddings(EmbeddingModel):
|
|
521
521
|
for batch in batched(texts, self.config.batch_size):
|
522
522
|
result = self.client.models.embed_content( # type: ignore[attr-defined]
|
523
523
|
model=self.config.model_name,
|
524
|
-
contents=batch,
|
524
|
+
contents=batch, # type: ignore
|
525
525
|
)
|
526
526
|
|
527
527
|
if not hasattr(result, "embeddings") or not isinstance(
|
@@ -532,7 +532,9 @@ class GeminiEmbeddings(EmbeddingModel):
|
|
532
532
|
)
|
533
533
|
|
534
534
|
# Extract .values from ContentEmbedding objects
|
535
|
-
all_embeddings.extend(
|
535
|
+
all_embeddings.extend(
|
536
|
+
[emb.values for emb in result.embeddings] # type: ignore
|
537
|
+
)
|
536
538
|
|
537
539
|
return all_embeddings
|
538
540
|
|
langroid/utils/logging.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
import logging
|
2
|
+
import os
|
2
3
|
import os.path
|
3
|
-
|
4
|
+
import threading
|
5
|
+
from typing import ClassVar, Dict, no_type_check
|
4
6
|
|
5
7
|
import colorlog
|
6
8
|
from rich.console import Console
|
@@ -114,22 +116,86 @@ def setup_loggers_for_package(package_name: str, level: int) -> None:
|
|
114
116
|
|
115
117
|
|
116
118
|
class RichFileLogger:
|
117
|
-
|
118
|
-
os.makedirs(os.path.dirname(log_file), exist_ok=True)
|
119
|
-
self.log_file = log_file
|
120
|
-
if not append:
|
121
|
-
if os.path.exists(self.log_file):
|
122
|
-
os.remove(self.log_file)
|
123
|
-
self.file = None
|
124
|
-
self.console = None
|
125
|
-
self.append = append
|
126
|
-
self.color = color
|
119
|
+
"""Singleton-per-path, ref-counted, thread-safe file logger.
|
127
120
|
|
121
|
+
• Any number of calls to `RichFileLogger(path)` yield the same object.
|
122
|
+
• A per-instance lock guarantees that the underlying file is opened only
|
123
|
+
once, even when many threads construct the logger concurrently.
|
124
|
+
• A reference counter tracks how many parts of the program are using the
|
125
|
+
logger; the FD is closed only when the counter reaches zero.
|
126
|
+
• All writes are serialised with a dedicated write-lock.
|
127
|
+
"""
|
128
|
+
|
129
|
+
_instances: ClassVar[Dict[str, "RichFileLogger"]] = {}
|
130
|
+
_ref_counts: ClassVar[Dict[str, int]] = {}
|
131
|
+
# guards _instances & _ref_counts
|
132
|
+
_class_lock: ClassVar[threading.Lock] = threading.Lock()
|
133
|
+
|
134
|
+
# ------------------------------------------------------------------ #
|
135
|
+
# construction / destruction
|
136
|
+
# ------------------------------------------------------------------ #
|
137
|
+
def __new__(
|
138
|
+
cls, log_file: str, append: bool = False, color: bool = True
|
139
|
+
) -> "RichFileLogger":
|
140
|
+
with cls._class_lock:
|
141
|
+
if log_file in cls._instances:
|
142
|
+
cls._ref_counts[log_file] += 1
|
143
|
+
return cls._instances[log_file]
|
144
|
+
|
145
|
+
inst = super().__new__(cls)
|
146
|
+
# create the per-instance init-lock *before* releasing class-lock
|
147
|
+
inst._init_lock = threading.Lock()
|
148
|
+
cls._instances[log_file] = inst
|
149
|
+
cls._ref_counts[log_file] = 1
|
150
|
+
return inst
|
151
|
+
|
152
|
+
def __init__(self, log_file: str, append: bool = False, color: bool = True) -> None:
|
153
|
+
# Double-checked locking: perform heavy init exactly once.
|
154
|
+
if getattr(self, "_init_done", False):
|
155
|
+
return
|
156
|
+
|
157
|
+
if not hasattr(self, "_init_lock"):
|
158
|
+
self._init_lock: threading.Lock = threading.Lock()
|
159
|
+
|
160
|
+
with self._init_lock:
|
161
|
+
if getattr(self, "_init_done", False):
|
162
|
+
return
|
163
|
+
|
164
|
+
os.makedirs(os.path.dirname(log_file), exist_ok=True)
|
165
|
+
mode = "a" if append else "w"
|
166
|
+
self.file = open(log_file, mode, buffering=1, encoding="utf-8")
|
167
|
+
self.log_file: str = log_file
|
168
|
+
self.color: bool = color
|
169
|
+
self.console: Console | None = (
|
170
|
+
Console(file=self.file, force_terminal=True, width=200)
|
171
|
+
if color
|
172
|
+
else None
|
173
|
+
)
|
174
|
+
self._write_lock = threading.Lock()
|
175
|
+
self._init_done = True # set last
|
176
|
+
|
177
|
+
# ------------------------------------------------------------------ #
|
178
|
+
# public API
|
179
|
+
# ------------------------------------------------------------------ #
|
128
180
|
@no_type_check
|
129
181
|
def log(self, message: str) -> None:
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
console.print(escape(message))
|
182
|
+
"""Thread-safe write to the log file."""
|
183
|
+
with self._write_lock:
|
184
|
+
if self.color and self.console is not None:
|
185
|
+
self.console.print(escape(message))
|
186
|
+
else:
|
187
|
+
print(message, file=self.file)
|
188
|
+
self.file.flush()
|
189
|
+
|
190
|
+
def close(self) -> None:
|
191
|
+
"""Decrease ref-count; close FD only when last user is done."""
|
192
|
+
with self._class_lock:
|
193
|
+
count = self._ref_counts.get(self.log_file, 0) - 1
|
194
|
+
if count <= 0:
|
195
|
+
self._ref_counts.pop(self.log_file, None)
|
196
|
+
self._instances.pop(self.log_file, None)
|
197
|
+
with self._write_lock:
|
198
|
+
if not self.file.closed:
|
199
|
+
self.file.close()
|
134
200
|
else:
|
135
|
-
|
201
|
+
self._ref_counts[self.log_file] = count
|
@@ -8,7 +8,7 @@ langroid/agent/batch.py,sha256=vi1r5i1-vN80WfqHDSwjEym_KfGsqPGUtwktmiK1nuk,20635
|
|
8
8
|
langroid/agent/chat_agent.py,sha256=2HIYzYxkrGkRIS97ioKfIqjaW3RbX89M39LjzBobBEY,88381
|
9
9
|
langroid/agent/chat_document.py,sha256=6O20Fp4QrquykaF2jFtwNHkvcoDte1LLwVZNk9mVH9c,18057
|
10
10
|
langroid/agent/openai_assistant.py,sha256=JkAcs02bIrgPNVvUWVR06VCthc5-ulla2QMBzux_q6o,34340
|
11
|
-
langroid/agent/task.py,sha256=
|
11
|
+
langroid/agent/task.py,sha256=dJ2nRKc9-ulZyzNaABMKb9PKJzWRdZTonH9TPswpdbc,90801
|
12
12
|
langroid/agent/tool_message.py,sha256=BhjP-_TfQ2tgxuY4Yo_JHLOwwt0mJ4BwjPnREvEY4vk,14744
|
13
13
|
langroid/agent/xml_tool_message.py,sha256=oeBKnJNoGaKdtz39XoWGMTNlVyXew2MWH5lgtYeh8wQ,15496
|
14
14
|
langroid/agent/callbacks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -56,13 +56,13 @@ langroid/agent/tools/segment_extract_tool.py,sha256=__srZ_VGYLVOdPrITUM8S0HpmX4q
|
|
56
56
|
langroid/agent/tools/tavily_search_tool.py,sha256=soI-j0HdgVQLf09wRQScaEK4b5RpAX9C4cwOivRFWWI,1903
|
57
57
|
langroid/agent/tools/mcp/__init__.py,sha256=DJNM0VeFnFS3pJKCyFGggT8JVjVu0rBzrGzasT1HaSM,387
|
58
58
|
langroid/agent/tools/mcp/decorators.py,sha256=h7dterhsmvWJ8q4mp_OopmuG2DF71ty8cZwOyzdDZuk,1127
|
59
|
-
langroid/agent/tools/mcp/fastmcp_client.py,sha256=
|
59
|
+
langroid/agent/tools/mcp/fastmcp_client.py,sha256=0ohGFa47ZqMEFR0QNJbIgHC0M6WeWm-lepkW8S8oMQk,17440
|
60
60
|
langroid/cachedb/__init__.py,sha256=G2KyNnk3Qkhv7OKyxTOnpsxfDycx3NY0O_wXkJlalNY,96
|
61
61
|
langroid/cachedb/base.py,sha256=ztVjB1DtN6pLCujCWnR6xruHxwVj3XkYniRTYAKKqk0,1354
|
62
62
|
langroid/cachedb/redis_cachedb.py,sha256=7kgnbf4b5CKsCrlL97mHWKvdvlLt8zgn7lc528jEpiE,5141
|
63
63
|
langroid/embedding_models/__init__.py,sha256=KyYxR3jDFUCfYjSuCL86qjAmrq6mXXjOT4lFNOKVj6Y,955
|
64
64
|
langroid/embedding_models/base.py,sha256=Ml7oA6PzQm0wZmIYn3fhF7dvZCi-amviWUwOeBegH3A,2562
|
65
|
-
langroid/embedding_models/models.py,sha256=
|
65
|
+
langroid/embedding_models/models.py,sha256=g6BdBon6JzCIg5hYN07y6aCcWRJqCpGwCLkvXDPEeew,20787
|
66
66
|
langroid/embedding_models/remote_embeds.py,sha256=6_kjXByVbqhY9cGwl9R83ZcYC2km-nGieNNAo1McHaY,5151
|
67
67
|
langroid/embedding_models/protoc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
68
68
|
langroid/embedding_models/protoc/embeddings.proto,sha256=_O-SgFpTaylQeOTgSpxhEJ7CUw7PeCQQJLaPqpPYKJg,321
|
@@ -112,7 +112,7 @@ langroid/utils/configuration.py,sha256=ZkHHkEeWuS-o3_S4g0SE0wz-UK_of23NOWve1kpQi
|
|
112
112
|
langroid/utils/constants.py,sha256=CK09kda9bNDEhnwClq7ZTWZOh38guJlfcZ5hKUS1Ijo,1075
|
113
113
|
langroid/utils/git_utils.py,sha256=WnflJ3R3owhlD0LNdSJakcKhExcEehE1UW5jYVQl8JY,7955
|
114
114
|
langroid/utils/globals.py,sha256=Az9dOFqR6n9CoTYSqa2kLikQWS0oCQ9DFQIQAnG-2q8,1355
|
115
|
-
langroid/utils/logging.py,sha256=
|
115
|
+
langroid/utils/logging.py,sha256=zTcsd0Gsmar-BMUCoVyRJUOvKE6Nb54PRFu5coqCAmA,6844
|
116
116
|
langroid/utils/object_registry.py,sha256=iPz9GHzvmCeVoidB3JdAMEKcxJEqTdUr0otQEexDZ5s,2100
|
117
117
|
langroid/utils/pandas_utils.py,sha256=UctS986Jtl_MvU5rA7-GfrjEHXP7MNu8ePhepv0bTn0,755
|
118
118
|
langroid/utils/pydantic_utils.py,sha256=R7Ps8VP56-eSo-LYHWllFo-SJ2zDmdItuuYpUq2gGJ8,20854
|
@@ -133,7 +133,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
|
|
133
133
|
langroid/vector_store/postgres.py,sha256=wHPtIi2qM4fhO4pMQr95pz1ZCe7dTb2hxl4VYspGZoA,16104
|
134
134
|
langroid/vector_store/qdrantdb.py,sha256=O6dSBoDZ0jzfeVBd7LLvsXu083xs2fxXtPa9gGX3JX4,18443
|
135
135
|
langroid/vector_store/weaviatedb.py,sha256=Yn8pg139gOy3zkaPfoTbMXEEBCiLiYa1MU5d_3UA1K4,11847
|
136
|
-
langroid-0.53.
|
137
|
-
langroid-0.53.
|
138
|
-
langroid-0.53.
|
139
|
-
langroid-0.53.
|
136
|
+
langroid-0.53.13.dist-info/METADATA,sha256=MxvVyK7eqNOrqoRg3LEZ2s9v9kIgQZcTfg1BCErytmA,64946
|
137
|
+
langroid-0.53.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
138
|
+
langroid-0.53.13.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
|
139
|
+
langroid-0.53.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|