narada 0.1.33a3__tar.gz → 0.1.33a5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: narada
3
- Version: 0.1.33a3
3
+ Version: 0.1.33a5
4
4
  Summary: Python client SDK for Narada
5
5
  Project-URL: Homepage, https://github.com/NaradaAI/narada-python-sdk/narada
6
6
  Project-URL: Repository, https://github.com/NaradaAI/narada-python-sdk
@@ -10,9 +10,9 @@ License-Expression: Apache-2.0
10
10
  Requires-Python: >=3.12
11
11
  Requires-Dist: aiohttp>=3.12.13
12
12
  Requires-Dist: narada-core==0.0.11
13
+ Requires-Dist: packaging==24.2
13
14
  Requires-Dist: playwright>=1.53.0
14
15
  Requires-Dist: rich>=14.0.0
15
- Requires-Dist: semver>=3.0.4
16
16
  Description-Content-Type: text/markdown
17
17
 
18
18
  <p align="center">
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "narada"
3
- version = "0.1.33-alpha.3"
3
+ version = "0.1.33a5"
4
4
  description = "Python client SDK for Narada"
5
5
  license = "Apache-2.0"
6
6
  readme = "README.md"
@@ -11,7 +11,7 @@ dependencies = [
11
11
  "aiohttp>=3.12.13",
12
12
  "playwright>=1.53.0",
13
13
  "rich>=14.0.0",
14
- "semver>=3.0.4",
14
+ "packaging==24.2",
15
15
  ]
16
16
 
17
17
  [project.urls]
@@ -10,15 +10,6 @@ from typing import Any
10
10
  from uuid import uuid4
11
11
 
12
12
  import aiohttp
