From 2944b78d704012c75e20c19ee01af434f80c6a18 Mon Sep 17 00:00:00 2001 From: Jovan Nadendla Date: Tue, 11 Aug 2026 15:38:13 -0500 Subject: [PATCH 1/3] feat: add targeted list limit updates --- README.md | 4 ++++ client.py | 13 +++++++++++++ robot.py | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+) diff --git a/README.md b/README.md index 662644b..47e3633 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,10 @@ specific = await robot.get_run("run-id") # Update metadata or workflow await robot.update({"meta": {"name": "New Name"}}) + +# Update a list, crawl, or search limit without resending the workflow +await robot.set_list_limit(25) + await robot.refresh() # reload from server # Delete diff --git a/client.py b/client.py index a16b406..1167ee7 100644 --- a/client.py +++ b/client.py @@ -93,6 +93,19 @@ async def update_robot(self, robot_id: str, updates: dict): raise MaxunError(f"Failed to update robot {robot_id}") return data + async def update_list_limits(self, robot_id: str, limits: list,): + """Update one or more list limits without resending the workflow.""" + data = await self._handle( + self.client.put( + f"/robots/{robot_id}", + json={"limits": limits}, + ) + ) + + if not data: + raise MaxunError(f"Failed to update list limits for robot {robot_id}") + + return data async def delete_robot(self, robot_id: str): await self._handle(self.client.delete(f"/robots/{robot_id}")) diff --git a/robot.py b/robot.py index 6bbeb1a..7797280 100644 --- a/robot.py +++ b/robot.py @@ -1,5 +1,6 @@ from typing import Optional, List from .client import Client +from .types import MaxunError class Robot: @@ -62,6 +63,45 @@ async def update(self, updates: dict) -> None: updated = await self.client.update_robot(self.id, updates) self.robot_data = updated + async def set_list_limit(self, limit: int) -> None: + supported_actions = {"scrapeList", "crawl", "search"} + workflow = ( + (self.robot_data.get("recording") or {}) + .get("workflow") + or [] + ) + + for pair_index, pair in enumerate(workflow): + for action_index, action in enumerate( + pair.get("what") or [] + ): + if action.get("action") not in supported_actions: + continue + + for arg_index, arg in enumerate( + action.get("args") or [] + ): + if isinstance(arg, dict) and "limit" in arg: + self.robot_data = ( + await self.client.update_list_limits( + self.id, + [ + { + "pairIndex": pair_index, + "actionIndex": action_index, + "argIndex": arg_index, + "limit": limit, + } + ], + ) + ) + return + + raise MaxunError( + "This robot has no scrapeList, crawl, or search " + "action with a limit to update." + ) + async def duplicate(self, target_url: str): new_robot_data = await self.client.duplicate_robot(self.id, target_url) return Robot(self.client, new_robot_data) From 4033b1a5f0d365302706782b2927336843519f4a Mon Sep 17 00:00:00 2001 From: Jovan Nadendla <91028790+J-Nad@users.noreply.github.com> Date: Tue, 11 Aug 2026 19:01:10 -0500 Subject: [PATCH 2/3] Fix syntax by removing trailing comma in method --- client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client.py b/client.py index 1167ee7..2cd9d95 100644 --- a/client.py +++ b/client.py @@ -93,7 +93,7 @@ async def update_robot(self, robot_id: str, updates: dict): raise MaxunError(f"Failed to update robot {robot_id}") return data - async def update_list_limits(self, robot_id: str, limits: list,): + async def update_list_limits(self, robot_id: str, limits: list): """Update one or more list limits without resending the workflow.""" data = await self._handle( self.client.put( @@ -282,4 +282,4 @@ async def create_crawl_robot(self, url: str, options: dict): async def create_search_robot(self, options: dict): return await self._handle( self.client.post("/search", json=options) - ) \ No newline at end of file + ) From c83edea32b6408229daec0ec8d577d81496c6046 Mon Sep 17 00:00:00 2001 From: Jovan Nadendla Date: Tue, 18 Aug 2026 17:50:47 -0500 Subject: [PATCH 3/3] Add typing and docs for targeted list limits --- client.py | 6 +++--- robot.py | 1 + types.py | 7 ++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/client.py b/client.py index 2cd9d95..d014383 100644 --- a/client.py +++ b/client.py @@ -2,9 +2,9 @@ import os import httpx from datetime import datetime, timezone -from typing import Optional, Union +from typing import Optional, Union, List from .llm_options import build_llm_payload -from .types import Config, MaxunError +from .types import Config, MaxunError, ListLimitUpdate def _document_content_type(file_name: str) -> str: @@ -93,7 +93,7 @@ async def update_robot(self, robot_id: str, updates: dict): raise MaxunError(f"Failed to update robot {robot_id}") return data - async def update_list_limits(self, robot_id: str, limits: list): + async def update_list_limits(self, robot_id: str, limits: List[ListLimitUpdate],): """Update one or more list limits without resending the workflow.""" data = await self._handle( self.client.put( diff --git a/robot.py b/robot.py index 7797280..4cb908f 100644 --- a/robot.py +++ b/robot.py @@ -64,6 +64,7 @@ async def update(self, updates: dict) -> None: self.robot_data = updated async def set_list_limit(self, limit: int) -> None: + """Set the limit for the first matching scrapeList, crawl, or search action.""" supported_actions = {"scrapeList", "crawl", "search"} workflow = ( (self.robot_data.get("recording") or {}) diff --git a/types.py b/types.py index fca6553..3ee57c8 100644 --- a/types.py +++ b/types.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from typing import Optional, List, Dict, Any, Literal +from typing import Optional, List, Dict, Any, Literal, TypedDict # ====================== # Core Types @@ -117,6 +117,11 @@ class SearchOptions: RunResult = Dict[str, Any] ApiResponse = Dict[str, Any] +class ListLimitUpdate(TypedDict): + pairIndex: int + actionIndex: int + argIndex: int + limit: int class MaxunError(Exception): def __init__(self, message: str, status_code: Optional[int] = None, details: Any = None):