From 582f07a863e6299e998c943f115d823d03a28f97 Mon Sep 17 00:00:00 2001 From: Cooper Miller Date: Wed, 26 Feb 2025 14:51:07 -0600 Subject: [PATCH 1/9] new ReasoningPart type --- pyproject.toml | 2 +- src/scrapybara/client.py | 17 +++++++++++++++++ src/scrapybara/core/client_wrapper.py | 2 +- src/scrapybara/types/act.py | 8 +++++++- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 32fd0ae..d5715c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "scrapybara" -version = "2.2.8" +version = "2.2.9" description = "" readme = "README.md" authors = [] diff --git a/src/scrapybara/client.py b/src/scrapybara/client.py index 78f6bc8..27c9c8e 100644 --- a/src/scrapybara/client.py +++ b/src/scrapybara/client.py @@ -56,6 +56,7 @@ ToolCallPart, ToolMessage, ToolResultPart, + ReasoningPart, UserMessage, AssistantMessage, Step, @@ -1306,9 +1307,17 @@ def act_stream( if isinstance(part, ToolCallPart) ] + # Extract reasoning from reasoning part + reasoning = "\n".join( + part.reasoning + for part in act_response.message.content + if isinstance(part, ReasoningPart) + ) + # Create initial step step = Step( text=text, + reasoning=reasoning, tool_calls=tool_calls if tool_calls else None, finish_reason=act_response.finish_reason, usage=act_response.usage, @@ -1706,9 +1715,17 @@ async def act_stream( if isinstance(part, ToolCallPart) ] + # Extract reasoning from reasoning part + reasoning = "\n".join( + part.reasoning + for part in act_response.message.content + if isinstance(part, ReasoningPart) + ) + # Create initial step step = Step( text=text, + reasoning=reasoning, tool_calls=tool_calls if tool_calls else None, finish_reason=act_response.finish_reason, usage=act_response.usage, diff --git a/src/scrapybara/core/client_wrapper.py b/src/scrapybara/core/client_wrapper.py index 4cdb0d2..53ce88b 100644 --- a/src/scrapybara/core/client_wrapper.py +++ b/src/scrapybara/core/client_wrapper.py @@ -16,7 +16,7 @@ def get_headers(self) -> typing.Dict[str, str]: headers: typing.Dict[str, str] = { "X-Fern-Language": "Python", "X-Fern-SDK-Name": "scrapybara", - "X-Fern-SDK-Version": "2.2.8", + "X-Fern-SDK-Version": "2.2.9", } headers["x-api-key"] = self.api_key return headers diff --git a/src/scrapybara/types/act.py b/src/scrapybara/types/act.py index 9ae3053..61707bf 100644 --- a/src/scrapybara/types/act.py +++ b/src/scrapybara/types/act.py @@ -31,6 +31,11 @@ class ToolResultPart(BaseModel): result: Any is_error: Optional[bool] = False +class ReasoningPart(BaseModel): + type: Literal["reasoning"] = "reasoning" + reasoning: str + signature: Optional[str] = None + instructions: Optional[str] = None class UserMessage(BaseModel): role: Literal["user"] = "user" @@ -39,7 +44,7 @@ class UserMessage(BaseModel): class AssistantMessage(BaseModel): role: Literal["assistant"] = "assistant" - content: List[Union[TextPart, ToolCallPart]] + content: List[Union[TextPart, ToolCallPart, ReasoningPart]] class ToolMessage(BaseModel): @@ -83,6 +88,7 @@ class SingleActResponse(BaseModel): # Step definition class Step(BaseModel): text: str + reasoning: Optional[str] = None tool_calls: Optional[List[ToolCallPart]] = None tool_results: Optional[List[ToolResultPart]] = None finish_reason: Optional[ From dc15cd61f6448ba6961a65d6a58dffb0471fdaee Mon Sep 17 00:00:00 2001 From: Cooper Miller Date: Wed, 26 Feb 2025 14:55:43 -0600 Subject: [PATCH 2/9] dont pass empty string --- src/scrapybara/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scrapybara/client.py b/src/scrapybara/client.py index 27c9c8e..4e371a6 100644 --- a/src/scrapybara/client.py +++ b/src/scrapybara/client.py @@ -1317,7 +1317,7 @@ def act_stream( # Create initial step step = Step( text=text, - reasoning=reasoning, + reasoning=reasoning if reasoning else None, tool_calls=tool_calls if tool_calls else None, finish_reason=act_response.finish_reason, usage=act_response.usage, @@ -1725,7 +1725,7 @@ async def act_stream( # Create initial step step = Step( text=text, - reasoning=reasoning, + reasoning=reasoning if reasoning else None, tool_calls=tool_calls if tool_calls else None, finish_reason=act_response.finish_reason, usage=act_response.usage, From a2a1af87ed9200e6ac06ac6bcccaf15d935dc11f Mon Sep 17 00:00:00 2001 From: Cooper Miller Date: Wed, 26 Feb 2025 15:48:48 -0600 Subject: [PATCH 3/9] reasoning in recon assistant msg --- src/scrapybara/client.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/scrapybara/client.py b/src/scrapybara/client.py index 4e371a6..c86bed8 100644 --- a/src/scrapybara/client.py +++ b/src/scrapybara/client.py @@ -1166,12 +1166,15 @@ def act( request_options=request_options, ): steps.append(step) - assistant_msg = AssistantMessage( - content=( - ([TextPart(text=step.text)] if step.text else []) - + (step.tool_calls or []) - ) - ) + content_parts = [] + if step.text: + content_parts.append(TextPart(text=step.text)) + if step.reasoning: + content_parts.append(ReasoningPart(reasoning=step.reasoning)) + if step.tool_calls: + content_parts.extend(step.tool_calls) + + assistant_msg = AssistantMessage(content=content_parts) result_messages.append(assistant_msg) if step.tool_results: tool_msg = ToolMessage(content=step.tool_results) @@ -1574,12 +1577,15 @@ async def act( request_options=request_options, ): steps.append(step) - assistant_msg = AssistantMessage( - content=( - ([TextPart(text=step.text)] if step.text else []) - + (step.tool_calls or []) - ) - ) + content_parts = [] + if step.text: + content_parts.append(TextPart(text=step.text)) + if step.reasoning: + content_parts.append(ReasoningPart(reasoning=step.reasoning)) + if step.tool_calls: + content_parts.extend(step.tool_calls) + + assistant_msg = AssistantMessage(content=content_parts) result_messages.append(assistant_msg) if step.tool_results: tool_msg = ToolMessage(content=step.tool_results) From 9de8041f321c5c900488109d8588027078f89281 Mon Sep 17 00:00:00 2001 From: Cooper Miller Date: Wed, 26 Feb 2025 15:54:22 -0600 Subject: [PATCH 4/9] reasoning actually in recon assistant msg --- src/scrapybara/client.py | 32 ++++++++++++++------------------ 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/src/scrapybara/client.py b/src/scrapybara/client.py index c86bed8..0a12603 100644 --- a/src/scrapybara/client.py +++ b/src/scrapybara/client.py @@ -1166,15 +1166,13 @@ def act( request_options=request_options, ): steps.append(step) - content_parts = [] - if step.text: - content_parts.append(TextPart(text=step.text)) - if step.reasoning: - content_parts.append(ReasoningPart(reasoning=step.reasoning)) - if step.tool_calls: - content_parts.extend(step.tool_calls) - - assistant_msg = AssistantMessage(content=content_parts) + assistant_msg = AssistantMessage( + content=( + ([TextPart(text=step.text)] if step.text else []) + + ([ReasoningPart(reasoning=step.reasoning)] if step.reasoning else []) + + (step.tool_calls or []) + ) + ) result_messages.append(assistant_msg) if step.tool_results: tool_msg = ToolMessage(content=step.tool_results) @@ -1577,15 +1575,13 @@ async def act( request_options=request_options, ): steps.append(step) - content_parts = [] - if step.text: - content_parts.append(TextPart(text=step.text)) - if step.reasoning: - content_parts.append(ReasoningPart(reasoning=step.reasoning)) - if step.tool_calls: - content_parts.extend(step.tool_calls) - - assistant_msg = AssistantMessage(content=content_parts) + assistant_msg = AssistantMessage( + content=( + ([TextPart(text=step.text)] if step.text else []) + + ([ReasoningPart(reasoning=step.reasoning)] if step.reasoning else []) + + (step.tool_calls or []) + ) + ) result_messages.append(assistant_msg) if step.tool_results: tool_msg = ToolMessage(content=step.tool_results) From 3e329cdd04eefdf2870068e68a3b2255f3c95dc9 Mon Sep 17 00:00:00 2001 From: Justin Sun Date: Wed, 26 Feb 2025 14:50:45 -0800 Subject: [PATCH 5/9] chore: modify test print statement --- tests/custom/test_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/custom/test_client.py b/tests/custom/test_client.py index ab01f81..e811f59 100644 --- a/tests/custom/test_client.py +++ b/tests/custom/test_client.py @@ -48,7 +48,7 @@ def test_ubuntu() -> None: schema=YCStats, on_step=lambda step: print(step.text, step.tool_calls), ) - print(response) + print(response.output) assert response.output is not None assert response.output.number_of_startups is not None assert response.output.combined_valuation is not None @@ -77,7 +77,7 @@ def test_browser() -> None: schema=YCStats, on_step=lambda step: print(step.text, step.tool_calls), ) - print(response) + print(response.output) assert response.output is not None assert response.output.number_of_startups is not None assert response.output.combined_valuation is not None From cb5f349878da29b5c96476c7b91c4672fe20356e Mon Sep 17 00:00:00 2001 From: Justin Sun Date: Wed, 26 Feb 2025 14:54:32 -0800 Subject: [PATCH 6/9] oops --- tests/custom/test_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/custom/test_client.py b/tests/custom/test_client.py index e811f59..a923676 100644 --- a/tests/custom/test_client.py +++ b/tests/custom/test_client.py @@ -104,7 +104,7 @@ def test_windows() -> None: schema=YCStats, on_step=lambda step: print(step.text, step.tool_calls), ) - print(response) + print(response.output) assert response.output is not None assert response.output.number_of_startups is not None assert response.output.combined_valuation is not None From d74366a4a66f5e2c3f2b11336a9019802ed59d2c Mon Sep 17 00:00:00 2001 From: Cooper Miller Date: Wed, 26 Feb 2025 18:17:37 -0600 Subject: [PATCH 7/9] Step requires entire ReasoningPart --- src/scrapybara/client.py | 16 ++++++++-------- src/scrapybara/types/act.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/scrapybara/client.py b/src/scrapybara/client.py index 0a12603..5573de4 100644 --- a/src/scrapybara/client.py +++ b/src/scrapybara/client.py @@ -1169,7 +1169,7 @@ def act( assistant_msg = AssistantMessage( content=( ([TextPart(text=step.text)] if step.text else []) - + ([ReasoningPart(reasoning=step.reasoning)] if step.reasoning else []) + + (step.reasoning if step.reasoning else []) + (step.tool_calls or []) ) ) @@ -1309,11 +1309,11 @@ def act_stream( ] # Extract reasoning from reasoning part - reasoning = "\n".join( - part.reasoning + reasoning = [ + part for part in act_response.message.content if isinstance(part, ReasoningPart) - ) + ] # Create initial step step = Step( @@ -1578,7 +1578,7 @@ async def act( assistant_msg = AssistantMessage( content=( ([TextPart(text=step.text)] if step.text else []) - + ([ReasoningPart(reasoning=step.reasoning)] if step.reasoning else []) + + (step.reasoning if step.reasoning else []) + (step.tool_calls or []) ) ) @@ -1718,11 +1718,11 @@ async def act_stream( ] # Extract reasoning from reasoning part - reasoning = "\n".join( - part.reasoning + reasoning = [ + part for part in act_response.message.content if isinstance(part, ReasoningPart) - ) + ] # Create initial step step = Step( diff --git a/src/scrapybara/types/act.py b/src/scrapybara/types/act.py index 61707bf..13891f6 100644 --- a/src/scrapybara/types/act.py +++ b/src/scrapybara/types/act.py @@ -88,7 +88,7 @@ class SingleActResponse(BaseModel): # Step definition class Step(BaseModel): text: str - reasoning: Optional[str] = None + reasoning: Optional[List[ReasoningPart]] = None tool_calls: Optional[List[ToolCallPart]] = None tool_results: Optional[List[ToolResultPart]] = None finish_reason: Optional[ From 2bd7cfe2e9524c5ef0161938d7fbf3d111e78cfd Mon Sep 17 00:00:00 2001 From: Cooper Miller Date: Wed, 26 Feb 2025 18:43:11 -0600 Subject: [PATCH 8/9] fix: reasoning_parts --- src/scrapybara/client.py | 12 ++++++------ src/scrapybara/types/act.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/scrapybara/client.py b/src/scrapybara/client.py index 5573de4..eaaae73 100644 --- a/src/scrapybara/client.py +++ b/src/scrapybara/client.py @@ -1169,7 +1169,7 @@ def act( assistant_msg = AssistantMessage( content=( ([TextPart(text=step.text)] if step.text else []) - + (step.reasoning if step.reasoning else []) + + (step.reasoning_parts if step.reasoning_parts else []) + (step.tool_calls or []) ) ) @@ -1309,7 +1309,7 @@ def act_stream( ] # Extract reasoning from reasoning part - reasoning = [ + reasoning_parts = [ part for part in act_response.message.content if isinstance(part, ReasoningPart) @@ -1318,7 +1318,7 @@ def act_stream( # Create initial step step = Step( text=text, - reasoning=reasoning if reasoning else None, + reasoning_parts=reasoning_parts if reasoning_parts else None, tool_calls=tool_calls if tool_calls else None, finish_reason=act_response.finish_reason, usage=act_response.usage, @@ -1578,7 +1578,7 @@ async def act( assistant_msg = AssistantMessage( content=( ([TextPart(text=step.text)] if step.text else []) - + (step.reasoning if step.reasoning else []) + + (step.reasoning_parts if step.reasoning_parts else []) + (step.tool_calls or []) ) ) @@ -1718,7 +1718,7 @@ async def act_stream( ] # Extract reasoning from reasoning part - reasoning = [ + reasoning_parts = [ part for part in act_response.message.content if isinstance(part, ReasoningPart) @@ -1727,7 +1727,7 @@ async def act_stream( # Create initial step step = Step( text=text, - reasoning=reasoning if reasoning else None, + reasoning_parts=reasoning_parts if reasoning_parts else None, tool_calls=tool_calls if tool_calls else None, finish_reason=act_response.finish_reason, usage=act_response.usage, diff --git a/src/scrapybara/types/act.py b/src/scrapybara/types/act.py index 13891f6..acad2f5 100644 --- a/src/scrapybara/types/act.py +++ b/src/scrapybara/types/act.py @@ -88,7 +88,7 @@ class SingleActResponse(BaseModel): # Step definition class Step(BaseModel): text: str - reasoning: Optional[List[ReasoningPart]] = None + reasoning_parts: Optional[List[ReasoningPart]] = None tool_calls: Optional[List[ToolCallPart]] = None tool_results: Optional[List[ToolResultPart]] = None finish_reason: Optional[ From 1c6e9ecb77030816807d90a84af4e28ac99a8bbe Mon Sep 17 00:00:00 2001 From: Cooper Miller Date: Wed, 26 Feb 2025 19:23:11 -0600 Subject: [PATCH 9/9] test claude-3-7-sonnet-20250219-thinking --- tests/custom/test_client.py | 63 +++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/tests/custom/test_client.py b/tests/custom/test_client.py index a923676..951c749 100644 --- a/tests/custom/test_client.py +++ b/tests/custom/test_client.py @@ -110,8 +110,71 @@ def test_windows() -> None: assert response.output.combined_valuation is not None windows_instance.stop() +def test_ubuntu_thinking() -> None: + _check_api_key() + client = Scrapybara() + + ubuntu_instance = client.start_ubuntu() + print(ubuntu_instance.get_stream_url().stream_url) + assert ubuntu_instance.id is not None + instances = client.get_instances() + assert len(instances) > 0 + screenshot_response = ubuntu_instance.screenshot() + assert screenshot_response.base_64_image is not None + ubuntu_instance.browser.start() + cdp_url = ubuntu_instance.browser.get_cdp_url() + assert cdp_url is not None + response = client.act( + model=Anthropic(name="claude-3-7-sonnet-20250219-thinking"), + system=UBUNTU_SYSTEM_PROMPT, + prompt="Go to the YC website and get the number of funded startups and combined valuation", + tools=[ + ComputerTool(ubuntu_instance), + BashTool(ubuntu_instance), + EditTool(ubuntu_instance), + ], + schema=YCStats, + on_step=lambda step: print(step.text, step.tool_calls, step.reasoning_parts), + ) + print(response.output) + assert response.output is not None + assert response.output.number_of_startups is not None + assert response.output.combined_valuation is not None + ubuntu_instance.browser.stop() + ubuntu_instance.stop() + + +def test_browser_thinking() -> None: + _check_api_key() + client = Scrapybara() + + browser_instance = client.start_browser() + print(browser_instance.get_stream_url().stream_url) + assert browser_instance.id is not None + screenshot_response = browser_instance.screenshot() + assert screenshot_response.base_64_image is not None + cdp_url = browser_instance.get_cdp_url() + assert cdp_url is not None + response = client.act( + model=Anthropic(name="claude-3-7-sonnet-20250219-thinking"), + system=BROWSER_SYSTEM_PROMPT, + prompt="Go to the YC website and get the number of funded startups and combined valuation", + tools=[ + ComputerTool(browser_instance), + ], + schema=YCStats, + on_step=lambda step: print(step.text, step.tool_calls, step.reasoning_parts), + ) + print(response.output) + assert response.output is not None + assert response.output.number_of_startups is not None + assert response.output.combined_valuation is not None + browser_instance.stop() + if __name__ == "__main__": test_ubuntu() test_browser() + test_ubuntu_thinking() + test_browser_thinking() # test_windows()