langroid 0.54.1__tar.gz → 0.55.0__tar.gz
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-0.54.1 → langroid-0.55.0}/PKG-INFO +2 -2
- {langroid-0.54.1 → langroid-0.55.0}/README.md +1 -1
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/base.py +190 -27
- langroid-0.55.0/langroid/agent/done_sequence_parser.py +151 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/task.py +261 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/mcp/fastmcp_client.py +143 -33
- {langroid-0.54.1 → langroid-0.55.0}/pyproject.toml +1 -1
- {langroid-0.54.1 → langroid-0.55.0}/.gitignore +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/LICENSE +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/batch.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/callbacks/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/callbacks/chainlit.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/chat_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/chat_document.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/openai_assistant.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/arangodb/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/arangodb/arangodb_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/arangodb/system_messages.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/arangodb/tools.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/arangodb/utils.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/doc_chat_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/doc_chat_task.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/lance_rag/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/lance_rag/lance_rag_task.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/lance_tools.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/neo4j/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/neo4j/system_messages.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/neo4j/tools.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/relevance_extractor_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/retriever_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/sql/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/sql/utils/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/sql/utils/system_message.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/sql/utils/tools.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/special/table_chat_agent.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tool_message.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/exa_search_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/file_tools.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/google_search_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/mcp/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/mcp/decorators.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/metaphor_search_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/orchestration.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/recipient_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/retrieval_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/rewind_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/segment_extract_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/tools/tavily_search_tool.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/agent/xml_tool_message.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/cachedb/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/cachedb/base.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/cachedb/redis_cachedb.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/base.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/models.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/protoc/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/protoc/embeddings.proto +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/embedding_models/remote_embeds.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/exceptions.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/azure_openai.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/base.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/config.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/mock_lm.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/model_info.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/openai_gpt.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/prompt_formatter/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/prompt_formatter/base.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/provider_params.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/language_models/utils.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/mytypes.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/agent_chats.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/code_parser.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/document_parser.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/file_attachment.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/md_parser.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/para_sentence_split.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/parse_json.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/parser.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/pdf_utils.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/repo_loader.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/routing.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/search.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/spider.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/table_loader.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/url_loader.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/urls.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/utils.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/parsing/web_search.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/prompts/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/prompts/dialog.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/prompts/prompts_config.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/prompts/templates.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/py.typed +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/pydantic_v1/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/pydantic_v1/main.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/algorithms/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/algorithms/graph.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/configuration.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/constants.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/git_utils.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/globals.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/logging.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/object_registry.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/output/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/output/citations.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/output/printing.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/output/status.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/pandas_utils.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/pydantic_utils.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/system.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/utils/types.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/__init__.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/base.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/chromadb.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/lancedb.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/meilisearch.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/pineconedb.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/postgres.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/qdrantdb.py +0 -0
- {langroid-0.54.1 → langroid-0.55.0}/langroid/vector_store/weaviatedb.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: langroid
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.55.0
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
Author-email: Prasad Chalasani <pchalasani@gmail.com>
|
6
6
|
License: MIT
|
@@ -209,7 +209,7 @@ Description-Content-Type: text/markdown
|
|
209
209
|
[](https://pypi.org/project/langroid/)
|
210
210
|
[](https://pypi.org/project/langroid/)
|
211
211
|
[](https://github.com/langroid/langroid/actions/workflows/pytest.yml)
|
212
|
-
[](https://codecov.io/gh/langroid/langroid)
|
213
213
|
[](https://github.com/langroid/langroid/actions/workflows/docker-publish.yml)
|
214
214
|
|
215
215
|
[](https://langroid.github.io/langroid)
|
@@ -8,7 +8,7 @@
|
|
8
8
|
[](https://pypi.org/project/langroid/)
|
9
9
|
[](https://pypi.org/project/langroid/)
|
10
10
|
[](https://github.com/langroid/langroid/actions/workflows/pytest.yml)
|
11
|
-
[](https://codecov.io/gh/langroid/langroid)
|
12
12
|
[](https://github.com/langroid/langroid/actions/workflows/docker-publish.yml)
|
13
13
|
|
14
14
|
[](https://langroid.github.io/langroid)
|
@@ -251,6 +251,172 @@ class Agent(ABC):
|
|
251
251
|
def clear_dialog(self) -> None:
|
252
252
|
self.dialog = []
|
253
253
|
|
254
|
+
def _analyze_handler_params(
|
255
|
+
self, handler_method: Any
|
256
|
+
) -> Tuple[bool, Optional[str], Optional[str]]:
|
257
|
+
"""
|
258
|
+
Analyze parameters of a handler method to determine their types.
|
259
|
+
|
260
|
+
Returns:
|
261
|
+
Tuple of (has_annotations, agent_param_name, chat_doc_param_name)
|
262
|
+
- has_annotations: True if useful type annotations were found
|
263
|
+
- agent_param_name: Name of the agent parameter if found
|
264
|
+
- chat_doc_param_name: Name of the chat_doc parameter if found
|
265
|
+
"""
|
266
|
+
sig = inspect.signature(handler_method)
|
267
|
+
params = list(sig.parameters.values())
|
268
|
+
# Remove 'self' parameter
|
269
|
+
params = [p for p in params if p.name != "self"]
|
270
|
+
|
271
|
+
agent_param = None
|
272
|
+
chat_doc_param = None
|
273
|
+
has_annotations = False
|
274
|
+
|
275
|
+
for param in params:
|
276
|
+
# First try type annotations
|
277
|
+
if param.annotation != inspect.Parameter.empty:
|
278
|
+
ann_str = str(param.annotation)
|
279
|
+
# Check for Agent-like types
|
280
|
+
if (
|
281
|
+
param.annotation == self.__class__
|
282
|
+
or "Agent" in ann_str
|
283
|
+
or (
|
284
|
+
hasattr(param.annotation, "__name__")
|
285
|
+
and "Agent" in param.annotation.__name__
|
286
|
+
)
|
287
|
+
):
|
288
|
+
agent_param = param.name
|
289
|
+
has_annotations = True
|
290
|
+
# Check for ChatDocument-like types
|
291
|
+
elif "ChatDocument" in ann_str or "ChatDoc" in ann_str:
|
292
|
+
chat_doc_param = param.name
|
293
|
+
has_annotations = True
|
294
|
+
|
295
|
+
# Fallback to parameter names
|
296
|
+
elif param.name == "agent":
|
297
|
+
agent_param = param.name
|
298
|
+
elif param.name == "chat_doc":
|
299
|
+
chat_doc_param = param.name
|
300
|
+
|
301
|
+
return has_annotations, agent_param, chat_doc_param
|
302
|
+
|
303
|
+
@no_type_check
|
304
|
+
def _create_handler_wrapper(
|
305
|
+
self,
|
306
|
+
message_class: Type[ToolMessage],
|
307
|
+
handler_method: Any,
|
308
|
+
is_async: bool = False,
|
309
|
+
) -> Any:
|
310
|
+
"""
|
311
|
+
Create a wrapper function for a handler method based on its signature.
|
312
|
+
|
313
|
+
Args:
|
314
|
+
message_class: The ToolMessage class
|
315
|
+
handler_method: The handle/handle_async method
|
316
|
+
is_async: Whether this is for an async handler
|
317
|
+
|
318
|
+
Returns:
|
319
|
+
Appropriate wrapper function
|
320
|
+
"""
|
321
|
+
sig = inspect.signature(handler_method)
|
322
|
+
params = list(sig.parameters.values())
|
323
|
+
params = [p for p in params if p.name != "self"]
|
324
|
+
|
325
|
+
has_annotations, agent_param, chat_doc_param = self._analyze_handler_params(
|
326
|
+
handler_method,
|
327
|
+
)
|
328
|
+
|
329
|
+
# Build wrapper based on found parameters
|
330
|
+
if len(params) == 0:
|
331
|
+
if is_async:
|
332
|
+
|
333
|
+
async def wrapper(obj: Any) -> Any:
|
334
|
+
return await obj.handle_async()
|
335
|
+
|
336
|
+
else:
|
337
|
+
|
338
|
+
def wrapper(obj: Any) -> Any:
|
339
|
+
return obj.handle()
|
340
|
+
|
341
|
+
elif agent_param and chat_doc_param:
|
342
|
+
# Both parameters present - build wrapper respecting their order
|
343
|
+
param_names = [p.name for p in params]
|
344
|
+
if param_names.index(agent_param) < param_names.index(chat_doc_param):
|
345
|
+
# agent is first parameter
|
346
|
+
if is_async:
|
347
|
+
|
348
|
+
async def wrapper(obj: Any, chat_doc: Any) -> Any:
|
349
|
+
return await obj.handle_async(self, chat_doc)
|
350
|
+
|
351
|
+
else:
|
352
|
+
|
353
|
+
def wrapper(obj: Any, chat_doc: Any) -> Any:
|
354
|
+
return obj.handle(self, chat_doc)
|
355
|
+
|
356
|
+
else:
|
357
|
+
# chat_doc is first parameter
|
358
|
+
if is_async:
|
359
|
+
|
360
|
+
async def wrapper(obj: Any, chat_doc: Any) -> Any:
|
361
|
+
return await obj.handle_async(chat_doc, self)
|
362
|
+
|
363
|
+
else:
|
364
|
+
|
365
|
+
def wrapper(obj: Any, chat_doc: Any) -> Any:
|
366
|
+
return obj.handle(chat_doc, self)
|
367
|
+
|
368
|
+
elif agent_param and not chat_doc_param:
|
369
|
+
# Only agent parameter
|
370
|
+
if is_async:
|
371
|
+
|
372
|
+
async def wrapper(obj: Any) -> Any:
|
373
|
+
return await obj.handle_async(self)
|
374
|
+
|
375
|
+
else:
|
376
|
+
|
377
|
+
def wrapper(obj: Any) -> Any:
|
378
|
+
return obj.handle(self)
|
379
|
+
|
380
|
+
elif chat_doc_param and not agent_param:
|
381
|
+
# Only chat_doc parameter
|
382
|
+
if is_async:
|
383
|
+
|
384
|
+
async def wrapper(obj: Any, chat_doc: Any) -> Any:
|
385
|
+
return await obj.handle_async(chat_doc)
|
386
|
+
|
387
|
+
else:
|
388
|
+
|
389
|
+
def wrapper(obj: Any, chat_doc: Any) -> Any:
|
390
|
+
return obj.handle(chat_doc)
|
391
|
+
|
392
|
+
else:
|
393
|
+
# No recognized parameters - backward compatibility
|
394
|
+
# Assume single parameter is chat_doc (legacy behavior)
|
395
|
+
if len(params) == 1:
|
396
|
+
if is_async:
|
397
|
+
|
398
|
+
async def wrapper(obj: Any, chat_doc: Any) -> Any:
|
399
|
+
return await obj.handle_async(chat_doc)
|
400
|
+
|
401
|
+
else:
|
402
|
+
|
403
|
+
def wrapper(obj: Any, chat_doc: Any) -> Any:
|
404
|
+
return obj.handle(chat_doc)
|
405
|
+
|
406
|
+
else:
|
407
|
+
# Multiple unrecognized parameters - best guess
|
408
|
+
if is_async:
|
409
|
+
|
410
|
+
async def wrapper(obj: Any, chat_doc: Any) -> Any:
|
411
|
+
return await obj.handle_async(chat_doc)
|
412
|
+
|
413
|
+
else:
|
414
|
+
|
415
|
+
def wrapper(obj: Any, chat_doc: Any) -> Any:
|
416
|
+
return obj.handle(chat_doc)
|
417
|
+
|
418
|
+
return wrapper
|
419
|
+
|
254
420
|
def _get_tool_list(
|
255
421
|
self, message_class: Optional[Type[ToolMessage]] = None
|
256
422
|
) -> List[str]:
|
@@ -304,13 +470,12 @@ class Agent(ABC):
|
|
304
470
|
in one place, i.e. in the message class.
|
305
471
|
See `tests/main/test_stateless_tool_messages.py` for an example.
|
306
472
|
"""
|
307
|
-
|
308
|
-
|
473
|
+
wrapper = self._create_handler_wrapper(
|
474
|
+
message_class,
|
475
|
+
message_class.handle,
|
476
|
+
is_async=False,
|
309
477
|
)
|
310
|
-
|
311
|
-
setattr(self, handler, lambda obj, chat_doc: obj.handle(chat_doc))
|
312
|
-
else:
|
313
|
-
setattr(self, handler, lambda obj: obj.handle())
|
478
|
+
setattr(self, handler, wrapper)
|
314
479
|
elif (
|
315
480
|
hasattr(message_class, "response")
|
316
481
|
and inspect.isfunction(message_class.response)
|
@@ -320,11 +485,17 @@ class Agent(ABC):
|
|
320
485
|
len(inspect.signature(message_class.response).parameters) > 2
|
321
486
|
)
|
322
487
|
if has_chat_doc_arg:
|
323
|
-
|
324
|
-
|
325
|
-
|
488
|
+
|
489
|
+
def response_wrapper_with_chat_doc(obj: Any, chat_doc: Any) -> Any:
|
490
|
+
return obj.response(self, chat_doc)
|
491
|
+
|
492
|
+
setattr(self, handler, response_wrapper_with_chat_doc)
|
326
493
|
else:
|
327
|
-
|
494
|
+
|
495
|
+
def response_wrapper_no_chat_doc(obj: Any) -> Any:
|
496
|
+
return obj.response(self)
|
497
|
+
|
498
|
+
setattr(self, handler, response_wrapper_no_chat_doc)
|
328
499
|
|
329
500
|
if hasattr(message_class, "handle_message_fallback") and (
|
330
501
|
inspect.isfunction(message_class.handle_message_fallback)
|
@@ -334,10 +505,13 @@ class Agent(ABC):
|
|
334
505
|
# `handle_message_fallback` method (which does nothing).
|
335
506
|
# It's possible multiple tool messages have a `handle_message_fallback`,
|
336
507
|
# in which case, the last one inserted will be used.
|
508
|
+
def fallback_wrapper(msg: Any) -> Any:
|
509
|
+
return message_class.handle_message_fallback(self, msg)
|
510
|
+
|
337
511
|
setattr(
|
338
512
|
self,
|
339
513
|
"handle_message_fallback",
|
340
|
-
|
514
|
+
fallback_wrapper,
|
341
515
|
)
|
342
516
|
|
343
517
|
async_handler_name = f"{handler}_async"
|
@@ -346,23 +520,12 @@ class Agent(ABC):
|
|
346
520
|
and inspect.isfunction(message_class.handle_async)
|
347
521
|
and not hasattr(self, async_handler_name)
|
348
522
|
):
|
349
|
-
|
350
|
-
|
523
|
+
wrapper = self._create_handler_wrapper(
|
524
|
+
message_class,
|
525
|
+
message_class.handle_async,
|
526
|
+
is_async=True,
|
351
527
|
)
|
352
|
-
|
353
|
-
if has_chat_doc_arg:
|
354
|
-
|
355
|
-
@no_type_check
|
356
|
-
async def handler(obj, chat_doc):
|
357
|
-
return await obj.handle_async(chat_doc)
|
358
|
-
|
359
|
-
else:
|
360
|
-
|
361
|
-
@no_type_check
|
362
|
-
async def handler(obj):
|
363
|
-
return await obj.handle_async()
|
364
|
-
|
365
|
-
setattr(self, async_handler_name, handler)
|
528
|
+
setattr(self, async_handler_name, wrapper)
|
366
529
|
elif (
|
367
530
|
hasattr(message_class, "response_async")
|
368
531
|
and inspect.isfunction(message_class.response_async)
|
@@ -0,0 +1,151 @@
|
|
1
|
+
"""Parser for done sequence DSL (Domain Specific Language).
|
2
|
+
|
3
|
+
Converts string patterns into DoneSequence objects for convenient task completion
|
4
|
+
configuration.
|
5
|
+
|
6
|
+
Examples:
|
7
|
+
"T, A" -> Tool followed by Agent response
|
8
|
+
"T[calculator], A" -> Specific tool 'calculator' followed by Agent response
|
9
|
+
"L, T, A, L" -> LLM, Tool, Agent, LLM sequence
|
10
|
+
"C[quit|exit]" -> Content matching regex pattern
|
11
|
+
"""
|
12
|
+
|
13
|
+
import re
|
14
|
+
from typing import List, Union
|
15
|
+
|
16
|
+
from langroid.pydantic_v1 import BaseModel
|
17
|
+
|
18
|
+
from .task import AgentEvent, DoneSequence, EventType
|
19
|
+
|
20
|
+
|
21
|
+
def parse_done_sequence(sequence: Union[str, DoneSequence]) -> DoneSequence:
|
22
|
+
"""Parse a string pattern or return existing DoneSequence unchanged.
|
23
|
+
|
24
|
+
Args:
|
25
|
+
sequence: Either a DoneSequence object or a string pattern to parse
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
DoneSequence object
|
29
|
+
|
30
|
+
Raises:
|
31
|
+
ValueError: If the string pattern is invalid
|
32
|
+
"""
|
33
|
+
if isinstance(sequence, DoneSequence):
|
34
|
+
return sequence
|
35
|
+
|
36
|
+
if not isinstance(sequence, str):
|
37
|
+
raise ValueError(f"Expected string or DoneSequence, got {type(sequence)}")
|
38
|
+
|
39
|
+
events = _parse_string_pattern(sequence)
|
40
|
+
return DoneSequence(events=events)
|
41
|
+
|
42
|
+
|
43
|
+
def _parse_string_pattern(pattern: str) -> List[AgentEvent]:
|
44
|
+
"""Parse a string pattern into a list of AgentEvent objects.
|
45
|
+
|
46
|
+
Pattern format:
|
47
|
+
- Single letter codes: T, A, L, U, N, C
|
48
|
+
- Specific tools: T[tool_name]
|
49
|
+
- Content match: C[regex_pattern]
|
50
|
+
- Separated by commas, spaces allowed
|
51
|
+
|
52
|
+
Args:
|
53
|
+
pattern: String pattern to parse
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
List of AgentEvent objects
|
57
|
+
|
58
|
+
Raises:
|
59
|
+
ValueError: If pattern is invalid
|
60
|
+
"""
|
61
|
+
events = []
|
62
|
+
|
63
|
+
# Split by comma and strip whitespace
|
64
|
+
parts = [p.strip() for p in pattern.split(",")]
|
65
|
+
|
66
|
+
for part in parts:
|
67
|
+
if not part:
|
68
|
+
continue
|
69
|
+
|
70
|
+
event = _parse_event_token(part)
|
71
|
+
events.append(event)
|
72
|
+
|
73
|
+
if not events:
|
74
|
+
raise ValueError(f"No valid events found in pattern: {pattern}")
|
75
|
+
|
76
|
+
return events
|
77
|
+
|
78
|
+
|
79
|
+
def _parse_event_token(token: str) -> AgentEvent:
|
80
|
+
"""Parse a single event token into an AgentEvent.
|
81
|
+
|
82
|
+
Args:
|
83
|
+
token: Single event token (e.g., "T", "T[calc]", "C[quit|exit]")
|
84
|
+
|
85
|
+
Returns:
|
86
|
+
AgentEvent object
|
87
|
+
|
88
|
+
Raises:
|
89
|
+
ValueError: If token is invalid
|
90
|
+
"""
|
91
|
+
# Check for bracket notation
|
92
|
+
bracket_match = re.match(r"^([A-Z])\[([^\]]+)\]$", token)
|
93
|
+
|
94
|
+
if bracket_match:
|
95
|
+
event_code = bracket_match.group(1)
|
96
|
+
param = bracket_match.group(2)
|
97
|
+
|
98
|
+
if event_code == "T":
|
99
|
+
# Specific tool: T[tool_name]
|
100
|
+
return AgentEvent(event_type=EventType.SPECIFIC_TOOL, tool_name=param)
|
101
|
+
elif event_code == "C":
|
102
|
+
# Content match: C[regex_pattern]
|
103
|
+
return AgentEvent(event_type=EventType.CONTENT_MATCH, content_pattern=param)
|
104
|
+
else:
|
105
|
+
raise ValueError(
|
106
|
+
f"Invalid event code with brackets: {event_code}. "
|
107
|
+
"Only T[tool] and C[pattern] are supported."
|
108
|
+
)
|
109
|
+
|
110
|
+
# Simple single-letter codes
|
111
|
+
event_map = {
|
112
|
+
"T": EventType.TOOL,
|
113
|
+
"A": EventType.AGENT_RESPONSE,
|
114
|
+
"L": EventType.LLM_RESPONSE,
|
115
|
+
"U": EventType.USER_RESPONSE,
|
116
|
+
"N": EventType.NO_RESPONSE,
|
117
|
+
"C": EventType.CONTENT_MATCH, # C without brackets matches any content
|
118
|
+
}
|
119
|
+
|
120
|
+
if token in event_map:
|
121
|
+
return AgentEvent(event_type=event_map[token])
|
122
|
+
|
123
|
+
# If not a single letter, could be a full event type name
|
124
|
+
token_upper = token.upper()
|
125
|
+
if token_upper == "TOOL":
|
126
|
+
return AgentEvent(event_type=EventType.TOOL)
|
127
|
+
elif token_upper == "AGENT":
|
128
|
+
return AgentEvent(event_type=EventType.AGENT_RESPONSE)
|
129
|
+
elif token_upper == "LLM":
|
130
|
+
return AgentEvent(event_type=EventType.LLM_RESPONSE)
|
131
|
+
elif token_upper == "USER":
|
132
|
+
return AgentEvent(event_type=EventType.USER_RESPONSE)
|
133
|
+
else:
|
134
|
+
raise ValueError(
|
135
|
+
f"Invalid event token: '{token}'. "
|
136
|
+
"Valid tokens are: T, A, L, U, N, C, or T[tool_name], C[pattern]"
|
137
|
+
)
|
138
|
+
|
139
|
+
|
140
|
+
def parse_done_sequences(
|
141
|
+
sequences: List[Union[str, DoneSequence]]
|
142
|
+
) -> List[DoneSequence]:
|
143
|
+
"""Parse a list of mixed string patterns and DoneSequence objects.
|
144
|
+
|
145
|
+
Args:
|
146
|
+
sequences: List containing strings and/or DoneSequence objects
|
147
|
+
|
148
|
+
Returns:
|
149
|
+
List of DoneSequence objects
|
150
|
+
"""
|
151
|
+
return [parse_done_sequence(seq) for seq in sequences]
|