@@ -107,6 +107,7 @@ def __init__(self, root: Path, *, dry_run: bool = False, stdout: TextIO | None =
107107
108108 def emit (self , record : ToolTypeRecord ) -> list [Path ]:
109109 self ._guard_not_present (record )
110+ self ._written = []
110111 plan = _EmitPlan ()
111112 self ._plan_tool_results (record , plan )
112113 self ._plan_tool_dispatch (record , plan )
@@ -124,7 +125,8 @@ def emit(self, record: ToolTypeRecord) -> list[Path]:
124125
125126 def _guard_not_present (self , record : ToolTypeRecord ) -> None :
126127 dispatch_text = self .paths .tool_dispatch .read_text (encoding = "utf-8" )
127- if f'"{ record .name } "' in dispatch_text and f'"{ record .name } ":' in dispatch_text :
128+ known = _parse_handlers_from_text (dispatch_text )
129+ if record .name in known :
128130 msg = f"tool type { record .name !r} already present in _FILE_ACTIVITY_HANDLERS"
129131 raise ValueError (msg )
130132
@@ -148,7 +150,7 @@ def transform(text: str) -> str:
148150 text = self ._insert_before (
149151 text ,
150152 "# Registration order is tie-break only when priorities are equal." ,
151- self ._render_dispatch_builder (record ) + "\n " ,
153+ self ._render_dispatch_builder (record ) + "\n \n \n " ,
152154 )
153155 text = self ._insert_before (
154156 text ,
@@ -158,10 +160,10 @@ def transform(text: str) -> str:
158160 )
159161 guard = record .guard_name
160162 if f" { guard } ," not in text :
161- text = self ._replace_once (
163+ text = self ._append_to_block (
162164 text ,
163- " is_web_search_tool_result, \n ) " ,
164- f" is_web_search_tool_result, \n { guard } ,\n ) " ,
165+ "from models.tool_results import ( " ,
166+ f" { guard } ," ,
165167 )
166168 entry = f' "{ record .name } ": { handler } ,\n '
167169 text = self ._insert_before (text , "}\n KNOWN_TOOL_TYPES" , entry , marker_in_prev_line = True )
@@ -173,7 +175,6 @@ def _render_dispatch_builder(self, record: ToolTypeRecord) -> str:
173175 assert record .result is not None
174176 sig = "tr: ToolResultDict, base: dict[str, object]"
175177 lines = [
176- "" ,
177178 f"def { record .builder_name } ({ sig } ) -> dict[str, object]:" ,
178179 " # TODO: map toolUseResult fields to parsed result keys." ,
179180 " result = dict(base)" ,
@@ -208,25 +209,25 @@ def transform(text: str) -> str:
208209 text = self ._insert_before (
209210 text ,
210211 "# Dict passed into dispatch predicates" ,
211- self ._render_typed_dict (record ) + "\n \n " ,
212+ self ._render_typed_dict (record ) + "\n \n \n " ,
212213 )
213214 member = f" | { record .typed_dict_class } \n "
214215 text = self ._insert_before (
215216 text ,
216217 " | ToolResultWithContentDict" ,
217218 member ,
218219 )
219- anchor = ' return "questions" in tr and "answers" in tr\n \n \n '
220- text = self ._replace_once (
220+ text = self ._insert_before (
221221 text ,
222- anchor + "# Tool names on assistant" ,
223- anchor + f"{ self ._render_guard (record )} \n \n \n # Tool names on assistant" ,
222+ "# Tool names on assistant" ,
223+ self ._render_guard (record ) + "\n \n \n " ,
224+ )
225+ if f' "{ record .name } ",' not in text :
226+ text = self ._append_to_block (
227+ text ,
228+ "ToolNameLiteral = Literal[" ,
229+ f' "{ record .name } ",' ,
224230 )
225- text = self ._replace_once (
226- text ,
227- ' "WebSearch",\n ]' ,
228- f' "WebSearch",\n "{ record .name } ",\n ]' ,
229- )
230231 return text
231232
232233 plan .stage_patch (self .paths .tool_results , transform )
@@ -375,25 +376,18 @@ def transform(text: str) -> str:
375376 for inv in record .result .overlap_invariants :
376377 before_guard = inv .resolved_before_guard ()
377378 after_guard = inv .resolved_after_guard ()
378- if before_guard not in text :
379- text = self ._replace_once (
379+ if f" { before_guard } ," not in text :
380+ text = self ._append_to_block (
381+ text ,
382+ "from models.tool_results import (" ,
383+ f" { before_guard } ," ,
384+ )
385+ if f" { after_guard } ," not in text :
386+ text = self ._append_to_block (
380387 text ,
381- " is_task_async_tool_result, \n ) " ,
382- f" is_task_async_tool_result, \n { before_guard } , \n ) " ,
388+ "from models.tool_results import ( " ,
389+ f" { after_guard } , " ,
383390 )
384- if after_guard not in text :
385- if before_guard in text :
386- text = self ._replace_once (
387- text ,
388- f" { before_guard } ,\n )" ,
389- f" { before_guard } ,\n { after_guard } ,\n )" ,
390- )
391- else :
392- text = self ._replace_once (
393- text ,
394- " is_task_async_tool_result,\n )" ,
395- f" is_task_async_tool_result,\n { after_guard } ,\n )" ,
396- )
397391 row = (
398392 f" (\n "
399393 f" { before_guard } ,\n "
@@ -477,6 +471,19 @@ def _replace_once(text: str, anchor: str, replacement: str) -> str:
477471 raise ValueError (msg )
478472 return text .replace (anchor , replacement , 1 )
479473
474+ @staticmethod
475+ def _append_to_block (text : str , block_start : str , entry : str ) -> str :
476+ """Append *entry* before the closing ``)`` or ``]`` of *block_start*."""
477+ idx = text .find (block_start )
478+ if idx == - 1 :
479+ raise ValueError (f"scaffold block not found: { block_start !r} " )
480+ search_from = idx + len (block_start )
481+ match = re .search (r"\n[)\]]" , text [search_from :])
482+ if not match :
483+ raise ValueError (f"closing delimiter not found for block: { block_start !r} " )
484+ close_pos = search_from + match .start ()
485+ return text [:close_pos ] + "\n " + entry + text [close_pos :]
486+
480487
481488def _parse_handlers_from_text (text : str ) -> frozenset [str ]:
482489 marker = "_FILE_ACTIVITY_HANDLERS: dict"
0 commit comments