[Pather / AutoTool] rework route kwargs into tool_options, and actualyl pass them through AutoTool

This commit is contained in:
Jan Petykiewicz 2026-07-12 20:32:59 -07:00
commit 67afee7704
11 changed files with 865 additions and 130 deletions

View file

@ -251,7 +251,7 @@ class SolverRequest:
Public Pather calls are converted into this smaller shape before grammar
enumeration. `length`, `jog`, and `out_ptype` become endpoint constraints;
`route_kwargs` are forwarded to Tool primitive-offer generation.
`tool_options` are forwarded to Tool primitive-offer generation.
"""
family: PrimitiveKind
"""High-level route family being solved."""
@ -259,8 +259,8 @@ class SolverRequest:
"""Tool queried for primitive offers."""
in_ptype: str | None
"""Input ptype at the start of the route."""
route_kwargs: Mapping[str, Any]
"""Tool kwargs forwarded to primitive-offer generation."""
tool_options: Mapping[str, Any]
"""Custom Tool kwargs forwarded to primitive-offer generation."""
length: float | None = None
"""Requested local x displacement, when constrained."""
jog: float | None = None
@ -439,9 +439,8 @@ class Solver:
out_ptype: str | None = None,
extra: Mapping[str, Any] | None = None,
) -> tuple[PrimitiveOffer, ...]:
"""Query the active Tool with route kwargs and per-query overrides."""
kwargs = dict(self.request.route_kwargs)
kwargs.pop('out_ptype', None)
"""Query the active Tool with custom planning options and internal overrides."""
kwargs = dict(self.request.tool_options)
if extra:
kwargs.update(extra)
try:
@ -1018,6 +1017,7 @@ class RoutingPlanner:
constrain_jog: bool = False,
max_bends: int | None = None,
strategy: RouteTieBreakStrategy | str | None = None,
tool_options: Mapping[str, Any] | None = None,
**kwargs: Any,
) -> SolverRequest:
"""Build normalized solver input for one route leg."""
@ -1025,7 +1025,7 @@ class RoutingPlanner:
family=family,
tool=context.tool,
in_ptype=context.port.ptype,
route_kwargs=kwargs,
tool_options={} if tool_options is None else dict(tool_options),
length=length,
jog=jog,
ccw=ccw,
@ -1070,6 +1070,7 @@ class RoutingPlanner:
constrain_jog: bool = False,
max_bends: int | None = None,
strategy: RouteTieBreakStrategy | str | None = None,
tool_options: Mapping[str, Any] | None = None,
**kwargs: Any,
) -> RouteLeg:
"""Solve one route leg and attach it to its source Pather context."""
@ -1107,6 +1108,7 @@ class RoutingPlanner:
constrain_jog=constrain_jog,
max_bends=max_bends,
strategy=strategy,
tool_options=tool_options,
**kwargs,
)
try:
@ -1212,6 +1214,7 @@ class RoutingPlanner:
*,
spacing: float | ArrayLike | None = None,
strategy: RouteTieBreakStrategy | str | None = None,
tool_options: Mapping[str, Any] | None = None,
**bounds: Any,
) -> PreparedRouteResult:
"""Plan straight or single-bend traces, including `each` and bundle-bound modes."""
@ -1225,6 +1228,8 @@ class RoutingPlanner:
request_details['spacing'] = spacing
if strategy is not None:
request_details['strategy'] = strategy
if tool_options:
request_details['tool_options'] = dict(tool_options)
operation: RouteOperation = 'trace'
diagnostic_request = request_details
return self._plan_trace_route(
@ -1235,6 +1240,7 @@ class RoutingPlanner:
diagnostic_request,
spacing=spacing,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
)
@ -1249,6 +1255,7 @@ class RoutingPlanner:
*,
spacing: float | ArrayLike | None,
strategy: RouteTieBreakStrategy | str | None,
tool_options: Mapping[str, Any] | None,
**route_bounds: Any,
) -> PreparedRouteResult:
portspec = tuple(context.portspec for context in contexts)
@ -1263,6 +1270,7 @@ class RoutingPlanner:
length=length,
ccw=ccw,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
)
return self.prepared_result_from_legs((leg,))
@ -1279,6 +1287,7 @@ class RoutingPlanner:
length=each,
ccw=ccw,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
),
)
@ -1295,6 +1304,7 @@ class RoutingPlanner:
length=None,
ccw=ccw,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
)
return self.prepared_result_from_legs((leg,))
@ -1321,6 +1331,7 @@ class RoutingPlanner:
length=route_length,
ccw=ccw,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
)
actions.append(self.prepared_route_action_from_leg(leg))
@ -1333,6 +1344,7 @@ class RoutingPlanner:
*,
spacing: float | ArrayLike | None = None,
strategy: RouteTieBreakStrategy | str | None = None,
tool_options: Mapping[str, Any] | None = None,
**bounds: Any,
) -> PreparedRouteResult:
"""Plan `trace_to()` by resolving positional targets or delegating to `trace()` modes."""
@ -1344,6 +1356,8 @@ class RoutingPlanner:
request_details['spacing'] = spacing
if strategy is not None:
request_details['strategy'] = strategy
if tool_options:
request_details['tool_options'] = dict(tool_options)
operation: RouteOperation = 'trace_to'
diagnostic_request = request_details
return self._plan_trace_to_route(
@ -1353,6 +1367,7 @@ class RoutingPlanner:
diagnostic_request,
spacing=spacing,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
)
@ -1366,6 +1381,7 @@ class RoutingPlanner:
*,
spacing: float | ArrayLike | None,
strategy: RouteTieBreakStrategy | str | None,
tool_options: Mapping[str, Any] | None,
**route_bounds: Any,
) -> PreparedRouteResult:
if len(contexts) == 1:
@ -1383,6 +1399,7 @@ class RoutingPlanner:
diagnostic_request,
spacing=spacing,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
)
@ -1402,6 +1419,7 @@ class RoutingPlanner:
length=length,
ccw=ccw,
strategy=strategy,
tool_options=tool_options,
**other_bounds,
)
return self.prepared_result_from_legs((leg,))
@ -1414,9 +1432,11 @@ class RoutingPlanner:
*,
spacing: float | ArrayLike | None = None,
strategy: RouteTieBreakStrategy | str | None = None,
tool_options: Mapping[str, Any] | None = None,
**bounds: Any,
) -> PreparedRouteResult:
"""Plan S-bend routes for single ports or spaced bundles."""
offset = planner_bounds.finite_scalar(offset, 'offset')
request_details = {'offset': offset, **{
key: value for key, value in bounds.items() if value is not None
}}
@ -1426,6 +1446,8 @@ class RoutingPlanner:
request_details['spacing'] = spacing
if strategy is not None:
request_details['strategy'] = strategy
if tool_options:
request_details['tool_options'] = dict(tool_options)
operation: RouteOperation = 'jog'
diagnostic_request = request_details
if numpy.isclose(offset, 0):
@ -1437,6 +1459,7 @@ class RoutingPlanner:
length=length,
spacing=spacing,
strategy=strategy,
tool_options=tool_options,
**bounds,
)
route_bounds = dict(bounds)
@ -1460,6 +1483,7 @@ class RoutingPlanner:
operation,
diagnostic_request,
strategy=strategy,
tool_options=tool_options,
**other_bounds,
)
))
@ -1471,6 +1495,7 @@ class RoutingPlanner:
length=length,
jog=offset,
strategy=strategy,
tool_options=tool_options,
**other_bounds,
)
return self.prepared_result_from_legs((leg,))
@ -1483,9 +1508,11 @@ class RoutingPlanner:
*,
spacing: float | ArrayLike | None = None,
strategy: RouteTieBreakStrategy | str | None = None,
tool_options: Mapping[str, Any] | None = None,
**bounds: Any,
) -> PreparedRouteResult:
"""Plan U-turn routes for single ports or spaced bundles."""
offset = planner_bounds.finite_scalar(offset, 'offset')
route_bounds = dict(bounds)
request_details = {'offset': offset, **{
key: value for key, value in route_bounds.items() if value is not None
@ -1496,6 +1523,8 @@ class RoutingPlanner:
request_details['spacing'] = spacing
if strategy is not None:
request_details['strategy'] = strategy
if tool_options:
request_details['tool_options'] = dict(tool_options)
operation: RouteOperation = 'uturn'
diagnostic_request = request_details
portspec = tuple(context.portspec for context in contexts)
@ -1512,6 +1541,7 @@ class RoutingPlanner:
operation,
diagnostic_request,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
)
))
@ -1523,6 +1553,7 @@ class RoutingPlanner:
length=length,
jog=offset,
strategy=strategy,
tool_options=tool_options,
**route_bounds,
)
return self.prepared_result_from_legs((leg,))
@ -1539,6 +1570,7 @@ class RoutingPlanner:
/,
*,
strategy: RouteTieBreakStrategy | str | None = None,
tool_options: Mapping[str, Any] | None = None,
**kwargs: Any,
) -> tuple[RouteLeg, ...]:
"""
@ -1557,6 +1589,7 @@ class RoutingPlanner:
length=length,
jog=offset,
strategy=strategy,
tool_options=tool_options,
**kwargs,
),)
route_name = 'jog' if kind == 's' else 'uturn'
@ -1573,6 +1606,7 @@ class RoutingPlanner:
length=length,
jog=offset,
strategy=strategy,
tool_options=tool_options,
**kwargs,
)
base_length = anchor.candidate.public_length
@ -1589,6 +1623,7 @@ class RoutingPlanner:
length=spec_length,
jog=spec_offset,
strategy=strategy,
tool_options=tool_options,
**kwargs,
)
else:
@ -1600,6 +1635,7 @@ class RoutingPlanner:
length=spec_length,
jog=spec_offset,
strategy=strategy,
tool_options=tool_options,
**kwargs,
)
return tuple(routes_by_name[spec_portspec] for spec_portspec, _length, _offset in specs)
@ -1614,17 +1650,9 @@ class RoutingPlanner:
plug_destination: bool,
thru: str | None,
strategy: RouteTieBreakStrategy | str | None = None,
**kwargs: Any,
tool_options: Mapping[str, Any] | None = None,
) -> PreparedRouteResult:
"""Plan a bounded route from one source port into a destination port."""
reserved = {
'portspec', 'ccw', 'length', 'offset', 'plug_into', 'spacing', 'each', 'set_rotation',
*planner_bounds.POSITION_KEYS,
*planner_bounds.BUNDLE_BOUND_KEYS,
}
collisions = sorted(set(kwargs) & reserved)
if collisions:
raise BuildError(f'trace_into() kwargs cannot override route arguments: {", ".join(collisions)}')
if out_ptype is None:
out_ptype = port_dst.ptype
if context_src.port.rotation is None or port_dst.rotation is None:
@ -1642,7 +1670,8 @@ class RoutingPlanner:
constrain_jog=family == 'bend',
max_bends=self.TRACE_INTO_MAX_BENDS,
strategy=strategy,
**(dict(kwargs) | {'out_ptype': out_ptype}),
tool_options=tool_options,
out_ptype=out_ptype,
)
solver = self.solver_for_request(request)
candidate = None