langroid 0.53.0__py3-none-any.whl → 0.53.2__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.
@@ -1,10 +1,22 @@
1
1
  from .decorators import mcp_tool
2
- from .fastmcp_client import FastMCPClient, make_mcp_tool_sync, make_mcp_tool
2
+ from .fastmcp_client import (
3
+ FastMCPClient,
4
+ get_langroid_tool,
5
+ get_langroid_tool_async,
6
+ get_langroid_tools,
7
+ get_langroid_tools_async,
8
+ get_mcp_tool_async,
9
+ get_mcp_tools_async,
10
+ )
3
11
 
4
12
 
5
13
  __all__ = [
6
14
  "mcp_tool",
7
15
  "FastMCPClient",
8
- "make_mcp_tool_sync",
9
- "make_mcp_tool",
16
+ "get_langroid_tool",
17
+ "get_langroid_tool_async",
18
+ "get_langroid_tools",
19
+ "get_langroid_tools_async",
20
+ "get_mcp_tool_async",
21
+ "get_mcp_tools_async",
10
22
  ]
@@ -1,7 +1,7 @@
1
1
  from typing import Callable, Type
2
2
 
3
3
  from langroid.agent.tool_message import ToolMessage
4
- from langroid.agent.tools.mcp.fastmcp_client import make_mcp_tool_sync
4
+ from langroid.agent.tools.mcp.fastmcp_client import get_langroid_tool
5
5
 
6
6
 