13
- import semver
14
- from narada.config import BrowserConfig, ProxyConfig
15
- from narada.utils import assert_never
16
- from narada.version import __version__
17
- from narada.window import (
18
- LocalBrowserWindow,
19
- CloudBrowserWindow,
20
- create_side_panel_url,
21
- )
22
13
  from narada_core.errors import (
23
14
  NaradaExtensionMissingError,
24
15
  NaradaExtensionUnauthenticatedError,
@@ -27,6 +18,7 @@ from narada_core.errors import (
27
18
  NaradaUnsupportedBrowserError,
28
19
  )
29
20
  from narada_core.models import _SdkConfig
21
+ from packaging.version import Version
30
22
  from playwright._impl._errors import Error as PlaywrightError
31
23
  from playwright.async_api import (
32
24
  Browser,
@@ -34,14 +26,21 @@ from playwright.async_api import (
34
26
  ElementHandle,
35
27
  Page,
36
28
  Playwright,
37
- )
38
- from playwright.async_api import TimeoutError as PlaywrightTimeoutError
39
- from playwright.async_api import (
40
29
  async_playwright,
41
30
  )
31
+ from playwright.async_api import TimeoutError as PlaywrightTimeoutError
42
32
  from playwright.async_api._context_manager import PlaywrightContextManager
43
33
  from rich.console import Console
44
34
 
35
+ from narada.config import BrowserConfig, ProxyConfig
36
+ from narada.utils import assert_never
37
+ from narada.version import __version__
38
+ from narada.window import (
39
+ CloudBrowserWindow,
40
+ LocalBrowserWindow,
41
+ create_side_panel_url,
42
+ )
43
+
45
44
 
46
45
  @dataclass
47
46
  class _LaunchBrowserResult:
@@ -57,14 +56,23 @@ class Narada:
57
56
  _EXTENSION_UNAUTHENTICATED_INDICATOR_SELECTOR = "#narada-extension-unauthenticated"
58
57
  _INITIALIZATION_ERROR_INDICATOR_SELECTOR = "#narada-initialization-error"
59
58
 
60
- _api_key: str
59
+ _auth_headers: dict[str, str]
61
60
  _console: Console
62
61
  _playwright_context_manager: PlaywrightContextManager | None = None
63
62
  _playwright: Playwright | None = None
64
63
  _cloud_windows: set[CloudBrowserWindow]
65
64
 
66
- def __init__(self, *, api_key: str | None = None) -> None:
67
- self._api_key = api_key or os.environ["NARADA_API_KEY"]
65
+ def __init__(
66
+ self,
67
+ *,
68
+ api_key: str | None = None,
69
+ auth_headers: dict[str, str] | None = None,
70
+ ) -> None:
71
+ if auth_headers is not None:
72
+ self._auth_headers = auth_headers
73
+ else:
74
+ api_key = api_key or os.environ["NARADA_API_KEY"]
75
+ self._auth_headers = {"x-api-key": api_key}
68
76
  self._console = Console()
69
77
  self._cloud_windows = set()
70
78
 
@@ -94,9 +102,7 @@ class Narada:
94
102
 
95
103
  try:
96
104
  async with aiohttp.ClientSession() as session:
97
- async with session.get(
98
- url, headers={"x-api-key": self._api_key}
99
- ) as resp:
105
+ async with session.get(url, headers=self._auth_headers) as resp:
100
106
  if not resp.ok:
101
107
  logging.warning(
102
108
  "Failed to fetch SDK config: %s %s",
@@ -116,7 +122,9 @@ class Narada:
116
122
  return
117
123
 
118
124
  package_config = config.packages["narada"]
119
- if semver.compare(__version__, package_config.min_required_version) < 0:
125
+ current_version = Version(__version__)
126
+ min_required_version = Version(package_config.min_required_version)
127
+ if current_version < min_required_version:
120
128
  raise RuntimeError(
121
129
  f"narada<={__version__} is not supported. Please upgrade to version "
122
130
  f"{package_config.min_required_version} or higher."
@@ -137,7 +145,7 @@ class Narada:
137
145
  await self._fix_download_behavior(side_panel_page)
138
146
 
139
147
  return LocalBrowserWindow(
140
- api_key=self._api_key,
148
+ auth_headers=self._auth_headers,
141
149
  browser_process_id=launch_browser_result.browser_process_id,
142
150
  browser_window_id=browser_window_id,
143
151
  config=config,
@@ -170,7 +178,7 @@ class Narada:
170
178
  async with aiohttp.ClientSession() as session:
171
179
  async with session.post(
172
180
  endpoint_url,
173
- headers={"x-api-key": self._api_key},
181
+ headers=self._auth_headers,
174
182
  json=request_body,
175
183
  timeout=aiohttp.ClientTimeout(
176
184
  total=180
@@ -200,7 +208,7 @@ class Narada:
200
208
  async with aiohttp.ClientSession() as cleanup_session:
201
209
  async with cleanup_session.post(
202
210
  f"{base_url}/cloud-browser/stop-cloud-browser-session",
203
- headers={"x-api-key": self._api_key},
211
+ headers=self._auth_headers,
204
212
  json={"session_id": session_id},
205
213
  timeout=aiohttp.ClientTimeout(total=10),
206
214
  ) as resp:
@@ -248,7 +256,7 @@ class Narada:
248
256
  cloud_window = CloudBrowserWindow(
249
257
  browser_window_id=browser_window_id,
250
258
  session_id=session_id,
251
- api_key=self._api_key,
259
+ auth_headers=self._auth_headers,
252
260
  )
253
261
 
254
262
  # Track the window for cleanup in __aexit__
@@ -311,7 +319,7 @@ class Narada:
311
319
  self._print_success_message(browser_window_id)
312
320
 
313
321
  return LocalBrowserWindow(
314
- api_key=self._api_key,
322
+ auth_headers=self._auth_headers,
315
323
  browser_process_id=None,
316
324
  browser_window_id=browser_window_id,
317
325
  config=config,
@@ -8,7 +8,6 @@ from pathlib import Path
8
8
  from typing import IO, Any, TypeVar, overload
9
9
 
10
10
  import aiohttp
11
- from narada.config import BrowserConfig
12
11
  from narada_core.actions.models import (
13
12
  AgenticMouseAction,
14
13
  AgenticMouseActionRequest,
@@ -53,6 +52,8 @@ from playwright.async_api import (
53
52
  )
54
53
  from pydantic import BaseModel
55
54
 
55
+ from narada.config import BrowserConfig
56
+
56
57
  logger = logging.getLogger(__name__)
57
58
 
58
59
  _StructuredOutput = TypeVar("_StructuredOutput", bound=BaseModel)
@@ -67,18 +68,18 @@ class _PresignedPost(BaseModel):
67
68
 
68
69
 
69
70
  class BaseBrowserWindow(ABC):
70
- _api_key: str
71
+ _auth_headers: dict[str, str]
71
72
  _base_url: str
72
73
  _browser_window_id: str
73
74
 
74
75
  def __init__(
75
76
  self,
76
77
  *,
77
- api_key: str,
78
+ auth_headers: dict[str, str],
78
79
  base_url: str,
79
80
  browser_window_id: str,
80
81
  ) -> None:
81
- self._api_key = api_key
82
+ self._auth_headers = auth_headers
82
83
  self._base_url = base_url
83
84
  self._browser_window_id = browser_window_id
84
85
 
@@ -99,7 +100,7 @@ class BaseBrowserWindow(ABC):
99
100
  # First generate a presigned POST for uploading the file.
100
101
  async with session.post(
101
102
  f"{self._base_url}/remote-dispatch/generate-file-upload-presigned-post",
102
- headers={"x-api-key": self._api_key},
103
+ headers=self._auth_headers,
103
104
  json={"filename": filename},
104
105
  ) as resp:
105
106
  resp.raise_for_status()
@@ -192,8 +193,6 @@ class BaseBrowserWindow(ABC):
192
193
  """
193
194
  deadline = time.monotonic() + timeout
194
195
 
195
- headers = {"x-api-key": self._api_key}
196
-
197
196
  agent_prefix = (
198
197
  agent.prompt_prefix() if isinstance(agent, Agent) else f"{agent} "
199
198
  )
@@ -238,7 +237,7 @@ class BaseBrowserWindow(ABC):
238
237
  async with aiohttp.ClientSession() as session:
239
238
  async with session.post(
240
239
  f"{self._base_url}/remote-dispatch",
241
- headers=headers,
240
+ headers=self._auth_headers,
242
241
  json=body,
243
242
  timeout=aiohttp.ClientTimeout(total=timeout),
244
243
  ) as resp:
@@ -248,7 +247,7 @@ class BaseBrowserWindow(ABC):
248
247
  while (now := time.monotonic()) < deadline:
249
248
  async with session.get(
250
249
  f"{self._base_url}/remote-dispatch/responses/{request_id}",
251
- headers=headers,
250
+ headers=self._auth_headers,
252
251
  timeout=aiohttp.ClientTimeout(total=deadline - now),
253
252
  ) as resp:
254
253
  resp.raise_for_status()
@@ -380,6 +379,7 @@ class BaseBrowserWindow(ABC):
380
379
  selectors=selectors,
381
380
  fallback_operator_query=fallback_operator_query,
382
381
  ),
382
+ response_model=response_model,
383
383
  timeout=timeout,
384
384
  )
385
385
 
@@ -511,8 +511,6 @@ class BaseBrowserWindow(ABC):
511
511
  *,
512
512
  timeout: int | None = None,
513
513
  ) -> _ResponseModel | None:
514
- headers = {"x-api-key": self._api_key}
515
-
516
514
  body = {
517
515
  "action": request.model_dump(),
518
516
  "browserWindowId": self.browser_window_id,
@@ -523,7 +521,7 @@ class BaseBrowserWindow(ABC):
523
521
  async with aiohttp.ClientSession() as session:
524
522
  async with session.post(
525
523
  f"{self._base_url}/extension-actions",
526
- headers=headers,
524
+ headers=self._auth_headers,
527
525
  json=body,
528
526
  # Don't specify `timeout` here as the (soft) timeout is handled by the server.
529
527
  ) as resp:
@@ -551,7 +549,7 @@ class LocalBrowserWindow(BaseBrowserWindow):
551
549
  def __init__(
552
550
  self,
553
551
  *,
554
- api_key: str,
552
+ auth_headers: dict[str, str],
555
553
  browser_process_id: int | None,
556
554
  browser_window_id: str,
557
555
  config: BrowserConfig,
@@ -559,7 +557,7 @@ class LocalBrowserWindow(BaseBrowserWindow):
559
557
  ) -> None:
560
558
  base_url = os.getenv("NARADA_API_BASE_URL", "https://api.narada.ai/fast/v2")
561
559
  super().__init__(
562
- api_key=api_key,
560
+ auth_headers=auth_headers,
563
561
  base_url=base_url,
564
562
  browser_window_id=browser_window_id,
565
563
  )
@@ -591,10 +589,19 @@ class LocalBrowserWindow(BaseBrowserWindow):
591
589
 
592
590
 
593
591
  class RemoteBrowserWindow(BaseBrowserWindow):
594
- def __init__(self, *, browser_window_id: str, api_key: str | None = None) -> None:
592
+ def __init__(
593
+ self,
594
+ *,
595
+ browser_window_id: str,
596
+ api_key: str | None = None,
597
+ auth_headers: dict[str, str] | None = None,
598
+ ) -> None:
595
599
  base_url = os.getenv("NARADA_API_BASE_URL", "https://api.narada.ai/fast/v2")
600
+ if auth_headers is None:
601
+ api_key = api_key or os.environ["NARADA_API_KEY"]
602
+ auth_headers = {"x-api-key": api_key}
596
603
  super().__init__(
597
- api_key=api_key or os.environ["NARADA_API_KEY"],
604
+ auth_headers=auth_headers,
598
605
  base_url=base_url,
599
606
  browser_window_id=browser_window_id,
600
607
  )
@@ -616,10 +623,14 @@ class CloudBrowserWindow(BaseBrowserWindow):
616
623
  browser_window_id: str,
617
624
  session_id: str,
618
625
  api_key: str | None = None,
626
+ auth_headers: dict[str, str] | None = None,
619
627
  ) -> None:
620
628
  base_url = os.getenv("NARADA_API_BASE_URL", "https://api.narada.ai/fast/v2")
629
+ if auth_headers is None:
630
+ api_key = api_key or os.environ["NARADA_API_KEY"]
631
+ auth_headers = {"x-api-key": api_key}
621
632
  super().__init__(
622
- api_key=api_key or os.environ["NARADA_API_KEY"],
633
+ auth_headers=auth_headers,
623
634
  base_url=base_url,
624
635
  browser_window_id=browser_window_id,
625
636
  )
@@ -631,7 +642,7 @@ class CloudBrowserWindow(BaseBrowserWindow):
631
642
  async with aiohttp.ClientSession() as session:
632
643
  async with session.post(
633
644
  f"{self._base_url}/cloud-browser/stop-cloud-browser-session",
634
- headers={"x-api-key": self._api_key},
645
+ headers=self._auth_headers,
635
646
  json={
636
647
  "session_id": self._session_id,
637
648
  },
File without changes
File without changes
File without changes
File without changes