langroid 0.33.6__py3-none-any.whl → 0.33.8__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 (129) hide show
  1. langroid/__init__.py +106 -0
  2. langroid/agent/__init__.py +41 -0
  3. langroid/agent/base.py +1983 -0
  4. langroid/agent/batch.py +398 -0
  5. langroid/agent/callbacks/__init__.py +0 -0
  6. langroid/agent/callbacks/chainlit.py +598 -0
  7. langroid/agent/chat_agent.py +1899 -0
  8. langroid/agent/chat_document.py +454 -0
  9. langroid/agent/openai_assistant.py +882 -0
  10. langroid/agent/special/__init__.py +59 -0
  11. langroid/agent/special/arangodb/__init__.py +0 -0
  12. langroid/agent/special/arangodb/arangodb_agent.py +656 -0
  13. langroid/agent/special/arangodb/system_messages.py +186 -0
  14. langroid/agent/special/arangodb/tools.py +107 -0
  15. langroid/agent/special/arangodb/utils.py +36 -0
  16. langroid/agent/special/doc_chat_agent.py +1466 -0
  17. langroid/agent/special/lance_doc_chat_agent.py +262 -0
  18. langroid/agent/special/lance_rag/__init__.py +9 -0
  19. langroid/agent/special/lance_rag/critic_agent.py +198 -0
  20. langroid/agent/special/lance_rag/lance_rag_task.py +82 -0
  21. langroid/agent/special/lance_rag/query_planner_agent.py +260 -0
  22. langroid/agent/special/lance_tools.py +61 -0
  23. langroid/agent/special/neo4j/__init__.py +0 -0
  24. langroid/agent/special/neo4j/csv_kg_chat.py +174 -0
  25. langroid/agent/special/neo4j/neo4j_chat_agent.py +433 -0
  26. langroid/agent/special/neo4j/system_messages.py +120 -0
  27. langroid/agent/special/neo4j/tools.py +32 -0
  28. langroid/agent/special/relevance_extractor_agent.py +127 -0
  29. langroid/agent/special/retriever_agent.py +56 -0
  30. langroid/agent/special/sql/__init__.py +17 -0
  31. langroid/agent/special/sql/sql_chat_agent.py +654 -0
  32. langroid/agent/special/sql/utils/__init__.py +21 -0
  33. langroid/agent/special/sql/utils/description_extractors.py +190 -0
  34. langroid/agent/special/sql/utils/populate_metadata.py +85 -0
  35. langroid/agent/special/sql/utils/system_message.py +35 -0
  36. langroid/agent/special/sql/utils/tools.py +64 -0
  37. langroid/agent/special/table_chat_agent.py +263 -0
  38. langroid/agent/task.py +2099 -0
  39. langroid/agent/tool_message.py +393 -0
  40. langroid/agent/tools/__init__.py +38 -0
  41. langroid/agent/tools/duckduckgo_search_tool.py +50 -0
  42. langroid/agent/tools/file_tools.py +234 -0
  43. langroid/agent/tools/google_search_tool.py +39 -0
  44. langroid/agent/tools/metaphor_search_tool.py +68 -0
  45. langroid/agent/tools/orchestration.py +303 -0
  46. langroid/agent/tools/recipient_tool.py +235 -0
  47. langroid/agent/tools/retrieval_tool.py +32 -0
  48. langroid/agent/tools/rewind_tool.py +137 -0
  49. langroid/agent/tools/segment_extract_tool.py +41 -0
  50. langroid/agent/xml_tool_message.py +382 -0
  51. langroid/cachedb/__init__.py +17 -0
  52. langroid/cachedb/base.py +58 -0
  53. langroid/cachedb/momento_cachedb.py +108 -0
  54. langroid/cachedb/redis_cachedb.py +153 -0
  55. langroid/embedding_models/__init__.py +39 -0
  56. langroid/embedding_models/base.py +74 -0
  57. langroid/embedding_models/models.py +461 -0
  58. langroid/embedding_models/protoc/__init__.py +0 -0
  59. langroid/embedding_models/protoc/embeddings.proto +19 -0
  60. langroid/embedding_models/protoc/embeddings_pb2.py +33 -0
  61. langroid/embedding_models/protoc/embeddings_pb2.pyi +50 -0
  62. langroid/embedding_models/protoc/embeddings_pb2_grpc.py +79 -0
  63. langroid/embedding_models/remote_embeds.py +153 -0
  64. langroid/exceptions.py +71 -0
  65. langroid/language_models/__init__.py +53 -0
  66. langroid/language_models/azure_openai.py +153 -0
  67. langroid/language_models/base.py +678 -0
  68. langroid/language_models/config.py +18 -0
  69. langroid/language_models/mock_lm.py +124 -0
  70. langroid/language_models/openai_gpt.py +1964 -0
  71. langroid/language_models/prompt_formatter/__init__.py +16 -0
  72. langroid/language_models/prompt_formatter/base.py +40 -0
  73. langroid/language_models/prompt_formatter/hf_formatter.py +132 -0
  74. langroid/language_models/prompt_formatter/llama2_formatter.py +75 -0
  75. langroid/language_models/utils.py +151 -0
  76. langroid/mytypes.py +84 -0
  77. langroid/parsing/__init__.py +52 -0
  78. langroid/parsing/agent_chats.py +38 -0
  79. langroid/parsing/code_parser.py +121 -0
  80. langroid/parsing/document_parser.py +718 -0
  81. langroid/parsing/para_sentence_split.py +62 -0
  82. langroid/parsing/parse_json.py +155 -0
  83. langroid/parsing/parser.py +313 -0
  84. langroid/parsing/repo_loader.py +790 -0
  85. langroid/parsing/routing.py +36 -0
  86. langroid/parsing/search.py +275 -0
  87. langroid/parsing/spider.py +102 -0
  88. langroid/parsing/table_loader.py +94 -0
  89. langroid/parsing/url_loader.py +115 -0
  90. langroid/parsing/urls.py +273 -0
  91. langroid/parsing/utils.py +373 -0
  92. langroid/parsing/web_search.py +156 -0
  93. langroid/prompts/__init__.py +9 -0
  94. langroid/prompts/dialog.py +17 -0
  95. langroid/prompts/prompts_config.py +5 -0
  96. langroid/prompts/templates.py +141 -0
  97. langroid/pydantic_v1/__init__.py +10 -0
  98. langroid/pydantic_v1/main.py +4 -0
  99. langroid/utils/__init__.py +19 -0
  100. langroid/utils/algorithms/__init__.py +3 -0
  101. langroid/utils/algorithms/graph.py +103 -0
  102. langroid/utils/configuration.py +98 -0
  103. langroid/utils/constants.py +30 -0
  104. langroid/utils/git_utils.py +252 -0
  105. langroid/utils/globals.py +49 -0
  106. langroid/utils/logging.py +135 -0
  107. langroid/utils/object_registry.py +66 -0
  108. langroid/utils/output/__init__.py +20 -0
  109. langroid/utils/output/citations.py +41 -0
  110. langroid/utils/output/printing.py +99 -0
  111. langroid/utils/output/status.py +40 -0
  112. langroid/utils/pandas_utils.py +30 -0
  113. langroid/utils/pydantic_utils.py +602 -0
  114. langroid/utils/system.py +286 -0
  115. langroid/utils/types.py +93 -0
  116. langroid/vector_store/__init__.py +50 -0
  117. langroid/vector_store/base.py +359 -0
  118. langroid/vector_store/chromadb.py +214 -0
  119. langroid/vector_store/lancedb.py +406 -0
  120. langroid/vector_store/meilisearch.py +299 -0
  121. langroid/vector_store/momento.py +278 -0
  122. langroid/vector_store/qdrantdb.py +468 -0
  123. {langroid-0.33.6.dist-info → langroid-0.33.8.dist-info}/METADATA +95 -94
  124. langroid-0.33.8.dist-info/RECORD +127 -0
  125. {langroid-0.33.6.dist-info → langroid-0.33.8.dist-info}/WHEEL +1 -1
  126. langroid-0.33.6.dist-info/RECORD +0 -7
  127. langroid-0.33.6.dist-info/entry_points.txt +0 -4
  128. pyproject.toml +0 -356
  129. {langroid-0.33.6.dist-info → langroid-0.33.8.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,235 @@
1
+ """
2
+ The `recipient_tool` is used to send a message to a specific recipient.
3
+ Various methods from the RecipientTool and AddRecipientTool class
4
+ are inserted into the Agent as methods (see `langroid/agent/base.py`,
5
+ the method `_get_tool_list()`).
6
+
7
+ See usage examples in `tests/main/test_multi_agent_complex.py` and
8
+ `tests/main/test_recipient_tool.py`.
9
+
10
+ A simpler alternative to this tool is `SendTool`, see here:
11
+ https://github.com/langroid/langroid/blob/main/langroid/agent/tools/orchestration.py
12
+
13
+ You can also define your own XML-based variant of this tool:
14
+ https://github.com/langroid/langroid/blob/main/examples/basic/xml-tool.py
15
+ which uses XML rather than JSON, and can be more reliable than JSON,
16
+ especially with weaker LLMs.
17
+
18
+ """
19
+
20
+ from typing import List, Type
21
+
22
+ from rich import print
23
+
24
+ from langroid.agent.chat_agent import ChatAgent
25
+ from langroid.agent.chat_document import ChatDocMetaData, ChatDocument
26
+ from langroid.agent.tool_message import ToolMessage
27
+ from langroid.mytypes import Entity
28
+ from langroid.utils.pydantic_utils import has_field
29
+
30
+
31
+ class AddRecipientTool(ToolMessage):
32
+ """
33
+ Used by LLM to add a recipient to the previous message, when it has
34
+ forgotten to specify a recipient. This avoids having to re-generate the
35
+ previous message (and thus saves token-cost and time).
36
+ """
37
+
38
+ request: str = "add_recipient"
39
+ purpose: str = (
40
+ "To clarify that the <intended_recipient> when I forgot to specify it, "
41
+ "to clarify who the message is intended for."
42
+ )
43
+ intended_recipient: str
44
+ _saved_content: str = ""
45
+
46
+ def response(self, agent: ChatAgent) -> ChatDocument:
47
+ """
48
+ Returns:
49
+ (ChatDocument): with content set to self.content and
50
+ metadata.recipient set to self.recipient.
51
+ """
52
+ print(
53
+ "[red]RecipientTool: "
54
+ f"Added recipient {self.intended_recipient} to message."
55
+ )
56
+ if self.__class__._saved_content == "":
57
+ recipient_request_name = RecipientTool.default_value("request")
58
+ content = f"""
59
+ Recipient specified but content is empty!
60
+ This could be because the `{self.request}` tool/function was used
61
+ before using `{recipient_request_name}` tool/function.
62
+ Resend the message using `{recipient_request_name}` tool/function.
63
+ """
64
+ else:
65
+ content = self.__class__._saved_content # use class-level attrib value
66
+ # erase content since we just used it.
67
+ self.__class__._saved_content = ""
68
+ return ChatDocument(
69
+ content=content,
70
+ metadata=ChatDocMetaData(
71
+ recipient=self.intended_recipient,
72
+ # we are constructing this so it looks as it msg is from LLM
73
+ sender=Entity.LLM,
74
+ ),
75
+ )
76
+
77
+
78
+ class RecipientTool(ToolMessage):
79
+ """
80
+ Used by LLM to send a message to a specific recipient.
81
+
82
+ Useful in cases where an LLM is talking to 2 or more
83
+ agents (or an Agent and human user), and needs to specify which agent (task)
84
+ its message is intended for. The recipient name should be the name of a task
85
+ (which is normally the name of the agent that the task wraps, although the task
86
+ can have its own name).
87
+
88
+ To use this tool/function-call, LLM must generate a JSON structure
89
+ with these fields:
90
+ {
91
+ "request": "recipient_message", # also the function name when using fn-calling
92
+ "intended_recipient": <name_of_recipient_task_or_entity>,
93
+ "content": <content>
94
+ }
95
+ The effect of this is that `content` will be sent to the `intended_recipient` task.
96
+ """
97
+
98
+ request: str = "recipient_message"
99
+ purpose: str = "To send message <content> to a specific <intended_recipient>."
100
+ intended_recipient: str
101
+ content: str
102
+
103
+ @classmethod
104
+ def create(cls, recipients: List[str], default: str = "") -> Type["RecipientTool"]:
105
+ """Create a restricted version of RecipientTool that
106
+ only allows certain recipients, and possibly sets a default recipient."""
107
+
108
+ class RecipientToolRestricted(cls): # type: ignore
109
+ allowed_recipients = recipients
110
+ default_recipient = default
111
+
112
+ return RecipientToolRestricted
113
+
114
+ @classmethod
115
+ def instructions(cls) -> str:
116
+ """
117
+ Generate instructions for using this tool/function.
118
+ These are intended to be appended to the system message of the LLM.
119
+ """
120
+ recipients = []
121
+ if has_field(cls, "allowed_recipients"):
122
+ recipients = cls.default_value("allowed_recipients")
123
+ if len(recipients) > 0:
124
+ recipients_str = ", ".join(recipients)
125
+ return f"""
126
+ Since you will be talking to multiple recipients,
127
+ you must clarify who your intended recipient is, using
128
+ the `{cls.default_value("request")}` tool/function-call, by setting the
129
+ 'intended_recipient' field to one of the following:
130
+ {recipients_str},
131
+ and setting the 'content' field to your message.
132
+ """
133
+ else:
134
+ return f"""
135
+ Since you will be talking to multiple recipients,
136
+ you must clarify who your intended recipient is, using
137
+ the `{cls.default_value("request")}` tool/function-call, by setting the
138
+ 'intended_recipient' field to the name of the recipient,
139
+ and setting the 'content' field to your message.
140
+ """
141
+
142
+ def response(self, agent: ChatAgent) -> str | ChatDocument:
143
+ """
144
+ When LLM has correctly used this tool,
145
+ construct a ChatDocument with an explicit recipient,
146
+ and make it look like it is from the LLM.
147
+
148
+ Returns:
149
+ (ChatDocument): with content set to self.content and
150
+ metadata.recipient set to self.intended_recipient.
151
+ """
152
+ default_recipient = self.__class__.default_value("default_recipient")
153
+ if self.intended_recipient == "" and default_recipient not in ["", None]:
154
+ self.intended_recipient = default_recipient
155
+ elif self.intended_recipient == "":
156
+ # save the content as a class-variable, so that
157
+ # we can construct the ChatDocument once the LLM specifies a recipient.
158
+ # This avoids having to re-generate the entire message, saving time + cost.
159
+ AddRecipientTool._saved_content = self.content
160
+ agent.enable_message(AddRecipientTool)
161
+ return ChatDocument(
162
+ content="""
163
+ Empty recipient field!
164
+ Please use the 'add_recipient' tool/function-call to specify who your
165
+ message is intended for.
166
+ DO NOT REPEAT your original message; ONLY specify the recipient via this
167
+ tool/function-call.
168
+ """,
169
+ metadata=ChatDocMetaData(
170
+ sender=Entity.AGENT,
171
+ recipient=Entity.LLM,
172
+ ),
173
+ )
174
+
175
+ print("[red]RecipientTool: Validated properly addressed message")
176
+
177
+ return ChatDocument(
178
+ content=self.content,
179
+ metadata=ChatDocMetaData(
180
+ recipient=self.intended_recipient,
181
+ # we are constructing this so it looks as if msg is from LLM
182
+ sender=Entity.LLM,
183
+ ),
184
+ )
185
+
186
+ @staticmethod
187
+ def handle_message_fallback(
188
+ agent: ChatAgent, msg: str | ChatDocument
189
+ ) -> str | ChatDocument | None:
190
+ """
191
+ Response of agent if this tool is not used, e.g.
192
+ the LLM simply sends a message without using this tool.
193
+ This method has two purposes:
194
+ (a) Alert the LLM that it has forgotten to specify a recipient, and prod it
195
+ to use the `add_recipient` tool to specify just the recipient
196
+ (and not re-generate the entire message).
197
+ (b) Save the content of the message in the agent's `content` field,
198
+ so the agent can construct a ChatDocument with this content once LLM
199
+ later specifies a recipient using the `add_recipient` tool.
200
+
201
+ This method is used to set the agent's handle_message_fallback() method.
202
+
203
+ Returns:
204
+ (str): reminder to LLM to use the `add_recipient` tool.
205
+ """
206
+ # Note: once the LLM specifies a missing recipient, the task loop
207
+ # mechanism will not allow any of the "native" responders to respond,
208
+ # since the recipient will differ from the task name.
209
+ # So if this method is called, we can be sure that the recipient has not
210
+ # been specified.
211
+ if (
212
+ isinstance(msg, str)
213
+ or msg.metadata.sender != Entity.LLM
214
+ or msg.metadata.recipient != "" # there IS an explicit recipient
215
+ ):
216
+ return None
217
+ content = msg if isinstance(msg, str) else msg.content
218
+ # save the content as a class-variable, so that
219
+ # we can construct the ChatDocument once the LLM specifies a recipient.
220
+ # This avoids having to re-generate the entire message, saving time + cost.
221
+ AddRecipientTool._saved_content = content
222
+ agent.enable_message(AddRecipientTool)
223
+ print("[red]RecipientTool: Recipient not specified, asking LLM to clarify.")
224
+ return ChatDocument(
225
+ content="""
226
+ Please use the 'add_recipient' tool/function-call to specify who your
227
+ `intended_recipient` is.
228
+ DO NOT REPEAT your original message; ONLY specify the
229
+ `intended_recipient` via this tool/function-call.
230
+ """,
231
+ metadata=ChatDocMetaData(
232
+ sender=Entity.AGENT,
233
+ recipient=Entity.LLM,
234
+ ),
235
+ )
@@ -0,0 +1,32 @@
1
+ from typing import List, Tuple
2
+
3
+ from langroid.agent.tool_message import ToolMessage
4
+
5
+
6
+ class RetrievalTool(ToolMessage):
7
+ """
8
+ Retrieval tool, only to be used by a DocChatAgent.
9
+ The handler method is defined in DocChatAgent.retrieval_tool
10
+ """
11
+
12
+ request: str = "retrieval_tool"
13
+ purpose: str = """
14
+ To retrieve up to <num_results> passages from a document-set, that are
15
+ relevant to a <query>, which could be a question or simply a topic or
16
+ search phrase.
17
+ """
18
+ query: str
19
+ num_results: int
20
+
21
+ @classmethod
22
+ def examples(cls) -> List["ToolMessage" | Tuple[str, "ToolMessage"]]:
23
+ return [
24
+ cls(
25
+ query="What are the eligibility criteria for the scholarship?",
26
+ num_results=3,
27
+ ),
28
+ cls(
29
+ query="Self-Attention mechanism in RNNs",
30
+ num_results=5,
31
+ ),
32
+ ]
@@ -0,0 +1,137 @@
1
+ """
2
+ The `rewind_tool` is used to rewind to the `n`th previous Assistant message
3
+ and replace it with a new `content`. This is useful in several scenarios and
4
+ - saves token-cost + inference time,
5
+ - reduces distracting clutter in chat history, which helps improve response quality.
6
+
7
+ This is intended to mimic how a human user might use a chat interface, where they
8
+ go down a conversation path, and want to go back in history to "edit and re-submit"
9
+ a previous message, to get a better response.
10
+
11
+ See usage examples in `tests/main/test_rewind_tool.py`.
12
+ """
13
+
14
+ from typing import List, Tuple
15
+
16
+ import langroid.language_models as lm
17
+ from langroid.agent.chat_agent import ChatAgent
18
+ from langroid.agent.chat_document import ChatDocument
19
+ from langroid.agent.tool_message import ToolMessage
20
+
21
+
22
+ def prune_messages(agent: ChatAgent, idx: int) -> ChatDocument | None:
23
+ """
24
+ Clear the message history of agent, starting at index `idx`,
25
+ taking care to first clear all dependent messages (possibly from other agents'
26
+ message histories) that are linked to the message at `idx`, via the `child_id` field
27
+ of the `metadata` field of the ChatDocument linked from the message at `idx`.
28
+
29
+ Args:
30
+ agent (ChatAgent): The agent whose message history is to be pruned.
31
+ idx (int): The index from which to start clearing the message history.
32
+
33
+ Returns:
34
+ The parent ChatDocument of the ChatDocument linked from the message at `idx`,
35
+ if it exists, else None.
36
+
37
+ """
38
+ assert idx >= 0, "Invalid index for message history!"
39
+ chat_doc_id = agent.message_history[idx].chat_document_id
40
+ chat_doc = ChatDocument.from_id(chat_doc_id)
41
+ assert chat_doc is not None, "ChatDocument not found in registry!"
42
+
43
+ parent = ChatDocument.from_id(chat_doc.metadata.parent_id) # may be None
44
+ # We're invaliding the msg at idx,
45
+ # so starting with chat_doc, go down the child links
46
+ # and clear history of each agent, to the msg_idx
47
+ curr_doc = chat_doc
48
+ while child_doc := curr_doc.metadata.child:
49
+ if child_doc.metadata.msg_idx >= 0:
50
+ child_agent = ChatAgent.from_id(child_doc.metadata.agent_id)
51
+ if child_agent is not None:
52
+ child_agent.clear_history(child_doc.metadata.msg_idx)
53
+ curr_doc = child_doc
54
+
55
+ # Clear out ObjectRegistry entries for this ChatDocuments
56
+ # and all descendants (in case they weren't already cleared above)
57
+ ChatDocument.delete_id(chat_doc.id())
58
+
59
+ # Finally, clear this agent's history back to idx,
60
+ # and replace the msg at idx with the new content
61
+ agent.clear_history(idx)
62
+ return parent
63
+
64
+
65
+ class RewindTool(ToolMessage):
66
+ """
67
+ Used by LLM to rewind (i.e. backtrack) to the `n`th Assistant message
68
+ and replace with a new msg.
69
+ """
70
+
71
+ request: str = "rewind_tool"
72
+ purpose: str = """
73
+ To rewind the conversation and replace the
74
+ <n>'th Assistant message with <content>
75
+ """
76
+ n: int
77
+ content: str
78
+
79
+ @classmethod
80
+ def examples(cls) -> List["ToolMessage" | Tuple[str, "ToolMessage"]]:
81
+ return [
82
+ cls(n=1, content="What are the 3 major causes of heart disease?"),
83
+ (
84
+ """
85
+ Based on the conversation so far, I realize I would get a better
86
+ response from Bob if rephrase my 2nd message to him to:
87
+ 'Who wrote the book Grime and Banishment?'
88
+ """,
89
+ cls(n=2, content="who wrote the book 'Grime and Banishment'?"),
90
+ ),
91
+ ]
92
+
93
+ def response(self, agent: ChatAgent) -> str | ChatDocument:
94
+ """
95
+ Define the tool-handler method for this tool here itself,
96
+ since it is a generic tool whose functionality should be the
97
+ same for any agent.
98
+
99
+ When LLM has correctly used this tool, rewind this agent's
100
+ `message_history` to the `n`th assistant msg, and replace it with `content`.
101
+ We need to mock it as if the LLM is sending this message.
102
+
103
+ Within a multi-agent scenario, this also means that any other messages dependent
104
+ on this message will need to be invalidated --
105
+ so go down the chain of child messages and clear each agent's history
106
+ back to the `msg_idx` corresponding to the child message.
107
+
108
+ Returns:
109
+ (ChatDocument): with content set to self.content.
110
+ """
111
+ idx = agent.nth_message_idx_with_role(lm.Role.ASSISTANT, self.n)
112
+ if idx < 0:
113
+ # set up a corrective message from AGENT
114
+ msg = f"""
115
+ Could not rewind to {self.n}th Assistant message!
116
+ Please check the value of `n` and try again.
117
+ Or it may be too early to use the `rewind_tool`.
118
+ """
119
+ return agent.create_agent_response(msg)
120
+
121
+ parent = prune_messages(agent, idx)
122
+
123
+ # create ChatDocument with new content, to be returned as result of this tool
124
+ result_doc = agent.create_llm_response(self.content)
125
+ result_doc.metadata.parent_id = "" if parent is None else parent.id()
126
+ result_doc.metadata.agent_id = agent.id
127
+ result_doc.metadata.msg_idx = idx
128
+
129
+ # replace the message at idx with this new message
130
+ agent.message_history.extend(ChatDocument.to_LLMMessage(result_doc))
131
+
132
+ # set the replaced doc's parent's child to this result_doc
133
+ if parent is not None:
134
+ # first remove the this parent's child from registry
135
+ ChatDocument.delete_id(parent.metadata.child_id)
136
+ parent.metadata.child_id = result_doc.id()
137
+ return result_doc
@@ -0,0 +1,41 @@
1
+ """
2
+ A tool to extract segment numbers from the last user message,
3
+ containing numbered segments.
4
+
5
+ The idea is that when an LLM wants to (or is asked to) simply extract
6
+ portions of a message verbatim, it should use this tool/function to
7
+ SPECIFY what should be extracted, rather than actually extracting it.
8
+ The output will be in the form of a list of segment numbers or ranges.
9
+ This will usually be much cheaper and faster than actually writing out the extracted
10
+ text. The handler of this tool/function will then extract the text and send it back.
11
+ """
12
+
13
+ from typing import List, Tuple
14
+
15
+ from langroid.agent.tool_message import ToolMessage
16
+
17
+
18
+ class SegmentExtractTool(ToolMessage):
19
+ request: str = "extract_segments"
20
+ purpose: str = """
21
+ To extract segments from a body of text containing numbered
22
+ segments, in the form of a <segment_list> which is a list of segment
23
+ numbers or ranges, like "10,12,14-17".
24
+ """
25
+ segment_list: str
26
+
27
+ @classmethod
28
+ def examples(cls) -> List["ToolMessage" | Tuple[str, "ToolMessage"]]:
29
+ return [
30
+ (
31
+ "I want to extract segments 1, 3, and 5 thru 7",
32
+ cls(segment_list="1,3,5-7"),
33
+ )
34
+ ]
35
+
36
+ @classmethod
37
+ def instructions(cls) -> str:
38
+ return """
39
+ Use this tool/function to indicate certain segments from
40
+ a body of text containing numbered segments.
41
+ """