langroid 0.51.1__tar.gz → 0.52.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.51.1 → langroid-0.52.0}/PKG-INFO +2 -2
- {langroid-0.51.1 → langroid-0.52.0}/README.md +1 -1
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/base.py +7 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/chat_agent.py +17 -8
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/chat_document.py +10 -6
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/doc_chat_agent.py +2 -2
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/table_chat_agent.py +2 -2
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/base.py +27 -4
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/model_info.py +33 -1
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/openai_gpt.py +33 -9
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/document_parser.py +24 -19
- langroid-0.52.0/langroid/parsing/file_attachment.py +157 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/parser.py +3 -0
- {langroid-0.51.1 → langroid-0.52.0}/pyproject.toml +1 -1
- {langroid-0.51.1 → langroid-0.52.0}/.gitignore +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/LICENSE +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/batch.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/callbacks/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/callbacks/chainlit.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/openai_assistant.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/arangodb/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/arangodb/arangodb_agent.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/arangodb/system_messages.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/arangodb/tools.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/arangodb/utils.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/doc_chat_task.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/lance_doc_chat_agent.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/lance_rag/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/lance_rag/critic_agent.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/lance_rag/lance_rag_task.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/lance_rag/query_planner_agent.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/lance_tools.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/neo4j/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/neo4j/csv_kg_chat.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/neo4j/neo4j_chat_agent.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/neo4j/system_messages.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/neo4j/tools.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/relevance_extractor_agent.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/retriever_agent.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/sql/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/sql/sql_chat_agent.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/sql/utils/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/sql/utils/description_extractors.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/sql/utils/populate_metadata.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/sql/utils/system_message.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/special/sql/utils/tools.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/task.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tool_message.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/duckduckgo_search_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/exa_search_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/file_tools.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/google_search_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/metaphor_search_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/orchestration.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/recipient_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/retrieval_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/rewind_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/segment_extract_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/tools/tavily_search_tool.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/agent/xml_tool_message.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/cachedb/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/cachedb/base.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/cachedb/momento_cachedb.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/cachedb/redis_cachedb.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/base.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/models.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/protoc/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/protoc/embeddings.proto +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/protoc/embeddings_pb2.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/protoc/embeddings_pb2.pyi +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/protoc/embeddings_pb2_grpc.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/embedding_models/remote_embeds.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/exceptions.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/azure_openai.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/config.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/mock_lm.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/prompt_formatter/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/prompt_formatter/base.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/prompt_formatter/hf_formatter.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/prompt_formatter/llama2_formatter.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/language_models/utils.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/mytypes.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/agent_chats.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/code_parser.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/md_parser.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/para_sentence_split.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/parse_json.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/pdf_utils.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/repo_loader.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/routing.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/search.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/spider.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/table_loader.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/url_loader.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/urls.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/utils.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/parsing/web_search.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/prompts/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/prompts/dialog.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/prompts/prompts_config.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/prompts/templates.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/py.typed +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/pydantic_v1/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/pydantic_v1/main.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/algorithms/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/algorithms/graph.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/configuration.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/constants.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/git_utils.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/globals.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/logging.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/object_registry.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/output/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/output/citations.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/output/printing.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/output/status.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/pandas_utils.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/pydantic_utils.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/system.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/utils/types.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/vector_store/__init__.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/vector_store/base.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/vector_store/chromadb.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/vector_store/lancedb.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/vector_store/meilisearch.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/vector_store/pineconedb.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/vector_store/postgres.py +0 -0
- {langroid-0.51.1 → langroid-0.52.0}/langroid/vector_store/qdrantdb.py +0 -0
- {langroid-0.51.1 → langroid-0.52.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.52.0
|
4
4
|
Summary: Harness LLMs with Multi-Agent Programming
|
5
5
|
Author-email: Prasad Chalasani <pchalasani@gmail.com>
|
6
6
|
License: MIT
|
@@ -846,7 +846,7 @@ import langroid.language_models as lm
|
|
846
846
|
|
847
847
|
mdl = lm.OpenAIGPT(
|
848
848
|
lm.OpenAIGPTConfig(
|
849
|
-
chat_model=lm.OpenAIChatModel.
|
849
|
+
chat_model=lm.OpenAIChatModel.GPT4o, # or, e.g. "ollama/qwen2.5"
|
850
850
|
),
|
851
851
|
)
|
852
852
|
|
@@ -47,6 +47,7 @@ from langroid.language_models.base import (
|
|
47
47
|
)
|
48
48
|
from langroid.language_models.openai_gpt import OpenAIGPT, OpenAIGPTConfig
|
49
49
|
from langroid.mytypes import Entity
|
50
|
+
from langroid.parsing.file_attachment import FileAttachment
|
50
51
|
from langroid.parsing.parse_json import extract_top_level_json
|
51
52
|
from langroid.parsing.parser import Parser, ParsingConfig
|
52
53
|
from langroid.prompts.prompts_config import PromptsConfig
|
@@ -440,6 +441,7 @@ class Agent(ABC):
|
|
440
441
|
def create_agent_response(
|
441
442
|
self,
|
442
443
|
content: str | None = None,
|
444
|
+
files: List[FileAttachment] = [],
|
443
445
|
content_any: Any = None,
|
444
446
|
tool_messages: List[ToolMessage] = [],
|
445
447
|
oai_tool_calls: Optional[List[OpenAIToolCall]] = None,
|
@@ -452,6 +454,7 @@ class Agent(ABC):
|
|
452
454
|
return self.response_template(
|
453
455
|
Entity.AGENT,
|
454
456
|
content=content,
|
457
|
+
files=files,
|
455
458
|
content_any=content_any,
|
456
459
|
tool_messages=tool_messages,
|
457
460
|
oai_tool_calls=oai_tool_calls,
|
@@ -689,6 +692,7 @@ class Agent(ABC):
|
|
689
692
|
self,
|
690
693
|
e: Entity,
|
691
694
|
content: str | None = None,
|
695
|
+
files: List[FileAttachment] = [],
|
692
696
|
content_any: Any = None,
|
693
697
|
tool_messages: List[ToolMessage] = [],
|
694
698
|
oai_tool_calls: Optional[List[OpenAIToolCall]] = None,
|
@@ -700,6 +704,7 @@ class Agent(ABC):
|
|
700
704
|
"""Template for response from entity `e`."""
|
701
705
|
return ChatDocument(
|
702
706
|
content=content or "",
|
707
|
+
files=files,
|
703
708
|
content_any=content_any,
|
704
709
|
tool_messages=tool_messages,
|
705
710
|
oai_tool_calls=oai_tool_calls,
|
@@ -714,6 +719,7 @@ class Agent(ABC):
|
|
714
719
|
def create_user_response(
|
715
720
|
self,
|
716
721
|
content: str | None = None,
|
722
|
+
files: List[FileAttachment] = [],
|
717
723
|
content_any: Any = None,
|
718
724
|
tool_messages: List[ToolMessage] = [],
|
719
725
|
oai_tool_calls: List[OpenAIToolCall] | None = None,
|
@@ -726,6 +732,7 @@ class Agent(ABC):
|
|
726
732
|
return self.response_template(
|
727
733
|
e=Entity.USER,
|
728
734
|
content=content,
|
735
|
+
files=files,
|
729
736
|
content_any=content_any,
|
730
737
|
tool_messages=tool_messages,
|
731
738
|
oai_tool_calls=oai_tool_calls,
|
@@ -1511,12 +1511,14 @@ class ChatAgent(Agent):
|
|
1511
1511
|
output_len = self.config.llm.model_max_output_tokens
|
1512
1512
|
if (
|
1513
1513
|
truncate
|
1514
|
-
and self.chat_num_tokens(hist)
|
1515
|
-
> self.llm.chat_context_length() - self.config.llm.model_max_output_tokens
|
1514
|
+
and output_len > self.llm.chat_context_length() - self.chat_num_tokens(hist)
|
1516
1515
|
):
|
1517
1516
|
# chat + output > max context length,
|
1518
|
-
# so first try to shorten requested output len to fit
|
1519
|
-
|
1517
|
+
# so first try to shorten requested output len to fit;
|
1518
|
+
# use an extra margin of 300 tokens in case our calcs are off
|
1519
|
+
output_len = (
|
1520
|
+
self.llm.chat_context_length() - self.chat_num_tokens(hist) - 300
|
1521
|
+
)
|
1520
1522
|
if output_len < self.config.llm.min_output_tokens:
|
1521
1523
|
# unacceptably small output len, so drop early parts of conv history
|
1522
1524
|
# if output_len is still too long, then drop early parts of conv history
|
@@ -1534,10 +1536,17 @@ class ChatAgent(Agent):
|
|
1534
1536
|
# and last message (user msg).
|
1535
1537
|
raise ValueError(
|
1536
1538
|
"""
|
1537
|
-
The message history is longer than the
|
1538
|
-
length
|
1539
|
-
|
1540
|
-
|
1539
|
+
The (message history + max_output_tokens) is longer than the
|
1540
|
+
max chat context length of this model, and we have tried
|
1541
|
+
reducing the requested max output tokens, as well as dropping
|
1542
|
+
early parts of the message history, to accommodate the model
|
1543
|
+
context length, but we have run out of msgs to drop.
|
1544
|
+
|
1545
|
+
HINT: In the `llm` field of your `ChatAgentConfig` object,
|
1546
|
+
which is of type `LLMConfig/OpenAIGPTConfig`, try
|
1547
|
+
- increasing `chat_context_length`
|
1548
|
+
(if accurate for the model), or
|
1549
|
+
- decreasing `max_output_tokens`
|
1541
1550
|
"""
|
1542
1551
|
)
|
1543
1552
|
# drop the second message, i.e. first msg after the sys msg
|
@@ -19,6 +19,7 @@ from langroid.language_models.base import (
|
|
19
19
|
)
|
20
20
|
from langroid.mytypes import DocMetaData, Document, Entity
|
21
21
|
from langroid.parsing.agent_chats import parse_message
|
22
|
+
from langroid.parsing.file_attachment import FileAttachment
|
22
23
|
from langroid.parsing.parse_json import extract_top_level_json, top_level_json_field
|
23
24
|
from langroid.pydantic_v1 import BaseModel, Extra
|
24
25
|
from langroid.utils.object_registry import ObjectRegistry
|
@@ -119,6 +120,7 @@ class ChatDocument(Document):
|
|
119
120
|
|
120
121
|
reasoning: str = "" # reasoning produced by a reasoning LLM
|
121
122
|
content_any: Any = None # to hold arbitrary data returned by responders
|
123
|
+
files: List[FileAttachment] = [] # list of file attachments
|
122
124
|
oai_tool_calls: Optional[List[OpenAIToolCall]] = None
|
123
125
|
oai_tool_id2result: Optional[OrderedDict[str, str]] = None
|
124
126
|
oai_tool_choice: ToolChoiceTypes | Dict[str, Dict[str, str] | str] = "auto"
|
@@ -356,12 +358,8 @@ class ChatDocument(Document):
|
|
356
358
|
Returns:
|
357
359
|
List[LLMMessage]: list of LLMMessages corresponding to this ChatDocument.
|
358
360
|
"""
|
359
|
-
|
361
|
+
|
360
362
|
sender_role = Role.USER
|
361
|
-
fun_call = None
|
362
|
-
oai_tool_calls = None
|
363
|
-
tool_id = "" # for OpenAI Assistant
|
364
|
-
chat_document_id: str = ""
|
365
363
|
if isinstance(message, str):
|
366
364
|
message = ChatDocument.from_str(message)
|
367
365
|
content = message.content or to_string(message.content_any) or ""
|
@@ -381,6 +379,8 @@ class ChatDocument(Document):
|
|
381
379
|
# same reasoning as for function-call above
|
382
380
|
content += " " + "\n\n".join(str(tc) for tc in oai_tool_calls)
|
383
381
|
oai_tool_calls = None
|
382
|
+
# some LLM APIs (e.g. gemini) don't like empty msg
|
383
|
+
content = content or " "
|
384
384
|
sender_name = message.metadata.sender_name
|
385
385
|
tool_ids = message.metadata.tool_ids
|
386
386
|
tool_id = tool_ids[-1] if len(tool_ids) > 0 else ""
|
@@ -409,6 +409,7 @@ class ChatDocument(Document):
|
|
409
409
|
role=Role.TOOL,
|
410
410
|
tool_call_id=oai_tools[0].id,
|
411
411
|
content=content,
|
412
|
+
files=message.files,
|
412
413
|
chat_document_id=chat_document_id,
|
413
414
|
)
|
414
415
|
]
|
@@ -424,6 +425,7 @@ class ChatDocument(Document):
|
|
424
425
|
role=Role.TOOL,
|
425
426
|
tool_call_id=message.metadata.oai_tool_id,
|
426
427
|
content=content,
|
428
|
+
files=message.files,
|
427
429
|
chat_document_id=chat_document_id,
|
428
430
|
)
|
429
431
|
]
|
@@ -437,7 +439,8 @@ class ChatDocument(Document):
|
|
437
439
|
LLMMessage(
|
438
440
|
role=Role.TOOL,
|
439
441
|
tool_call_id=tool_id,
|
440
|
-
content=result,
|
442
|
+
content=result or " ",
|
443
|
+
files=message.files,
|
441
444
|
chat_document_id=chat_document_id,
|
442
445
|
)
|
443
446
|
for tool_id, result in message.oai_tool_id2result.items()
|
@@ -450,6 +453,7 @@ class ChatDocument(Document):
|
|
450
453
|
role=sender_role,
|
451
454
|
tool_id=tool_id, # for OpenAI Assistant
|
452
455
|
content=content,
|
456
|
+
files=message.files,
|
453
457
|
function_call=fun_call,
|
454
458
|
tool_calls=oai_tool_calls,
|
455
459
|
name=sender_name,
|
@@ -204,8 +204,8 @@ class DocChatAgentConfig(ChatAgentConfig):
|
|
204
204
|
|
205
205
|
llm: OpenAIGPTConfig = OpenAIGPTConfig(
|
206
206
|
type="openai",
|
207
|
-
chat_model=OpenAIChatModel.
|
208
|
-
completion_model=OpenAIChatModel.
|
207
|
+
chat_model=OpenAIChatModel.GPT4o,
|
208
|
+
completion_model=OpenAIChatModel.GPT4o,
|
209
209
|
timeout=40,
|
210
210
|
)
|
211
211
|
prompts: PromptsConfig = PromptsConfig(
|
@@ -118,8 +118,8 @@ class TableChatAgentConfig(ChatAgentConfig):
|
|
118
118
|
vecdb: None | VectorStoreConfig = None
|
119
119
|
llm: OpenAIGPTConfig = OpenAIGPTConfig(
|
120
120
|
type="openai",
|
121
|
-
chat_model=OpenAIChatModel.
|
122
|
-
completion_model=OpenAIChatModel.
|
121
|
+
chat_model=OpenAIChatModel.GPT4o,
|
122
|
+
completion_model=OpenAIChatModel.GPT4o,
|
123
123
|
)
|
124
124
|
prompts: PromptsConfig = PromptsConfig(
|
125
125
|
max_tokens=1000,
|
@@ -21,6 +21,7 @@ from langroid.cachedb.base import CacheDBConfig
|
|
21
21
|
from langroid.cachedb.redis_cachedb import RedisCacheConfig
|
22
22
|
from langroid.language_models.model_info import ModelInfo, get_model_info
|
23
23
|
from langroid.parsing.agent_chats import parse_message
|
24
|
+
from langroid.parsing.file_attachment import FileAttachment
|
24
25
|
from langroid.parsing.parse_json import parse_imperfect_json, top_level_json_field
|
25
26
|
from langroid.prompts.dialog import collate_chat_history
|
26
27
|
from langroid.pydantic_v1 import BaseModel, BaseSettings, Field
|
@@ -53,6 +54,13 @@ class StreamEventType(Enum):
|
|
53
54
|
TOOL_ARGS = 5
|
54
55
|
|
55
56
|
|
57
|
+
class RetryParams(BaseSettings):
|
58
|
+
max_retries: int = 5
|
59
|
+
initial_delay: float = 1.0
|
60
|
+
exponential_base: float = 1.3
|
61
|
+
jitter: bool = True
|
62
|
+
|
63
|
+
|
56
64
|
class LLMConfig(BaseSettings):
|
57
65
|
"""
|
58
66
|
Common configuration for all language models.
|
@@ -63,7 +71,8 @@ class LLMConfig(BaseSettings):
|
|
63
71
|
streamer_async: Optional[Callable[..., Awaitable[None]]] = async_noop_fn
|
64
72
|
api_base: str | None = None
|
65
73
|
formatter: None | str = None
|
66
|
-
|
74
|
+
# specify None if you want to use the full max output tokens of the model
|
75
|
+
max_output_tokens: int | None = 8192
|
67
76
|
timeout: int = 20 # timeout for API requests
|
68
77
|
chat_model: str = ""
|
69
78
|
completion_model: str = ""
|
@@ -86,11 +95,13 @@ class LLMConfig(BaseSettings):
|
|
86
95
|
# Dict of model -> (input/prompt cost, output/completion cost)
|
87
96
|
chat_cost_per_1k_tokens: Tuple[float, float] = (0.0, 0.0)
|
88
97
|
completion_cost_per_1k_tokens: Tuple[float, float] = (0.0, 0.0)
|
98
|
+
retry_params: RetryParams = RetryParams()
|
89
99
|
|
90
100
|
@property
|
91
101
|
def model_max_output_tokens(self) -> int:
|
92
|
-
return (
|
93
|
-
self.max_output_tokens or get_model_info(self.chat_model).max_output_tokens
|
102
|
+
return min(
|
103
|
+
self.max_output_tokens or get_model_info(self.chat_model).max_output_tokens,
|
104
|
+
get_model_info(self.chat_model).max_output_tokens,
|
94
105
|
)
|
95
106
|
|
96
107
|
|
@@ -263,13 +274,14 @@ class LLMMessage(BaseModel):
|
|
263
274
|
tool_call_id: Optional[str] = None # which OpenAI LLM tool this is a response to
|
264
275
|
tool_id: str = "" # used by OpenAIAssistant
|
265
276
|
content: str
|
277
|
+
files: List[FileAttachment] = []
|
266
278
|
function_call: Optional[LLMFunctionCall] = None
|
267
279
|
tool_calls: Optional[List[OpenAIToolCall]] = None
|
268
280
|
timestamp: datetime = Field(default_factory=datetime.utcnow)
|
269
281
|
# link to corresponding chat document, for provenance/rewind purposes
|
270
282
|
chat_document_id: str = ""
|
271
283
|
|
272
|
-
def api_dict(self, has_system_role: bool = True) -> Dict[str, Any]:
|
284
|
+
def api_dict(self, model: str, has_system_role: bool = True) -> Dict[str, Any]:
|
273
285
|
"""
|
274
286
|
Convert to dictionary for API request, keeping ONLY
|
275
287
|
the fields that are expected in an API call!
|
@@ -283,6 +295,17 @@ class LLMMessage(BaseModel):
|
|
283
295
|
dict: dictionary representation of LLM message
|
284
296
|
"""
|
285
297
|
d = self.dict()
|
298
|
+
files: List[FileAttachment] = d.pop("files")
|
299
|
+
if len(files) > 0 and self.role == Role.USER:
|
300
|
+
# In there are files, then content is an array of
|
301
|
+
# different content-parts
|
302
|
+
d["content"] = [
|
303
|
+
dict(
|
304
|
+
type="text",
|
305
|
+
text=self.content,
|
306
|
+
)
|
307
|
+
] + [f.to_dict(model) for f in self.files]
|
308
|
+
|
286
309
|
# if there is a key k = "role" with value "system", change to "user"
|
287
310
|
# in case has_system_role is False
|
288
311
|
if not has_system_role and "role" in d and d["role"] == "system":
|
@@ -24,13 +24,16 @@ class OpenAIChatModel(ModelName):
|
|
24
24
|
"""Enum for OpenAI Chat models"""
|
25
25
|
|
26
26
|
GPT3_5_TURBO = "gpt-3.5-turbo-1106"
|
27
|
-
GPT4 = "gpt-4
|
27
|
+
GPT4 = "gpt-4o" # avoid deprecated gpt-4
|
28
28
|
GPT4_TURBO = "gpt-4-turbo"
|
29
29
|
GPT4o = "gpt-4o"
|
30
30
|
GPT4o_MINI = "gpt-4o-mini"
|
31
31
|
O1 = "o1"
|
32
32
|
O1_MINI = "o1-mini"
|
33
33
|
O3_MINI = "o3-mini"
|
34
|
+
GPT4_1 = "gpt-4.1"
|
35
|
+
GPT4_1_MINI = "gpt-4.1-mini"
|
36
|
+
GPT4_1_NANO = "gpt-4.1-nano"
|
34
37
|
|
35
38
|
|
36
39
|
class OpenAICompletionModel(str, Enum):
|
@@ -44,6 +47,7 @@ class AnthropicModel(ModelName):
|
|
44
47
|
"""Enum for Anthropic models"""
|
45
48
|
|
46
49
|
CLAUDE_3_5_SONNET = "claude-3-5-sonnet-latest"
|
50
|
+
CLAUDE_3_7_SONNET = "claude-3-7-sonnet-latest"
|
47
51
|
CLAUDE_3_OPUS = "claude-3-opus-latest"
|
48
52
|
CLAUDE_3_SONNET = "claude-3-sonnet-20240229"
|
49
53
|
CLAUDE_3_HAIKU = "claude-3-haiku-20240307"
|
@@ -63,6 +67,7 @@ class GeminiModel(ModelName):
|
|
63
67
|
GEMINI_1_5_FLASH = "gemini-1.5-flash"
|
64
68
|
GEMINI_1_5_FLASH_8B = "gemini-1.5-flash-8b"
|
65
69
|
GEMINI_1_5_PRO = "gemini-1.5-pro"
|
70
|
+
GEMINI_2_5_PRO = "gemini-2.5-pro-exp-02-05"
|
66
71
|
GEMINI_2_PRO = "gemini-2.0-pro-exp-02-05"
|
67
72
|
GEMINI_2_FLASH = "gemini-2.0-flash"
|
68
73
|
GEMINI_2_FLASH_LITE = "gemini-2.0-flash-lite-preview"
|
@@ -160,6 +165,33 @@ MODEL_INFO: Dict[str, ModelInfo] = {
|
|
160
165
|
output_cost_per_million=30.0,
|
161
166
|
description="GPT-4 Turbo",
|
162
167
|
),
|
168
|
+
OpenAIChatModel.GPT4_1_NANO.value: ModelInfo(
|
169
|
+
name=OpenAIChatModel.GPT4_1_NANO.value,
|
170
|
+
provider=ModelProvider.OPENAI,
|
171
|
+
context_length=1_047_576,
|
172
|
+
max_output_tokens=32_768,
|
173
|
+
input_cost_per_million=0.10,
|
174
|
+
output_cost_per_million=0.40,
|
175
|
+
description="GPT-4.1",
|
176
|
+
),
|
177
|
+
OpenAIChatModel.GPT4_1_MINI.value: ModelInfo(
|
178
|
+
name=OpenAIChatModel.GPT4_1_MINI.value,
|
179
|
+
provider=ModelProvider.OPENAI,
|
180
|
+
context_length=1_047_576,
|
181
|
+
max_output_tokens=32_768,
|
182
|
+
input_cost_per_million=0.40,
|
183
|
+
output_cost_per_million=1.60,
|
184
|
+
description="GPT-4.1 Mini",
|
185
|
+
),
|
186
|
+
OpenAIChatModel.GPT4_1.value: ModelInfo(
|
187
|
+
name=OpenAIChatModel.GPT4_1.value,
|
188
|
+
provider=ModelProvider.OPENAI,
|
189
|
+
context_length=1_047_576,
|
190
|
+
max_output_tokens=32_768,
|
191
|
+
input_cost_per_million=2.00,
|
192
|
+
output_cost_per_million=8.00,
|
193
|
+
description="GPT-4.1",
|
194
|
+
),
|
163
195
|
OpenAIChatModel.GPT4o.value: ModelInfo(
|
164
196
|
name=OpenAIChatModel.GPT4o.value,
|
165
197
|
provider=ModelProvider.OPENAI,
|
@@ -91,10 +91,13 @@ LLAMACPP_API_KEY = os.environ.get("LLAMA_API_KEY", DUMMY_API_KEY)
|
|
91
91
|
|
92
92
|
openai_chat_model_pref_list = [
|
93
93
|
OpenAIChatModel.GPT4o,
|
94
|
+
OpenAIChatModel.GPT4_1_NANO,
|
95
|
+
OpenAIChatModel.GPT4_1_MINI,
|
96
|
+
OpenAIChatModel.GPT4_1,
|
94
97
|
OpenAIChatModel.GPT4o_MINI,
|
95
98
|
OpenAIChatModel.O1_MINI,
|
99
|
+
OpenAIChatModel.O3_MINI,
|
96
100
|
OpenAIChatModel.O1,
|
97
|
-
OpenAIChatModel.GPT3_5_TURBO,
|
98
101
|
]
|
99
102
|
|
100
103
|
openai_completion_model_pref_list = [
|
@@ -1731,8 +1734,7 @@ class OpenAIGPT(LanguageModel):
|
|
1731
1734
|
logging.error(friendly_error(e, "Error in OpenAIGPT.achat: "))
|
1732
1735
|
raise e
|
1733
1736
|
|
1734
|
-
|
1735
|
-
def _chat_completions_with_backoff(self, **kwargs): # type: ignore
|
1737
|
+
def _chat_completions_with_backoff_body(self, **kwargs): # type: ignore
|
1736
1738
|
cached = False
|
1737
1739
|
hashed_key, result = self._cache_lookup("Completion", **kwargs)
|
1738
1740
|
if result is not None:
|
@@ -1781,8 +1783,17 @@ class OpenAIGPT(LanguageModel):
|
|
1781
1783
|
self._cache_store(hashed_key, result.model_dump())
|
1782
1784
|
return cached, hashed_key, result
|
1783
1785
|
|
1784
|
-
|
1785
|
-
|
1786
|
+
def _chat_completions_with_backoff(self, **kwargs): # type: ignore
|
1787
|
+
retry_func = retry_with_exponential_backoff(
|
1788
|
+
self._chat_completions_with_backoff_body,
|
1789
|
+
initial_delay=self.config.retry_params.initial_delay,
|
1790
|
+
max_retries=self.config.retry_params.max_retries,
|
1791
|
+
exponential_base=self.config.retry_params.exponential_base,
|
1792
|
+
jitter=self.config.retry_params.jitter,
|
1793
|
+
)
|
1794
|
+
return retry_func(**kwargs)
|
1795
|
+
|
1796
|
+
async def _achat_completions_with_backoff_body(self, **kwargs): # type: ignore
|
1786
1797
|
cached = False
|
1787
1798
|
hashed_key, result = self._cache_lookup("Completion", **kwargs)
|
1788
1799
|
if result is not None:
|
@@ -1836,6 +1847,16 @@ class OpenAIGPT(LanguageModel):
|
|
1836
1847
|
self._cache_store(hashed_key, result.model_dump())
|
1837
1848
|
return cached, hashed_key, result
|
1838
1849
|
|
1850
|
+
async def _achat_completions_with_backoff(self, **kwargs): # type: ignore
|
1851
|
+
retry_func = async_retry_with_exponential_backoff(
|
1852
|
+
self._achat_completions_with_backoff_body,
|
1853
|
+
initial_delay=self.config.retry_params.initial_delay,
|
1854
|
+
max_retries=self.config.retry_params.max_retries,
|
1855
|
+
exponential_base=self.config.retry_params.exponential_base,
|
1856
|
+
jitter=self.config.retry_params.jitter,
|
1857
|
+
)
|
1858
|
+
return await retry_func(**kwargs)
|
1859
|
+
|
1839
1860
|
def _prep_chat_completion(
|
1840
1861
|
self,
|
1841
1862
|
messages: Union[str, List[LLMMessage]],
|
@@ -1876,10 +1897,13 @@ class OpenAIGPT(LanguageModel):
|
|
1876
1897
|
args: Dict[str, Any] = dict(
|
1877
1898
|
model=chat_model,
|
1878
1899
|
messages=[
|
1879
|
-
m.api_dict(
|
1900
|
+
m.api_dict(
|
1901
|
+
self.config.chat_model,
|
1902
|
+
has_system_role=self.info().allows_system_message,
|
1903
|
+
)
|
1880
1904
|
for m in (llm_messages)
|
1881
1905
|
],
|
1882
|
-
|
1906
|
+
max_completion_tokens=max_tokens,
|
1883
1907
|
stream=self.get_stream(),
|
1884
1908
|
)
|
1885
1909
|
if self.get_stream():
|
@@ -2073,7 +2097,7 @@ class OpenAIGPT(LanguageModel):
|
|
2073
2097
|
function_call,
|
2074
2098
|
response_format,
|
2075
2099
|
)
|
2076
|
-
cached, hashed_key, response = self._chat_completions_with_backoff(**args)
|
2100
|
+
cached, hashed_key, response = self._chat_completions_with_backoff(**args) # type: ignore
|
2077
2101
|
if self.get_stream() and not cached:
|
2078
2102
|
llm_response, openai_response = self._stream_response(response, chat=True)
|
2079
2103
|
self._cache_store(hashed_key, openai_response)
|
@@ -2106,7 +2130,7 @@ class OpenAIGPT(LanguageModel):
|
|
2106
2130
|
function_call,
|
2107
2131
|
response_format,
|
2108
2132
|
)
|
2109
|
-
cached, hashed_key, response = await self._achat_completions_with_backoff(
|
2133
|
+
cached, hashed_key, response = await self._achat_completions_with_backoff( # type: ignore
|
2110
2134
|
**args
|
2111
2135
|
)
|
2112
2136
|
if self.get_stream() and not cached:
|
@@ -31,7 +31,7 @@ if TYPE_CHECKING:
|
|
31
31
|
from PIL import Image
|
32
32
|
|
33
33
|
from langroid.mytypes import DocMetaData, Document
|
34
|
-
from langroid.parsing.parser import Parser, ParsingConfig
|
34
|
+
from langroid.parsing.parser import LLMPdfParserConfig, Parser, ParsingConfig
|
35
35
|
|
36
36
|
logger = logging.getLogger(__name__)
|
37
37
|
|
@@ -1040,7 +1040,8 @@ class LLMPdfParser(DocumentParser):
|
|
1040
1040
|
raise ValueError(
|
1041
1041
|
"LLMPdfParser requires a llm-based config in pdf parsing config"
|
1042
1042
|
)
|
1043
|
-
self.
|
1043
|
+
self.llm_parser_config: LLMPdfParserConfig = config.pdf.llm_parser_config
|
1044
|
+
self.model_name = self.llm_parser_config.model_name
|
1044
1045
|
|
1045
1046
|
# Ensure output directory exists
|
1046
1047
|
self.OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
|
@@ -1059,9 +1060,7 @@ class LLMPdfParser(DocumentParser):
|
|
1059
1060
|
temp_file.close()
|
1060
1061
|
self.output_filename = Path(temp_file.name)
|
1061
1062
|
|
1062
|
-
self.max_tokens =
|
1063
|
-
config.pdf.llm_parser_config.max_tokens or self.DEFAULT_MAX_TOKENS
|
1064
|
-
)
|
1063
|
+
self.max_tokens = self.llm_parser_config.max_tokens or self.DEFAULT_MAX_TOKENS
|
1065
1064
|
|
1066
1065
|
"""
|
1067
1066
|
If True, each PDF page is processed as a separate chunk,
|
@@ -1069,12 +1068,12 @@ class LLMPdfParser(DocumentParser):
|
|
1069
1068
|
grouped into chunks based on `max_token_limit` before being sent
|
1070
1069
|
to the LLM.
|
1071
1070
|
"""
|
1072
|
-
self.split_on_page =
|
1071
|
+
self.split_on_page = self.llm_parser_config.split_on_page or False
|
1073
1072
|
|
1074
1073
|
# Rate limiting parameters
|
1075
1074
|
import asyncio
|
1076
1075
|
|
1077
|
-
self.requests_per_minute =
|
1076
|
+
self.requests_per_minute = self.llm_parser_config.requests_per_minute or 5
|
1078
1077
|
|
1079
1078
|
"""
|
1080
1079
|
A semaphore to control the number of concurrent requests to the LLM,
|
@@ -1231,6 +1230,7 @@ class LLMPdfParser(DocumentParser):
|
|
1231
1230
|
llm_config = OpenAIGPTConfig(
|
1232
1231
|
chat_model=self.model_name,
|
1233
1232
|
max_output_tokens=self.max_tokens,
|
1233
|
+
timeout=self.llm_parser_config.timeout,
|
1234
1234
|
)
|
1235
1235
|
llm = OpenAIGPT(config=llm_config)
|
1236
1236
|
page_nums = self._page_num_str(chunk.get("page_numbers", "?"))
|
@@ -1242,7 +1242,7 @@ class LLMPdfParser(DocumentParser):
|
|
1242
1242
|
image_url=dict(url=data_uri),
|
1243
1243
|
)
|
1244
1244
|
elif "claude" in self.model_name.lower():
|
1245
|
-
#
|
1245
|
+
# optimistically try this: some API proxies like litellm
|
1246
1246
|
# support this, and others may not.
|
1247
1247
|
file_content = dict(
|
1248
1248
|
type="file",
|
@@ -1259,27 +1259,32 @@ class LLMPdfParser(DocumentParser):
|
|
1259
1259
|
file_data=data_uri,
|
1260
1260
|
),
|
1261
1261
|
)
|
1262
|
+
prompt = (
|
1263
|
+
self.llm_parser_config.prompt
|
1264
|
+
or self.LLM_PDF_MD_SYSTEM_INSTRUCTION
|
1265
|
+
)
|
1266
|
+
system_prompt = (
|
1267
|
+
self.llm_parser_config.system_prompt
|
1268
|
+
or """
|
1269
|
+
You are an expert pdf -> markdown converter.
|
1270
|
+
Do NOT use any triple backquotes when you present the
|
1271
|
+
markdown content,like ```markdown etc.
|
1272
|
+
FAITHFULLY CONVERT THE PDF TO MARKDOWN,
|
1273
|
+
retaining ALL content as you find it.
|
1274
|
+
"""
|
1275
|
+
)
|
1262
1276
|
|
1263
1277
|
# Send the request with PDF content and system instructions
|
1264
1278
|
response = await llm.async_client.chat.completions.create( # type: ignore
|
1265
1279
|
model=self.model_name.split("/")[-1],
|
1266
1280
|
messages=[
|
1267
|
-
dict(
|
1268
|
-
role="system",
|
1269
|
-
content="""
|
1270
|
-
You are an expert pdf -> markdown converter.
|
1271
|
-
Do NOT use any triple backquotes when you present the
|
1272
|
-
markdown content,like ```markdown etc.
|
1273
|
-
FAITHFULLY CONVERT THE PDF TO MARKDOWN,
|
1274
|
-
retaining ALL content as you find it.
|
1275
|
-
""",
|
1276
|
-
),
|
1281
|
+
dict(role="system", content=system_prompt),
|
1277
1282
|
dict( # type: ignore
|
1278
1283
|
role="user",
|
1279
1284
|
content=[
|
1280
1285
|
dict(
|
1281
1286
|
type="text",
|
1282
|
-
text=
|
1287
|
+
text=prompt,
|
1283
1288
|
),
|
1284
1289
|
file_content,
|
1285
1290
|
],
|