diff --git a/CLAUDE.md b/CLAUDE.md index 1a8af886..2889e373 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -47,7 +47,7 @@ client.crawl # Website crawling jobs client.extract # Data extraction jobs client.web # Web fetch/search operations client.web.batch_fetch # Batch web fetch operations -client.agents # AI agent integrations (browser_use, cua, claude_computer_use, hyper_agent, gemini_computer_use, grok_computer_use, meta_computer_use) +client.agents # AI agent integrations (browser_use, cua, claude_computer_use, hyper_agent, gemini_computer_use, grok_computer_use, jev_computer_use, meta_computer_use) client.profiles # Browser profile management client.extensions # Browser extension management client.team # Team/credit info diff --git a/hyperbrowser/client/managers/async_manager/agents/__init__.py b/hyperbrowser/client/managers/async_manager/agents/__init__.py index 601993bd..150101f5 100644 --- a/hyperbrowser/client/managers/async_manager/agents/__init__.py +++ b/hyperbrowser/client/managers/async_manager/agents/__init__.py @@ -4,6 +4,7 @@ from .hyper_agent import HyperAgentManager from .gemini_computer_use import GeminiComputerUseManager from .grok_computer_use import GrokComputerUseManager +from .jev_computer_use import JevComputerUseManager from .meta_computer_use import MetaComputerUseManager @@ -15,4 +16,5 @@ def __init__(self, client): self.hyper_agent = HyperAgentManager(client) self.gemini_computer_use = GeminiComputerUseManager(client) self.grok_computer_use = GrokComputerUseManager(client) + self.jev_computer_use = JevComputerUseManager(client) self.meta_computer_use = MetaComputerUseManager(client) diff --git a/hyperbrowser/client/managers/async_manager/agents/jev_computer_use.py b/hyperbrowser/client/managers/async_manager/agents/jev_computer_use.py new file mode 100644 index 00000000..21f9c456 --- /dev/null +++ b/hyperbrowser/client/managers/async_manager/agents/jev_computer_use.py @@ -0,0 +1,84 @@ +import asyncio +from typing import Union + +from hyperbrowser.client._request import dump_request +from hyperbrowser.exceptions import HyperbrowserError +from hyperbrowser.types import ( + StartJevComputerUseTaskParams as StartJevComputerUseTaskParamsDict, +) + +from .....models import ( + POLLING_ATTEMPTS, + BasicResponse, + JevComputerUseTaskResponse, + JevComputerUseTaskStatusResponse, + StartJevComputerUseTaskParams, + StartJevComputerUseTaskResponse, +) + + +class JevComputerUseManager: + def __init__(self, client): + self._client = client + + async def start( + self, + params: Union[ + StartJevComputerUseTaskParamsDict, + StartJevComputerUseTaskParams, + ], + ) -> StartJevComputerUseTaskResponse: + response = await self._client.transport.post( + self._client._build_url("/task/jev"), + data=dump_request(params, StartJevComputerUseTaskParams), + ) + return StartJevComputerUseTaskResponse(**response.data) + + async def get(self, job_id: str) -> JevComputerUseTaskResponse: + response = await self._client.transport.get( + self._client._build_url(f"/task/jev/{job_id}") + ) + return JevComputerUseTaskResponse(**response.data) + + async def get_status(self, job_id: str) -> JevComputerUseTaskStatusResponse: + response = await self._client.transport.get( + self._client._build_url(f"/task/jev/{job_id}/status") + ) + return JevComputerUseTaskStatusResponse(**response.data) + + async def stop(self, job_id: str) -> BasicResponse: + response = await self._client.transport.put( + self._client._build_url(f"/task/jev/{job_id}/stop") + ) + return BasicResponse(**response.data) + + async def start_and_wait( + self, + params: Union[ + StartJevComputerUseTaskParamsDict, + StartJevComputerUseTaskParams, + ], + ) -> JevComputerUseTaskResponse: + job_start_resp = await self.start(params) + job_id = job_start_resp.job_id + if not job_id: + raise HyperbrowserError("Failed to start Jev Computer Use task job") + + failures = 0 + while True: + try: + job_response = await self.get_status(job_id) + if ( + job_response.status == "completed" + or job_response.status == "failed" + or job_response.status == "stopped" + ): + return await self.get(job_id) + failures = 0 + except Exception as e: + failures += 1 + if failures >= POLLING_ATTEMPTS: + raise HyperbrowserError( + f"Failed to poll Jev Computer Use task job {job_id} after {POLLING_ATTEMPTS} attempts: {e}" + ) + await asyncio.sleep(2) diff --git a/hyperbrowser/client/managers/sync_manager/agents/__init__.py b/hyperbrowser/client/managers/sync_manager/agents/__init__.py index 601993bd..150101f5 100644 --- a/hyperbrowser/client/managers/sync_manager/agents/__init__.py +++ b/hyperbrowser/client/managers/sync_manager/agents/__init__.py @@ -4,6 +4,7 @@ from .hyper_agent import HyperAgentManager from .gemini_computer_use import GeminiComputerUseManager from .grok_computer_use import GrokComputerUseManager +from .jev_computer_use import JevComputerUseManager from .meta_computer_use import MetaComputerUseManager @@ -15,4 +16,5 @@ def __init__(self, client): self.hyper_agent = HyperAgentManager(client) self.gemini_computer_use = GeminiComputerUseManager(client) self.grok_computer_use = GrokComputerUseManager(client) + self.jev_computer_use = JevComputerUseManager(client) self.meta_computer_use = MetaComputerUseManager(client) diff --git a/hyperbrowser/client/managers/sync_manager/agents/jev_computer_use.py b/hyperbrowser/client/managers/sync_manager/agents/jev_computer_use.py new file mode 100644 index 00000000..f0c5e196 --- /dev/null +++ b/hyperbrowser/client/managers/sync_manager/agents/jev_computer_use.py @@ -0,0 +1,84 @@ +import time +from typing import Union + +from hyperbrowser.client._request import dump_request +from hyperbrowser.exceptions import HyperbrowserError +from hyperbrowser.types import ( + StartJevComputerUseTaskParams as StartJevComputerUseTaskParamsDict, +) + +from .....models import ( + POLLING_ATTEMPTS, + BasicResponse, + JevComputerUseTaskResponse, + JevComputerUseTaskStatusResponse, + StartJevComputerUseTaskParams, + StartJevComputerUseTaskResponse, +) + + +class JevComputerUseManager: + def __init__(self, client): + self._client = client + + def start( + self, + params: Union[ + StartJevComputerUseTaskParamsDict, + StartJevComputerUseTaskParams, + ], + ) -> StartJevComputerUseTaskResponse: + response = self._client.transport.post( + self._client._build_url("/task/jev"), + data=dump_request(params, StartJevComputerUseTaskParams), + ) + return StartJevComputerUseTaskResponse(**response.data) + + def get(self, job_id: str) -> JevComputerUseTaskResponse: + response = self._client.transport.get( + self._client._build_url(f"/task/jev/{job_id}") + ) + return JevComputerUseTaskResponse(**response.data) + + def get_status(self, job_id: str) -> JevComputerUseTaskStatusResponse: + response = self._client.transport.get( + self._client._build_url(f"/task/jev/{job_id}/status") + ) + return JevComputerUseTaskStatusResponse(**response.data) + + def stop(self, job_id: str) -> BasicResponse: + response = self._client.transport.put( + self._client._build_url(f"/task/jev/{job_id}/stop") + ) + return BasicResponse(**response.data) + + def start_and_wait( + self, + params: Union[ + StartJevComputerUseTaskParamsDict, + StartJevComputerUseTaskParams, + ], + ) -> JevComputerUseTaskResponse: + job_start_resp = self.start(params) + job_id = job_start_resp.job_id + if not job_id: + raise HyperbrowserError("Failed to start Jev Computer Use task job") + + failures = 0 + while True: + try: + job_response = self.get_status(job_id) + if ( + job_response.status == "completed" + or job_response.status == "failed" + or job_response.status == "stopped" + ): + return self.get(job_id) + failures = 0 + except Exception as e: + failures += 1 + if failures >= POLLING_ATTEMPTS: + raise HyperbrowserError( + f"Failed to poll Jev Computer Use task job {job_id} after {POLLING_ATTEMPTS} attempts: {e}" + ) + time.sleep(2) diff --git a/hyperbrowser/models/__init__.py b/hyperbrowser/models/__init__.py index 49d8f29e..7bc21fe0 100644 --- a/hyperbrowser/models/__init__.py +++ b/hyperbrowser/models/__init__.py @@ -138,6 +138,15 @@ StartMetaComputerUseTaskResponse, MetaComputerUseApiKeys, ) +from .agents.jev_computer_use import ( + JevComputerUseTaskStatus, + JevComputerUseTaskData, + JevComputerUseTaskResponse, + JevComputerUseTaskStatusResponse, + StartJevComputerUseTaskParams, + StartJevComputerUseTaskResponse, + JevComputerUseApiKeys, +) from .agents.cua import ( CuaTaskData, CuaTaskResponse, @@ -175,6 +184,8 @@ GrokReasoningEffort, MetaComputerUseLlm, MetaReasoningEffort, + JevComputerUseLlm, + JevTextLlm, Country, DownloadsStatus, FetchScreenshotFormat, @@ -521,6 +532,12 @@ "MetaComputerUseTaskStatusResponse", "StartMetaComputerUseTaskParams", "StartMetaComputerUseTaskResponse", + "JevComputerUseTaskStatus", + "JevComputerUseTaskData", + "JevComputerUseTaskResponse", + "JevComputerUseTaskStatusResponse", + "StartJevComputerUseTaskParams", + "StartJevComputerUseTaskResponse", "CuaTaskStatus", "CuaTaskData", "CuaTaskResponse", @@ -531,6 +548,7 @@ "ClaudeComputerUseApiKeys", "GeminiComputerUseApiKeys", "GrokComputerUseApiKeys", + "JevComputerUseApiKeys", "MetaComputerUseApiKeys", "CuaApiKeys", "CuaBaseUrls", @@ -539,6 +557,8 @@ "GrokReasoningEffort", "MetaComputerUseLlm", "MetaReasoningEffort", + "JevComputerUseLlm", + "JevTextLlm", "HyperAgentOutputV110", "HyperAgentStepV110", # crawl diff --git a/hyperbrowser/models/agents/jev_computer_use.py b/hyperbrowser/models/agents/jev_computer_use.py new file mode 100644 index 00000000..5fb5b56a --- /dev/null +++ b/hyperbrowser/models/agents/jev_computer_use.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from typing import Any, Dict, List, Literal, Optional + +from pydantic import BaseModel, ConfigDict, Field + +from ..session import CreateSessionParams +from ..consts import JevComputerUseLlm, JevTextLlm + +JevComputerUseTaskStatus = Literal[ + "pending", "running", "completed", "failed", "stopped" +] + + +class JevComputerUseApiKeys(BaseModel): + """ + API keys for the Jev Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + jev: Optional[str] = Field(default=None, serialization_alias="jev") + google: Optional[str] = Field(default=None, serialization_alias="google") + + +class StartJevComputerUseTaskParams(BaseModel): + """ + Parameters for creating a new Jev Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + task: str + llm: Optional[JevComputerUseLlm] = Field(default=None, serialization_alias="llm") + text_llm: Optional[JevTextLlm] = Field(default=None, serialization_alias="textLlm") + session_id: Optional[str] = Field(default=None, serialization_alias="sessionId") + max_failures: Optional[int] = Field(default=None, serialization_alias="maxFailures") + max_steps: Optional[int] = Field(default=None, serialization_alias="maxSteps") + keep_browser_open: Optional[bool] = Field( + default=None, serialization_alias="keepBrowserOpen" + ) + session_options: Optional[CreateSessionParams] = Field( + default=None, serialization_alias="sessionOptions" + ) + use_custom_api_keys: Optional[bool] = Field( + default=None, serialization_alias="useCustomApiKeys" + ) + api_keys: Optional[JevComputerUseApiKeys] = Field( + default=None, serialization_alias="apiKeys" + ) + + +class StartJevComputerUseTaskResponse(BaseModel): + """ + Response from starting a Jev Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + job_id: str = Field(alias="jobId") + live_url: Optional[str] = Field(default=None, alias="liveUrl") + + +class JevComputerUseTaskStatusResponse(BaseModel): + """ + Response from getting a Jev Computer Use task status. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + status: JevComputerUseTaskStatus + + +class JevComputerUseTaskData(BaseModel): + model_config = ConfigDict( + populate_by_alias=True, + ) + + steps: List[Dict[str, Any]] + final_result: Optional[str] = Field(default=None, alias="finalResult") + + +class JevComputerUseTaskMetadata(BaseModel): + model_config = ConfigDict( + populate_by_alias=True, + ) + + input_tokens: Optional[int] = Field(default=None, alias="inputTokens") + output_tokens: Optional[int] = Field(default=None, alias="outputTokens") + num_task_steps_completed: Optional[int] = Field( + default=None, alias="numTaskStepsCompleted" + ) + + +class JevComputerUseTaskResponse(BaseModel): + """ + Response from a Jev Computer Use task. + """ + + model_config = ConfigDict( + populate_by_alias=True, + ) + + job_id: str = Field(alias="jobId") + status: JevComputerUseTaskStatus + metadata: Optional[JevComputerUseTaskMetadata] = Field( + default=None, alias="metadata" + ) + data: Optional[JevComputerUseTaskData] = Field(default=None, alias="data") + error: Optional[str] = Field(default=None, alias="error") + live_url: Optional[str] = Field(default=None, alias="liveUrl") diff --git a/hyperbrowser/models/consts.py b/hyperbrowser/models/consts.py index 698e8a3f..ec3de57c 100644 --- a/hyperbrowser/models/consts.py +++ b/hyperbrowser/models/consts.py @@ -68,6 +68,7 @@ "gemini-3-flash-preview", ] ClaudeComputerUseLlm = Literal[ + "claude-opus-5-5", "claude-fable-5-1", "claude-opus-5", "claude-opus-4-5", @@ -91,6 +92,8 @@ CuaLlm = Literal[ "computer-use-preview", "gpt-6-astra", + "gpt-6-sol", + "gpt-6-luna", "gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna", @@ -138,6 +141,11 @@ "xhigh", "max", ] +JevComputerUseLlm = Literal[ + "jev-1.13.0", + "jev-latest", +] +JevTextLlm = Literal["gemini-3.5-flash-lite",] SessionRegion = Literal[ "us", "us-central", diff --git a/hyperbrowser/types/__init__.py b/hyperbrowser/types/__init__.py index f0ebdfc8..4633a631 100644 --- a/hyperbrowser/types/__init__.py +++ b/hyperbrowser/types/__init__.py @@ -20,6 +20,7 @@ GeminiComputerUseApiKeys, GrokComputerUseApiKeys, HyperAgentApiKeys, + JevComputerUseApiKeys, MetaComputerUseApiKeys, StartBrowserUseTaskParams, StartClaudeComputerUseTaskParams, @@ -27,6 +28,7 @@ StartGeminiComputerUseTaskParams, StartGrokComputerUseTaskParams, StartHyperAgentTaskParams, + StartJevComputerUseTaskParams, StartMetaComputerUseTaskParams, ) from .computer_action import ( @@ -186,6 +188,7 @@ "GeminiComputerUseApiKeys", "GrokComputerUseApiKeys", "HyperAgentApiKeys", + "JevComputerUseApiKeys", "MetaComputerUseApiKeys", "StartBrowserUseTaskParams", "StartClaudeComputerUseTaskParams", @@ -193,6 +196,7 @@ "StartGeminiComputerUseTaskParams", "StartGrokComputerUseTaskParams", "StartHyperAgentTaskParams", + "StartJevComputerUseTaskParams", "StartMetaComputerUseTaskParams", "ClickActionParams", "ComputerActionMouseButton", diff --git a/hyperbrowser/types/agents.py b/hyperbrowser/types/agents.py index dc8b4b8a..bb6592e0 100644 --- a/hyperbrowser/types/agents.py +++ b/hyperbrowser/types/agents.py @@ -14,6 +14,8 @@ GrokReasoningEffort, MetaComputerUseLlm, MetaReasoningEffort, + JevComputerUseLlm, + JevTextLlm, HyperAgentLlm, HyperAgentVersion, ) @@ -144,6 +146,28 @@ class StartMetaComputerUseTaskParams(TypedDict, total=False): use_computer_action: Optional[bool] +class JevComputerUseApiKeys(TypedDict, total=False): + """Provider API keys for a Jev Computer Use task.""" + + jev: Optional[str] + google: Optional[str] + + +class StartJevComputerUseTaskParams(TypedDict, total=False): + """Parameters for starting a Jev Computer Use task.""" + + task: Required[str] + llm: Optional[JevComputerUseLlm] + text_llm: Optional[JevTextLlm] + session_id: Optional[str] + max_failures: Optional[int] + max_steps: Optional[int] + keep_browser_open: Optional[bool] + session_options: Optional[CreateSessionParams] + use_custom_api_keys: Optional[bool] + api_keys: Optional[JevComputerUseApiKeys] + + class CuaApiKeys(TypedDict, total=False): """Provider API keys for an OpenAI CUA task.""" @@ -204,6 +228,7 @@ class StartHyperAgentTaskParams(TypedDict, total=False): "GeminiComputerUseApiKeys", "GrokComputerUseApiKeys", "HyperAgentApiKeys", + "JevComputerUseApiKeys", "MetaComputerUseApiKeys", "StartBrowserUseTaskParams", "StartClaudeComputerUseTaskParams", @@ -211,5 +236,6 @@ class StartHyperAgentTaskParams(TypedDict, total=False): "StartGeminiComputerUseTaskParams", "StartGrokComputerUseTaskParams", "StartHyperAgentTaskParams", + "StartJevComputerUseTaskParams", "StartMetaComputerUseTaskParams", ] diff --git a/pyproject.toml b/pyproject.toml index 1fe69998..eb223a03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "hyperbrowser" -version = "1.5.0" +version = "1.6.0" description = "Python SDK for hyperbrowser" authors = ["Nikhil Shahi "] license = "MIT" diff --git a/tests/test_claude_computer_use_models.py b/tests/test_claude_computer_use_models.py new file mode 100644 index 00000000..18c3b930 --- /dev/null +++ b/tests/test_claude_computer_use_models.py @@ -0,0 +1,17 @@ +from hyperbrowser.client._request import dump_request +from hyperbrowser.models.agents.claude_computer_use import ( + StartClaudeComputerUseTaskParams, +) + + +def test_start_claude_computer_use_accepts_opus_5_5() -> None: + dumped = dump_request( + { + "task": "Inspect the page", + "llm": "claude-opus-5-5", + "reasoning_effort": "max", + }, + StartClaudeComputerUseTaskParams, + ) + assert dumped["llm"] == "claude-opus-5-5" + assert dumped["reasoningEffort"] == "max" diff --git a/tests/test_cua_models.py b/tests/test_cua_models.py new file mode 100644 index 00000000..c196ba1e --- /dev/null +++ b/tests/test_cua_models.py @@ -0,0 +1,20 @@ +import pytest +from pydantic import ValidationError + +from hyperbrowser.client._request import dump_request +from hyperbrowser.models.agents.cua import StartCuaTaskParams + + +@pytest.mark.parametrize("llm", ["gpt-6-sol", "gpt-6-luna", "gpt-6-astra"]) +def test_start_cua_accepts_gpt6_models(llm: str) -> None: + dumped = dump_request( + {"task": "Inspect the page", "llm": llm, "reasoning_effort": "max"}, + StartCuaTaskParams, + ) + assert dumped["llm"] == llm + assert dumped["reasoningEffort"] == "max" + + +def test_start_cua_rejects_unknown_model() -> None: + with pytest.raises(ValidationError): + StartCuaTaskParams(task="Inspect the page", llm="gpt-6-orbit") diff --git a/tests/test_jev_computer_use_models.py b/tests/test_jev_computer_use_models.py new file mode 100644 index 00000000..e92122cb --- /dev/null +++ b/tests/test_jev_computer_use_models.py @@ -0,0 +1,71 @@ +import pytest +from pydantic import ValidationError + +from hyperbrowser.client._request import dump_request +from hyperbrowser.models.agents.jev_computer_use import ( + JevComputerUseTaskResponse, + StartJevComputerUseTaskParams, +) + + +def test_jev_computer_use_task_response_parses_step_payload() -> None: + payload = { + "jobId": "job-123", + "status": "completed", + "data": { + "steps": [ + { + "step": 1, + "action": {"kind": "click", "target": "submit"}, + "url": "https://example.com", + "title": "Example", + "pageText": "Order details", + "targetDescription": "Submit button", + "confidence": 0.91, + "outcome": "dispatched", + "pageChanged": True, + "beforeObservation": 0, + "afterObservation": 1, + } + ], + "finalResult": "Found the order", + }, + } + + result = JevComputerUseTaskResponse(**payload) + + step = result.data.steps[0] + assert isinstance(step, dict) + assert step["step"] == 1 + assert step["action"] == {"kind": "click", "target": "submit"} + assert step["pageText"] == "Order details" + assert result.data.final_result == "Found the order" + assert not hasattr(result.data, "final_url") + assert not hasattr(result.data, "reached_max_steps") + assert not hasattr(result.data, "warnings") + + +@pytest.mark.parametrize("llm", ["jev-1.13.0", "jev-latest"]) +def test_start_jev_computer_use_accepts_api_models(llm: str) -> None: + model_params = StartJevComputerUseTaskParams( + task="Find the order", + llm=llm, + text_llm="gemini-3.5-flash-lite", + ) + dict_params = { + "task": "Find the order", + "llm": llm, + "text_llm": "gemini-3.5-flash-lite", + } + + for params in (model_params, dict_params): + dumped = dump_request(params, StartJevComputerUseTaskParams) + assert dumped["task"] == "Find the order" + assert dumped["llm"] == llm + assert dumped["textLlm"] == "gemini-3.5-flash-lite" + assert "startUrl" not in dumped + + +def test_start_jev_computer_use_rejects_unknown_model() -> None: + with pytest.raises(ValidationError): + StartJevComputerUseTaskParams(task="Find the order", llm="jev-1.0.0") diff --git a/tests/test_request_compat.py b/tests/test_request_compat.py index 4fe23f5b..7c163dc4 100644 --- a/tests/test_request_compat.py +++ b/tests/test_request_compat.py @@ -12,10 +12,12 @@ CuaBaseUrls, CreateSandboxParams, CreateSessionParams, + JevComputerUseApiKeys, MetaComputerUseApiKeys, StartBrowserUseTaskParams, StartClaudeComputerUseTaskParams, StartCuaTaskParams, + StartJevComputerUseTaskParams, StartMetaComputerUseTaskParams, ) from hyperbrowser.tools import _normalize_extract_tool_params @@ -121,14 +123,14 @@ def test_cua_custom_base_url_serializes_for_mapping_and_legacy_model(): def test_claude_computer_use_serializes_reasoning_effort_for_mapping_and_legacy_model(): mapping = { "task": "Complete the task", - "llm": "claude-opus-5", + "llm": "claude-opus-5-5", "reasoning_effort": "xhigh", "use_custom_api_keys": True, "api_keys": {"anthropic": "anthropic-key"}, } legacy = StartClaudeComputerUseTaskParams( task="Complete the task", - llm="claude-opus-5", + llm="claude-opus-5-5", reasoning_effort="xhigh", use_custom_api_keys=True, api_keys=ClaudeComputerUseApiKeys(anthropic="anthropic-key"), @@ -140,7 +142,7 @@ def test_claude_computer_use_serializes_reasoning_effort_for_mapping_and_legacy_ ) assert dump_request(mapping, StartClaudeComputerUseTaskParams) == { "task": "Complete the task", - "llm": "claude-opus-5", + "llm": "claude-opus-5-5", "reasoningEffort": "xhigh", "useCustomApiKeys": True, "apiKeys": {"anthropic": "anthropic-key"}, @@ -179,6 +181,35 @@ def test_meta_computer_use_serializes_for_mapping_and_legacy_model(): } +def test_jev_computer_use_serializes_for_mapping_and_legacy_model(): + mapping = { + "task": "Find the order", + "llm": "jev-1.13.0", + "text_llm": "gemini-3.5-flash-lite", + "use_custom_api_keys": True, + "api_keys": {"jev": "jev-key", "google": "google-key"}, + } + legacy = StartJevComputerUseTaskParams( + task="Find the order", + llm="jev-1.13.0", + text_llm="gemini-3.5-flash-lite", + use_custom_api_keys=True, + api_keys=JevComputerUseApiKeys(jev="jev-key", google="google-key"), + ) + + assert dump_request(mapping, StartJevComputerUseTaskParams) == dump_request( + legacy, + StartJevComputerUseTaskParams, + ) + assert dump_request(mapping, StartJevComputerUseTaskParams) == { + "task": "Find the order", + "llm": "jev-1.13.0", + "textLlm": "gemini-3.5-flash-lite", + "useCustomApiKeys": True, + "apiKeys": {"jev": "jev-key", "google": "google-key"}, + } + + def test_raw_json_schema_is_copied_without_rewriting_or_dereferencing(): schema = { "$defs": { diff --git a/tests/typecheck/valid_requests.py b/tests/typecheck/valid_requests.py index 08c42e56..6c2a97ca 100644 --- a/tests/typecheck/valid_requests.py +++ b/tests/typecheck/valid_requests.py @@ -99,7 +99,7 @@ def valid_sync_requests(client: Hyperbrowser) -> None: client.agents.cua.start( { "task": "Complete the task", - "llm": "gpt-5.6-terra", + "llm": "gpt-6-sol", "reasoning_effort": "high", "use_custom_api_keys": True, "api_keys": {"openai": "openai-key"}, @@ -111,10 +111,19 @@ def valid_sync_requests(client: Hyperbrowser) -> None: client.agents.claude_computer_use.start( { "task": "Complete the task", - "llm": "claude-opus-5", + "llm": "claude-opus-5-5", "reasoning_effort": "xhigh", } ) + client.agents.jev_computer_use.start( + { + "task": "Find the order", + "llm": "jev-1.13.0", + "text_llm": "gemini-3.5-flash-lite", + "use_custom_api_keys": True, + "api_keys": {"jev": "jev-key", "google": "google-key"}, + } + ) client.agents.meta_computer_use.start( { "task": "Complete the task", @@ -264,7 +273,7 @@ async def valid_async_requests(client: AsyncHyperbrowser) -> None: await client.agents.cua.start( { "task": "Complete the task", - "llm": "gpt-5.4", + "llm": "gpt-6-luna", "reasoning_effort": "none", "use_custom_api_keys": True, "api_keys": {"openai": "openai-key"}, @@ -280,6 +289,13 @@ async def valid_async_requests(client: AsyncHyperbrowser) -> None: "reasoning_effort": "max", } ) + await client.agents.jev_computer_use.start( + { + "task": "Find the order", + "llm": "jev-latest", + "text_llm": "gemini-3.5-flash-lite", + } + ) await client.agents.meta_computer_use.start( { "task": "Complete the task",