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
@@ -13,8 +13,8 @@ class AQLRetrievalTool(ToolMessage):
|
|
13
13
|
"""
|
14
14
|
aql_query: str
|
15
15
|
|
16
|
-
_max_result_tokens = 500
|
17
|
-
_max_retained_tokens = 200
|
16
|
+
_max_result_tokens: int = 500
|
17
|
+
_max_retained_tokens: int = 200
|
18
18
|
|
19
19
|
@classmethod
|
20
20
|
def examples(cls) -> List[ToolMessage | Tuple[str, ToolMessage]]:
|
@@ -101,7 +101,7 @@ class ArangoSchemaTool(ToolMessage):
|
|
101
101
|
properties: bool = True
|
102
102
|
collections: List[str] | None = None
|
103
103
|
|
104
|
-
_max_result_tokens = 500
|
104
|
+
_max_result_tokens: int = 500
|
105
105
|
|
106
106
|
|
107
107
|
arango_schema_tool_name = ArangoSchemaTool.default_value("request")
|
@@ -388,14 +388,14 @@ class DocChatAgent(ChatAgent):
|
|
388
388
|
p: (
|
389
389
|
m
|
390
390
|
if isinstance(m, dict)
|
391
|
-
else (isinstance(m, DocMetaData) and m.
|
391
|
+
else (isinstance(m, DocMetaData) and m.model_dump())
|
392
392
|
) # appease mypy
|
393
393
|
for p, m in zip(idxs, metadata)
|
394
394
|
}
|
395
395
|
elif isinstance(metadata, dict):
|
396
396
|
idx2meta = {p: metadata for p in idxs}
|
397
397
|
else:
|
398
|
-
idx2meta = {p: metadata.
|
398
|
+
idx2meta = {p: metadata.model_dump() for p in idxs}
|
399
399
|
urls_meta = {u: idx2meta[u] for u in url_idxs}
|
400
400
|
paths_meta = {p: idx2meta[p] for p in path_idxs}
|
401
401
|
docs: List[Document] = []
|
@@ -412,7 +412,7 @@ class DocChatAgent(ChatAgent):
|
|
412
412
|
# update metadata of each doc with meta
|
413
413
|
for d in url_docs:
|
414
414
|
orig_source = d.metadata.source
|
415
|
-
d.metadata = d.metadata.
|
415
|
+
d.metadata = d.metadata.model_copy(update=meta)
|
416
416
|
d.metadata.source = _append_metadata_source(
|
417
417
|
orig_source, meta.get("source", "")
|
418
418
|
)
|
@@ -429,7 +429,7 @@ class DocChatAgent(ChatAgent):
|
|
429
429
|
# update metadata of each doc with meta
|
430
430
|
for d in path_docs:
|
431
431
|
orig_source = d.metadata.source
|
432
|
-
d.metadata = d.metadata.
|
432
|
+
d.metadata = d.metadata.model_copy(update=meta)
|
433
433
|
d.metadata.source = _append_metadata_source(
|
434
434
|
orig_source, meta.get("source", "")
|
435
435
|
)
|
@@ -474,22 +474,22 @@ class DocChatAgent(ChatAgent):
|
|
474
474
|
if isinstance(metadata, list) and len(metadata) > 0:
|
475
475
|
for d, m in zip(docs, metadata):
|
476
476
|
orig_source = d.metadata.source
|
477
|
-
m_dict = m if isinstance(m, dict) else m.
|
478
|
-
d.metadata = d.metadata.
|
477
|
+
m_dict = m if isinstance(m, dict) else m.model_dump() # type: ignore
|
478
|
+
d.metadata = d.metadata.model_copy(update=m_dict) # type: ignore
|
479
479
|
d.metadata.source = _append_metadata_source(
|
480
480
|
orig_source, m_dict.get("source", "")
|
481
481
|
)
|
482
482
|
elif isinstance(metadata, dict):
|
483
483
|
for d in docs:
|
484
484
|
orig_source = d.metadata.source
|
485
|
-
d.metadata = d.metadata.
|
485
|
+
d.metadata = d.metadata.model_copy(update=metadata)
|
486
486
|
d.metadata.source = _append_metadata_source(
|
487
487
|
orig_source, metadata.get("source", "")
|
488
488
|
)
|
489
489
|
elif isinstance(metadata, DocMetaData):
|
490
490
|
for d in docs:
|
491
491
|
orig_source = d.metadata.source
|
492
|
-
d.metadata = d.metadata.
|
492
|
+
d.metadata = d.metadata.model_copy(update=metadata.model_dump())
|
493
493
|
d.metadata.source = _append_metadata_source(
|
494
494
|
orig_source, metadata.source
|
495
495
|
)
|
@@ -1025,10 +1025,12 @@ class DocChatAgent(ChatAgent):
|
|
1025
1025
|
f"{doc.content}{enrichment_config.delimiter}{enrichment}"
|
1026
1026
|
)
|
1027
1027
|
|
1028
|
-
new_doc = doc.
|
1028
|
+
new_doc = doc.model_copy(
|
1029
1029
|
update={
|
1030
1030
|
"content": combined_content,
|
1031
|
-
"metadata": doc.metadata.
|
1031
|
+
"metadata": doc.metadata.model_copy(
|
1032
|
+
update={"has_enrichment": True}
|
1033
|
+
),
|
1032
1034
|
}
|
1033
1035
|
)
|
1034
1036
|
augmented_docs.append(new_doc)
|
@@ -1212,7 +1214,7 @@ class DocChatAgent(ChatAgent):
|
|
1212
1214
|
return docs_scores
|
1213
1215
|
if len(docs_scores) == 0:
|
1214
1216
|
return []
|
1215
|
-
if set(docs_scores[0][0].
|
1217
|
+
if set(docs_scores[0][0].model_fields) != {"content", "metadata"}:
|
1216
1218
|
# Do not add context window when there are other fields besides just
|
1217
1219
|
# content and metadata, since we do not know how to set those other fields
|
1218
1220
|
# for newly created docs with combined content.
|
@@ -1515,7 +1517,7 @@ class DocChatAgent(ChatAgent):
|
|
1515
1517
|
delimiter = self.config.chunk_enrichment_config.delimiter
|
1516
1518
|
return [
|
1517
1519
|
(
|
1518
|
-
doc.
|
1520
|
+
doc.model_copy(update={"content": doc.content.split(delimiter)[0]})
|
1519
1521
|
if doc.content and getattr(doc.metadata, "has_enrichment", False)
|
1520
1522
|
else doc
|
1521
1523
|
)
|
@@ -1572,7 +1574,7 @@ class DocChatAgent(ChatAgent):
|
|
1572
1574
|
for p, e in zip(passages, extracts):
|
1573
1575
|
if e == NO_ANSWER or len(e) == 0:
|
1574
1576
|
continue
|
1575
|
-
p_copy = p.
|
1577
|
+
p_copy = p.model_copy()
|
1576
1578
|
p_copy.content = e
|
1577
1579
|
passage_extracts.append(p_copy)
|
1578
1580
|
|
@@ -1607,7 +1609,7 @@ class DocChatAgent(ChatAgent):
|
|
1607
1609
|
sender=Entity.LLM,
|
1608
1610
|
)
|
1609
1611
|
# copy metadata from first doc, unclear what to do here.
|
1610
|
-
meta.update(extracts[0].metadata)
|
1612
|
+
meta.update(extracts[0].metadata.model_dump())
|
1611
1613
|
return ChatDocument(
|
1612
1614
|
content="\n\n".join([e.content for e in extracts]),
|
1613
1615
|
metadata=ChatDocMetaData(**meta), # type: ignore
|
@@ -33,8 +33,8 @@ logger = logging.getLogger(__name__)
|
|
33
33
|
|
34
34
|
|
35
35
|
class QueryPlanCriticConfig(LanceQueryPlanAgentConfig):
|
36
|
-
name = "QueryPlanCritic"
|
37
|
-
system_message = f"""
|
36
|
+
name: str = "QueryPlanCritic"
|
37
|
+
system_message: str = f"""
|
38
38
|
You are an expert at carefully planning a query that needs to be answered
|
39
39
|
based on a large collection of documents. These docs have a special `content` field
|
40
40
|
and additional FILTERABLE fields in the SCHEMA below, along with the
|
@@ -37,11 +37,11 @@ class LanceQueryPlanAgentConfig(ChatAgentConfig):
|
|
37
37
|
critic_name: str = "QueryPlanCritic"
|
38
38
|
doc_agent_name: str = "LanceRAG"
|
39
39
|
doc_schema: str = ""
|
40
|
-
use_tools = False
|
40
|
+
use_tools: bool = False
|
41
41
|
max_retries: int = 5 # max number of retries for query plan
|
42
|
-
use_functions_api = True
|
42
|
+
use_functions_api: bool = True
|
43
43
|
|
44
|
-
system_message = """
|
44
|
+
system_message: str = """
|
45
45
|
You will receive a QUERY, to be answered based on an EXTREMELY LARGE collection
|
46
46
|
of documents you DO NOT have access to, but your ASSISTANT does.
|
47
47
|
You only know that these documents have a special `content` field
|
@@ -174,7 +174,7 @@ class LanceQueryPlanAgent(ChatAgent):
|
|
174
174
|
|
175
175
|
# (a) insert `recipient` in the QueryPlanTool:
|
176
176
|
# QPWithRecipient = QueryPlanTool.require_recipient()
|
177
|
-
# qp = QPWithRecipient(**msg.
|
177
|
+
# qp = QPWithRecipient(**msg.model_dump(), recipient=self.config.doc_agent_name)
|
178
178
|
# return qp
|
179
179
|
#
|
180
180
|
# OR
|
@@ -1,7 +1,8 @@
|
|
1
1
|
import logging
|
2
2
|
|
3
|
+
from pydantic import BaseModel, Field
|
4
|
+
|
3
5
|
from langroid.agent.tool_message import ToolMessage
|
4
|
-
from langroid.pydantic_v1 import BaseModel, Field
|
5
6
|
|
6
7
|
logger = logging.getLogger(__name__)
|
7
8
|
|
@@ -19,8 +20,8 @@ class QueryPlan(BaseModel):
|
|
19
20
|
|
20
21
|
|
21
22
|
class QueryPlanTool(ToolMessage):
|
22
|
-
request = "query_plan" # the agent method name that handles this tool
|
23
|
-
purpose = """
|
23
|
+
request: str = "query_plan" # the agent method name that handles this tool
|
24
|
+
purpose: str = """
|
24
25
|
Given a user's query, generate a query <plan> consisting of:
|
25
26
|
- <original_query> - the original query for reference
|
26
27
|
- <filter> condition if needed (or empty string if no filter is needed)
|
@@ -52,8 +53,8 @@ class QueryPlanAnswerTool(ToolMessage):
|
|
52
53
|
|
53
54
|
|
54
55
|
class QueryPlanFeedbackTool(ToolMessage):
|
55
|
-
request = "query_plan_feedback"
|
56
|
-
purpose = """
|
56
|
+
request: str = "query_plan_feedback"
|
57
|
+
purpose: str = """
|
57
58
|
To give <feedback> regarding the query plan,
|
58
59
|
along with a <suggested_fix> if any (empty string if no fix is suggested).
|
59
60
|
"""
|
@@ -0,0 +1,61 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from langroid.agent.tool_message import ToolMessage
|
4
|
+
from pydantic import BaseModel, Field
|
5
|
+
|
6
|
+
logger = logging.getLogger(__name__)
|
7
|
+
|
8
|
+
|
9
|
+
class QueryPlan(BaseModel):
|
10
|
+
original_query: str = Field(..., description="The original query for reference")
|
11
|
+
query: str = Field(..., description="A possibly NON-EMPTY rephrased query")
|
12
|
+
filter: str = Field(
|
13
|
+
"",
|
14
|
+
description="Filter condition if needed (or empty if no filter is needed)",
|
15
|
+
)
|
16
|
+
dataframe_calc: str = Field(
|
17
|
+
"", description="An optional Pandas-dataframe calculation/aggregation string"
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
class QueryPlanTool(ToolMessage):
|
22
|
+
request = "query_plan" # the agent method name that handles this tool
|
23
|
+
purpose = """
|
24
|
+
Given a user's query, generate a query <plan> consisting of:
|
25
|
+
- <original_query> - the original query for reference
|
26
|
+
- <filter> condition if needed (or empty string if no filter is needed)
|
27
|
+
- <query> - a possibly NON-EMPTY rephrased query that can be used to match the
|
28
|
+
CONTENT of the documents
|
29
|
+
(can be same as <original_query> if no rephrasing is needed)
|
30
|
+
- <dataframe_calc> - a Pandas-dataframe calculation/aggregation string
|
31
|
+
that can be used to calculate the answer
|
32
|
+
(or empty string if no calculation is needed).
|
33
|
+
"""
|
34
|
+
plan: QueryPlan
|
35
|
+
|
36
|
+
|
37
|
+
class AnswerTool(ToolMessage):
|
38
|
+
"""Wrapper for answer from LanceDocChatAgent"""
|
39
|
+
|
40
|
+
purpose: str = "To package the answer from LanceDocChatAgent"
|
41
|
+
request: str = "answer_tool"
|
42
|
+
answer: str
|
43
|
+
|
44
|
+
|
45
|
+
class QueryPlanAnswerTool(ToolMessage):
|
46
|
+
request: str = "query_plan_answer" # the agent method name that handles this tool
|
47
|
+
purpose: str = """
|
48
|
+
Assemble query <plan> and <answer>
|
49
|
+
"""
|
50
|
+
plan: QueryPlan
|
51
|
+
answer: str = Field(..., description="The answer received from the assistant")
|
52
|
+
|
53
|
+
|
54
|
+
class QueryPlanFeedbackTool(ToolMessage):
|
55
|
+
request = "query_plan_feedback"
|
56
|
+
purpose = """
|
57
|
+
To give <feedback> regarding the query plan,
|
58
|
+
along with a <suggested_fix> if any (empty string if no fix is suggested).
|
59
|
+
"""
|
60
|
+
feedback: str
|
61
|
+
suggested_fix: str
|
@@ -1,11 +1,11 @@
|
|
1
1
|
import logging
|
2
2
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
3
3
|
|
4
|
+
from pydantic import BaseModel
|
5
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
4
6
|
from rich import print
|
5
7
|
from rich.console import Console
|
6
8
|
|
7
|
-
from langroid.pydantic_v1 import BaseModel, BaseSettings
|
8
|
-
|
9
9
|
if TYPE_CHECKING:
|
10
10
|
import neo4j
|
11
11
|
|
@@ -47,11 +47,7 @@ class Neo4jSettings(BaseSettings):
|
|
47
47
|
password: str = ""
|
48
48
|
database: str = ""
|
49
49
|
|
50
|
-
|
51
|
-
# This enables the use of environment variables to set the settings,
|
52
|
-
# e.g. NEO4J_URI, NEO4J_USERNAME, etc.,
|
53
|
-
# which can either be set in a .env file or in the shell via export cmds.
|
54
|
-
env_prefix = "NEO4J_"
|
50
|
+
model_config = SettingsConfigDict(env_prefix="NEO4J_")
|
55
51
|
|
56
52
|
|
57
53
|
class QueryResult(BaseModel):
|