7
7
  def mcp_tool(
@@ -18,7 +18,7 @@ def mcp_tool(
18
18
 
19
19
  def decorator(user_cls: Type[ToolMessage]) -> Type[ToolMessage]:
20
20
  # build the “real” ToolMessage subclass for this server/tool
21
- RealTool: Type[ToolMessage] = make_mcp_tool_sync(server, tool_name)
21
+ RealTool: Type[ToolMessage] = get_langroid_tool(server, tool_name)
22
22
 
23
23
  # copy user‐defined methods / attributes onto RealTool
24
24
  for name, attr in user_cls.__dict__.items():
@@ -113,11 +113,14 @@ class FastMCPClient:
113
113
  # Default fallback
114
114
  return Any, Field(default=default, description=desc)
115
115
 
116
- async def make_tool(self, tool_name: str) -> Type[ToolMessage]:
117
- """Create a Langroid ToolMessage subclass for the given tool name."""
116
+ async def get_langroid_tool(self, tool_name: str) -> Type[ToolMessage]:
117
+ """
118
+ Create a Langroid ToolMessage subclass from the MCP Tool
119
+ with the given `tool_name`.
120
+ """
118
121
  if not self.client:
119
122
  raise RuntimeError("Client not initialized. Use async with FastMCPClient.")
120
- target = await self.find_mcp_tool(tool_name)
123
+ target = await self.get_mcp_tool_async(tool_name)
121
124
  if target is None:
122
125
  raise ValueError(f"No tool named {tool_name}")
123
126
  props = target.inputSchema.get("properties", {})
@@ -131,6 +134,24 @@ class FastMCPClient:
131
134
  camel_case = "".join(part.capitalize() for part in parts)
132
135
  model_name = f"{camel_case}Tool"
133
136
 
137
+ from langroid.agent.tool_message import ToolMessage as _BaseToolMessage
138
+
139
+ # IMPORTANT: Avoid clashes with reserved field names in Langroid ToolMessage!
140
+ # First figure out which field names are reserved
141
+ reserved = set(_BaseToolMessage.__annotations__.keys())
142
+ reserved.update(["recipient", "_handler"])
143
+ renamed: Dict[str, str] = {}
144
+ new_fields: Dict[str, Tuple[type, Any]] = {}
145
+ for fname, (ftype, fld) in fields.items():
146
+ if fname in reserved:
147
+ new_name = fname + "__"
148
+ renamed[fname] = new_name
149
+ new_fields[new_name] = (ftype, fld)
150
+ else:
151
+ new_fields[fname] = (ftype, fld)
152
+ # now replace fields with our renamed‐aware mapping
153
+ fields = new_fields
154
+
134
155
  # create Langroid ToolMessage subclass, with expected fields.
135
156
  tool_model = cast(
136
157
  Type[ToolMessage],
@@ -143,6 +164,7 @@ class FastMCPClient:
143
164
  ),
144
165
  )
145
166
  tool_model._server = self.server # type: ignore[attr-defined]
167
+ tool_model._renamed_fields = renamed # type: ignore[attr-defined]
146
168
 
147
169
  # 2) define an arg-free call_tool_async()
148
170
  async def call_tool_async(self: ToolMessage) -> Any:
@@ -150,6 +172,12 @@ class FastMCPClient:
150
172
 
151
173
  # pack up the payload
152
174
  payload = self.dict(exclude=self.Config.schema_extra["exclude"])
175
+
176
+ # restore any renamed fields
177
+ for orig, new in self.__class__._renamed_fields.items(): # type: ignore
178
+ if new in payload:
179
+ payload[orig] = payload.pop(new)
180
+
153
181
  # open a fresh client, call the tool, then close
154
182
  async with FastMCPClient(self.__class__._server) as client: # type: ignore
155
183
  return await client.call_mcp_tool(self.request, payload)
@@ -167,7 +195,7 @@ class FastMCPClient:
167
195
 
168
196
  return tool_model
169
197
 
170
- async def get_tools(self) -> List[Type[ToolMessage]]:
198
+ async def get_langroid_tools(self) -> List[Type[ToolMessage]]:
171
199
  """
172
200
  Get all available tools as Langroid ToolMessage classes,
173
201
  handling nested schemas, with `handle_async` methods
@@ -177,11 +205,13 @@ class FastMCPClient:
177
205
  resp = await self.client.list_tools()
178
206
  tools: List[Type[ToolMessage]] = []
179
207
  for t in resp:
180
- tools.append(await self.make_tool(t.name))
208
+ tools.append(await self.get_langroid_tool(t.name))
181
209
  return tools
182
210
 
183
- async def find_mcp_tool(self, name: str) -> Optional[Tool]:
184
- """Find the MCP Tool matching `name`, or None if missing.
211
+ async def get_mcp_tool_async(self, name: str) -> Optional[Tool]:
212
+ """Find the "original" MCP Tool (i.e. of type mcp.types.Tool) on the server
213
+ matching `name`, or None if missing. This contains the metadata for the tool:
214
+ name, description, inputSchema, etc.
185
215
 
186
216
  Args:
187
217
  name: Name of the tool to look up.
@@ -240,10 +270,46 @@ class FastMCPClient:
240
270
  return self._convert_tool_result(tool_name, result)
241
271
 
242
272
 
243
- async def make_mcp_tool(server: str, tool_name: str) -> Type[ToolMessage]:
273
+ async def get_langroid_tool_async(
274
+ server: str | ClientTransport,
275
+ tool_name: str,
276
+ ) -> Type[ToolMessage]:
244
277
  async with FastMCPClient(server) as client:
245
- return await client.make_tool(tool_name)
278
+ return await client.get_langroid_tool(tool_name)
246
279
 
247
280
 
248
- def make_mcp_tool_sync(server: str, tool_name: str) -> Type[ToolMessage]:
249
- return asyncio.run(make_mcp_tool(server, tool_name))
281
+ def get_langroid_tool(
282
+ server: str | ClientTransport,
283
+ tool_name: str,
284
+ ) -> Type[ToolMessage]:
285
+ return asyncio.run(get_langroid_tool_async(server, tool_name))
286
+
287
+
288
+ async def get_langroid_tools_async(
289
+ server: str | ClientTransport,
290
+ ) -> List[Type[ToolMessage]]:
291
+ async with FastMCPClient(server) as client:
292
+ return await client.get_langroid_tools()
293
+
294
+
295
+ def get_langroid_tools(
296
+ server: str | ClientTransport,
297
+ ) -> List[Type[ToolMessage]]:
298
+ return asyncio.run(get_langroid_tools_async(server))
299
+
300
+
301
+ async def get_mcp_tool_async(
302
+ server: str | ClientTransport,
303
+ name: str,
304
+ ) -> Optional[Tool]:
305
+ async with FastMCPClient(server) as client:
306
+ return await client.get_mcp_tool_async(name)
307
+
308
+
309
+ async def get_mcp_tools_async(
310
+ server: str | ClientTransport,
311
+ ) -> List[Tool]:
312
+ async with FastMCPClient(server) as client:
313
+ if not client.client:
314
+ raise RuntimeError("Client not initialized. Use async with FastMCPClient.")
315
+ return await client.client.list_tools()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langroid
3
- Version: 0.53.0
3
+ Version: 0.53.2
4
4
  Summary: Harness LLMs with Multi-Agent Programming
5
5
  Author-email: Prasad Chalasani <pchalasani@gmail.com>
6
6
  License: MIT
@@ -338,6 +338,22 @@ teacher_task.run()
338
338
  <details>
339
339
  <summary> <b>Click to expand</b></summary>
340
340
 
341
+ - **Mar-Apr 2025:**
342
+ - [0.53.0](https://github.com/langroid/langroid/releases/tag/0.53.0) MCP Tools Support.
343
+ - [0.52.0](https://github.com/langroid/langroid/releases/tag/0.52.0) Multimodal support, i.e. allow PDF, image
344
+ inputs to LLM.
345
+ - [0.51.0](https://github.com/langroid/langroid/releases/tag/0.51.0) `LLMPdfParser`, generalizing
346
+ `GeminiPdfParser` to parse documents directly with LLM.
347
+ - [0.50.0](https://github.com/langroid/langroid/releases/tag/0.50.0) Structure-aware Markdown chunking with chunks
348
+ enriched by section headers.
349
+ - [0.49.0](https://github.com/langroid/langroid/releases/tag/0.49.0) Enable easy switch to LiteLLM Proxy-server
350
+ - [0.48.0](https://github.com/langroid/langroid/releases/tag/0.48.0) Exa Crawler, Markitdown Parser
351
+ - [0.47.0](https://github.com/langroid/langroid/releases/tag/0.47.0) Support Firecrawl URL scraper/crawler -
352
+ thanks @abab-dev
353
+ - [0.46.0](https://github.com/langroid/langroid/releases/tag/0.46.0) Support LangDB LLM Gateway - thanks @MrunmayS.
354
+ - [0.45.0](https://github.com/langroid/langroid/releases/tag/0.45.0) Markdown parsing with `Marker` - thanks @abab-dev
355
+ - [0.44.0](https://github.com/langroid/langroid/releases/tag/0.44.0) Late imports to reduce startup time. Thanks
356
+ @abab-dev
341
357
  - **Feb 2025:**
342
358
  - [0.43.0](https://github.com/langroid/langroid/releases/tag/0.43.0): `GeminiPdfParser` for parsing PDF using
343
359
  Gemini LLMs - Thanks @abab-dev.
@@ -54,9 +54,9 @@ langroid/agent/tools/retrieval_tool.py,sha256=zcAV20PP_6VzSd-UE-IJcabaBseFL_QNz5
54
54
  langroid/agent/tools/rewind_tool.py,sha256=XAXL3BpNhCmBGYq_qi_sZfHJuIw7NY2jp4wnojJ7WRs,5606
55
55
  langroid/agent/tools/segment_extract_tool.py,sha256=__srZ_VGYLVOdPrITUM8S0HpmX4q7r5FHWMDdHdEv8w,1440
56
56
  langroid/agent/tools/tavily_search_tool.py,sha256=soI-j0HdgVQLf09wRQScaEK4b5RpAX9C4cwOivRFWWI,1903
57
- langroid/agent/tools/mcp/__init__.py,sha256=aMW4cRepRbE44iCGQu_fQF623bN8Q1HHBbRMGaO22kY,210
58
- langroid/agent/tools/mcp/decorators.py,sha256=zyV8iYq3dg3taHEkaC5aNncyb4UdNBp-i1tRhMOFkd4,1147
59
- langroid/agent/tools/mcp/fastmcp_client.py,sha256=goj7GSbez28cFaa-KmQRavw7MY_hHUCYhKP0fUZLDms,9548
57
+ langroid/agent/tools/mcp/__init__.py,sha256=cQb3gYxXk0YZ23QCqbVNMbMeCeWCJj6w3gqGnvyqv7w,459
58
+ langroid/agent/tools/mcp/decorators.py,sha256=mWnlTjyI9PMNi750PWzC_2B6V5K_XdxH0Co9kE2yAj0,1145
59
+ langroid/agent/tools/mcp/fastmcp_client.py,sha256=ffOV0lJOtE3DLfA6y3Fib28jyACySJPgbqjNJpSAEQA,11815
60
60
  langroid/cachedb/__init__.py,sha256=G2KyNnk3Qkhv7OKyxTOnpsxfDycx3NY0O_wXkJlalNY,96
61
61
  langroid/cachedb/base.py,sha256=ztVjB1DtN6pLCujCWnR6xruHxwVj3XkYniRTYAKKqk0,1354
62
62
  langroid/cachedb/redis_cachedb.py,sha256=7kgnbf4b5CKsCrlL97mHWKvdvlLt8zgn7lc528jEpiE,5141
@@ -132,7 +132,7 @@ langroid/vector_store/pineconedb.py,sha256=otxXZNaBKb9f_H75HTaU3lMHiaR2NUp5MqwLZ
132
132
  langroid/vector_store/postgres.py,sha256=wHPtIi2qM4fhO4pMQr95pz1ZCe7dTb2hxl4VYspGZoA,16104
133
133
  langroid/vector_store/qdrantdb.py,sha256=O6dSBoDZ0jzfeVBd7LLvsXu083xs2fxXtPa9gGX3JX4,18443
134
134
  langroid/vector_store/weaviatedb.py,sha256=Yn8pg139gOy3zkaPfoTbMXEEBCiLiYa1MU5d_3UA1K4,11847
135
- langroid-0.53.0.dist-info/METADATA,sha256=JohvBH6oCb3ki2nsiIcPT0IiNmmn0dM2eNjQ9lsLn5E,63549
136
- langroid-0.53.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
137
- langroid-0.53.0.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
138
- langroid-0.53.0.dist-info/RECORD,,
135
+ langroid-0.53.2.dist-info/METADATA,sha256=t3on9Riu1tK0c1icC8e3mhsCFEjTU4a7SkMGI1kV4bY,64823
136
+ langroid-0.53.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
137
+ langroid-0.53.2.dist-info/licenses/LICENSE,sha256=EgVbvA6VSYgUlvC3RvPKehSg7MFaxWDsFuzLOsPPfJg,1065
138
+ langroid-0.53.2.dist-info/RECORD,,