11"""Langchain tools for workspace operations."""
22
33from collections .abc import Callable
4- from typing import ClassVar , Literal
4+ from typing import Annotated , ClassVar , Literal , Optional
55
6+ from langchain_core .messages import ToolMessage
7+ from langchain_core .tools import InjectedToolCallId
68from langchain_core .tools .base import BaseTool
79from pydantic import BaseModel , Field
810
@@ -52,10 +54,11 @@ class ViewFileInput(BaseModel):
5254 """Input for viewing a file."""
5355
5456 filepath : str = Field (..., description = "Path to the file relative to workspace root" )
55- start_line : int | None = Field (None , description = "Starting line number to view (1-indexed, inclusive)" )
56- end_line : int | None = Field (None , description = "Ending line number to view (1-indexed, inclusive)" )
57- max_lines : int | None = Field (None , description = "Maximum number of lines to view at once, defaults to 500" )
58- line_numbers : bool | None = Field (True , description = "If True, add line numbers to the content (1-indexed)" )
57+ start_line : Optional [int ] = Field (None , description = "Starting line number to view (1-indexed, inclusive)" )
58+ end_line : Optional [int ] = Field (None , description = "Ending line number to view (1-indexed, inclusive)" )
59+ max_lines : Optional [int ] = Field (None , description = "Maximum number of lines to view at once, defaults to 500" )
60+ line_numbers : Optional [bool ] = Field (True , description = "If True, add line numbers to the content (1-indexed)" )
61+ tool_call_id : Annotated [str , InjectedToolCallId ]
5962
6063
6164class ViewFileTool (BaseTool ):
@@ -73,12 +76,13 @@ def __init__(self, codebase: Codebase) -> None:
7376
7477 def _run (
7578 self ,
79+ tool_call_id : str ,
7680 filepath : str ,
77- start_line : int | None = None ,
78- end_line : int | None = None ,
79- max_lines : int | None = None ,
80- line_numbers : bool | None = True ,
81- ) -> str :
81+ start_line : Optional [ int ] = None ,
82+ end_line : Optional [ int ] = None ,
83+ max_lines : Optional [ int ] = None ,
84+ line_numbers : Optional [ bool ] = True ,
85+ ) -> ToolMessage :
8286 result = view_file (
8387 self .codebase ,
8488 filepath ,
@@ -88,14 +92,15 @@ def _run(
8892 max_lines = max_lines if max_lines is not None else 500 ,
8993 )
9094
91- return result .render ()
95+ return result .render (tool_call_id )
9296
9397
9498class ListDirectoryInput (BaseModel ):
9599 """Input for listing directory contents."""
96100
97101 dirpath : str = Field (default = "./" , description = "Path to directory relative to workspace root" )
98102 depth : int = Field (default = 1 , description = "How deep to traverse. Use -1 for unlimited depth." )
103+ tool_call_id : Annotated [str , InjectedToolCallId ]
99104
100105
101106class ListDirectoryTool (BaseTool ):
@@ -109,9 +114,9 @@ class ListDirectoryTool(BaseTool):
109114 def __init__ (self , codebase : Codebase ) -> None :
110115 super ().__init__ (codebase = codebase )
111116
112- def _run (self , dirpath : str = "./" , depth : int = 1 ) -> str :
117+ def _run (self , tool_call_id : str , dirpath : str = "./" , depth : int = 1 ) -> ToolMessage :
113118 result = list_directory (self .codebase , dirpath , depth )
114- return result .render ()
119+ return result .render (tool_call_id )
115120
116121
117122class SearchInput (BaseModel ):
@@ -126,6 +131,7 @@ class SearchInput(BaseModel):
126131 page : int = Field (default = 1 , description = "Page number to return (1-based, default: 1)" )
127132 files_per_page : int = Field (default = 10 , description = "Number of files to return per page (default: 10)" )
128133 use_regex : bool = Field (default = False , description = "Whether to treat query as a regex pattern (default: False)" )
134+ tool_call_id : Annotated [str , InjectedToolCallId ]
129135
130136
131137class SearchTool (BaseTool ):
@@ -139,16 +145,17 @@ class SearchTool(BaseTool):
139145 def __init__ (self , codebase : Codebase ) -> None :
140146 super ().__init__ (codebase = codebase )
141147
142- def _run (self , query : str , file_extensions : list [str ] | None = None , page : int = 1 , files_per_page : int = 10 , use_regex : bool = False ) -> str :
148+ def _run (self , tool_call_id : str , query : str , file_extensions : Optional [ list [str ]] = None , page : int = 1 , files_per_page : int = 10 , use_regex : bool = False ) -> ToolMessage :
143149 result = search (self .codebase , query , file_extensions = file_extensions , page = page , files_per_page = files_per_page , use_regex = use_regex )
144- return result .render ()
150+ return result .render (tool_call_id )
145151
146152
147153class EditFileInput (BaseModel ):
148154 """Input for editing a file."""
149155
150156 filepath : str = Field (..., description = "Path to the file to edit" )
151157 content : str = Field (..., description = "New content for the file" )
158+ tool_call_id : Annotated [str , InjectedToolCallId ]
152159
153160
154161class EditFileTool (BaseTool ):
@@ -181,9 +188,9 @@ class EditFileTool(BaseTool):
181188 def __init__ (self , codebase : Codebase ) -> None :
182189 super ().__init__ (codebase = codebase )
183190
184- def _run (self , filepath : str , content : str ) -> str :
191+ def _run (self , filepath : str , content : str , tool_call_id : str ) -> str :
185192 result = edit_file (self .codebase , filepath , content )
186- return result .render ()
193+ return result .render (tool_call_id )
187194
188195
189196class CreateFileInput (BaseModel ):
@@ -340,6 +347,7 @@ class SemanticEditInput(BaseModel):
340347 edit_content : str = Field (..., description = FILE_EDIT_PROMPT )
341348 start : int = Field (default = 1 , description = "Starting line number (1-indexed, inclusive). Default is 1." )
342349 end : int = Field (default = - 1 , description = "Ending line number (1-indexed, inclusive). Default is -1 (end of file)." )
350+ tool_call_id : Annotated [str , InjectedToolCallId ]
343351
344352
345353class SemanticEditTool (BaseTool ):
@@ -353,10 +361,10 @@ class SemanticEditTool(BaseTool):
353361 def __init__ (self , codebase : Codebase ) -> None :
354362 super ().__init__ (codebase = codebase )
355363
356- def _run (self , filepath : str , edit_content : str , start : int = 1 , end : int = - 1 ) -> str :
364+ def _run (self , filepath : str , tool_call_id : str , edit_content : str , start : int = 1 , end : int = - 1 ) -> ToolMessage :
357365 # Create the the draft editor mini llm
358366 result = semantic_edit (self .codebase , filepath , edit_content , start = start , end = end )
359- return result .render ()
367+ return result .render (tool_call_id )
360368
361369
362370class RenameFileInput (BaseModel ):
@@ -1033,6 +1041,7 @@ class RelaceEditInput(BaseModel):
10331041
10341042 filepath : str = Field (..., description = "Path of the file relative to workspace root" )
10351043 edit_snippet : str = Field (..., description = RELACE_EDIT_PROMPT )
1044+ tool_call_id : Annotated [str , InjectedToolCallId ]
10361045
10371046
10381047class RelaceEditTool (BaseTool ):
@@ -1046,9 +1055,9 @@ class RelaceEditTool(BaseTool):
10461055 def __init__ (self , codebase : Codebase ) -> None :
10471056 super ().__init__ (codebase = codebase )
10481057
1049- def _run (self , filepath : str , edit_snippet : str ) -> str :
1058+ def _run (self , filepath : str , edit_snippet : str , tool_call_id : str ) -> ToolMessage :
10501059 result = relace_edit (self .codebase , filepath , edit_snippet )
1051- return result .render ()
1060+ return result .render (tool_call_id = tool_call_id )
10521061
10531062
10541063class ReflectionInput (BaseModel ):
0 commit comments