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..eaaae73 100644 --- a/src/scrapybara/client.py +++ b/src/scrapybara/client.py @@ -56,6 +56,7 @@ ToolCallPart, ToolMessage, ToolResultPart, + ReasoningPart, UserMessage, AssistantMessage, Step, @@ -1168,6 +1169,7 @@ def act( assistant_msg = AssistantMessage( content=( ([TextPart(text=step.text)] if step.text else []) + + (step.reasoning_parts if step.reasoning_parts else []) + (step.tool_calls or []) ) ) @@ -1306,9 +1308,17 @@ def act_stream( if isinstance(part, ToolCallPart) ] + # Extract reasoning from reasoning part + reasoning_parts = [ + part + for part in act_response.message.content + if isinstance(part, ReasoningPart) + ] + # Create initial step step = Step( text=text, + 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, @@ -1568,6 +1578,7 @@ async def act( assistant_msg = AssistantMessage( content=( ([TextPart(text=step.text)] if step.text else []) + + (step.reasoning_parts if step.reasoning_parts else []) + (step.tool_calls or []) ) ) @@ -1706,9 +1717,17 @@ async def act_stream( if isinstance(part, ToolCallPart) ] + # Extract reasoning from reasoning part + reasoning_parts = [ + part + for part in act_response.message.content + if isinstance(part, ReasoningPart) + ] + # Create initial step step = Step( text=text, + 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/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..acad2f5 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_parts: Optional[List[ReasoningPart]] = None tool_calls: Optional[List[ToolCallPart]] = None tool_results: Optional[List[ToolResultPart]] = None finish_reason: Optional[ diff --git a/tests/custom/test_client.py b/tests/custom/test_client.py index ab01f81..951c749 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 @@ -104,14 +104,77 @@ 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 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()