narada 0.1.26__tar.gz → 0.1.28__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.26
3
+ Version: 0.1.28
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
@@ -9,9 +9,10 @@ Author-email: Narada <support@narada.ai>
9
9
  License-Expression: Apache-2.0
10
10
  Requires-Python: >=3.12
11
11
  Requires-Dist: aiohttp>=3.12.13
12
- Requires-Dist: narada-core==0.0.5
12
+ Requires-Dist: narada-core==0.0.6
13
13
  Requires-Dist: playwright>=1.53.0
14
14
  Requires-Dist: rich>=14.0.0
15
+ Requires-Dist: semver>=3.0.4
15
16
  Description-Content-Type: text/markdown
16
17
 
17
18
  <p align="center">
@@ -1,16 +1,17 @@
1
1
  [project]
2
2
  name = "narada"
3
- version = "0.1.26"
3
+ version = "0.1.28"
4
4
  description = "Python client SDK for Narada"
5
5
  license = "Apache-2.0"
6
6
  readme = "README.md"
7
7
  authors = [{ name = "Narada", email = "support@narada.ai" }]
8
8
  requires-python = ">=3.12"
9
9
  dependencies = [
10
- "narada-core==0.0.5",
10
+ "narada-core==0.0.6",
11
11
  "aiohttp>=3.12.13",
12
12
  "playwright>=1.53.0",
13
13
  "rich>=14.0.0",
14
+ "semver>=3.0.4",
14
15
  ]
15
16
 
16
17
  [project.urls]
@@ -1,5 +1,3 @@
1
- import importlib.metadata
2
-
3
1
  from narada_core.errors import (
4
2
  NaradaError,
5
3
  NaradaExtensionMissingError,
@@ -12,20 +10,15 @@ from narada_core.models import Agent, File, Response, ResponseContent
12
10
 
13
11
  from narada.client import Narada
14
12
  from narada.config import BrowserConfig
15
- from narada.render_html import render_html
13
+ from narada.utils import download_file, render_html
14
+ from narada.version import __version__
16
15
  from narada.window import LocalBrowserWindow, RemoteBrowserWindow
17
16
 
18
- # Get version from package metadata
19
- try:
20
- __version__ = importlib.metadata.version("narada")
21
- except Exception:
22
- # Fallback version if package metadata is not available
23
- __version__ = "unknown"
24
-
25
17
  __all__ = [
26
18
  "__version__",
27
19
  "Agent",
28
20
  "BrowserConfig",
21
+ "download_file",
29
22
  "File",
30
23
  "LocalBrowserWindow",
31
24
  "Narada",
@@ -9,6 +9,8 @@ from dataclasses import dataclass
9
9
  from typing import Any
10
10
  from uuid import uuid4
11
11
 
12
+ import aiohttp
13
+ import semver
12
14
  from narada_core.errors import (
13
15
  NaradaExtensionMissingError,
14
16
  NaradaExtensionUnauthenticatedError,
@@ -16,6 +18,7 @@ from narada_core.errors import (
16
18
  NaradaTimeoutError,
17
19
  NaradaUnsupportedBrowserError,
18
20
  )
21
+ from narada_core.models import _SdkConfig
19
22
  from playwright._impl._errors import Error as PlaywrightError
20
23
  from playwright.async_api import (
21
24
  ElementHandle,
@@ -31,6 +34,7 @@ from rich.console import Console
31
34
 
32
35
  from narada.config import BrowserConfig
33
36
  from narada.utils import assert_never
37
+ from narada.version import __version__
34
38
  from narada.window import LocalBrowserWindow, create_side_panel_url
35
39
 
36
40
 
@@ -58,6 +62,8 @@ class Narada:
58
62
  self._console = Console()
59
63
 
60
64
  async def __aenter__(self) -> Narada:
65
+ await self._validate_sdk_config()
66
+
61
67
  self._playwright_context_manager = async_playwright()
62
68
  self._playwright = await self._playwright_context_manager.__aenter__()
63
69
  return self
@@ -70,6 +76,40 @@ class Narada:
70
76
  self._playwright_context_manager = None
71
77
  self._playwright = None
72
78
 
79
+ async def _fetch_sdk_config(self) -> _SdkConfig | None:
80
+ base_url = os.getenv("NARADA_API_BASE_URL", "https://api.narada.ai/fast/v2")
81
+ url = f"{base_url}/sdk/config"
82
+
83
+ try:
84
+ async with aiohttp.ClientSession() as session:
85
+ async with session.get(
86
+ url, headers={"x-api-key": self._api_key}
87
+ ) as resp:
88
+ if not resp.ok:
89
+ logging.warning(
90
+ "Failed to fetch SDK config: %s %s",
91
+ resp.status,
92
+ await resp.text(),
93
+ )
94
+ return None
95
+
96
+ return _SdkConfig.model_validate(await resp.json())
97
+ except Exception as e:
98
+ logging.warning("Failed to fetch SDK config: %s", e)
99
+ return None
100
+
101
+ async def _validate_sdk_config(self) -> None:
102
+ config = await self._fetch_sdk_config()
103
+ if config is None:
104
+ return
105
+
106
+ package_config = config.packages["narada"]
107
+ if semver.compare(__version__, package_config.min_required_version) < 0:
108
+ raise RuntimeError(
109
+ f"narada<={__version__} is not supported. Please upgrade to version "
110
+ f"{package_config.min_required_version} or higher."
111
+ )
112
+
73
113
  async def open_and_initialize_browser_window(
74
114
  self, config: BrowserConfig | None = None
75
115
  ) -> LocalBrowserWindow:
@@ -0,0 +1,46 @@
1
+ import webbrowser
2
+ from pathlib import Path
3
+ from tempfile import NamedTemporaryFile
4
+ from typing import Never
5
+
6
+
7
+ def assert_never() -> Never:
8
+ raise AssertionError("Expected code to be unreachable")
9
+
10
+
11
+ def download_file(filename: str, content: str | bytes) -> None:
12
+ """
13
+ Downloads a file to the user's Downloads directory.
14
+
15
+ Args:
16
+ filename: The name of the file to save. Can include subdirectories
17
+ (e.g., "reports/2025/data.csv") relative to the Downloads
18
+ directory.
19
+ content: The content to write. If str, writes in text mode (UTF-8).
20
+ If bytes, writes in binary mode.
21
+ """
22
+ path = Path.home() / "Downloads" / filename
23
+ path.parent.mkdir(parents=True, exist_ok=True)
24
+ if isinstance(content, str):
25
+ path.write_text(content, encoding="utf-8")
26
+ else:
27
+ path.write_bytes(content)
28
+
29
+
30
+ def render_html(html: str) -> None:
31
+ """
32
+ Renders HTML content by opening it in the default browser.
33
+
34
+ Args:
35
+ html: The HTML content to render.
36
+ """
37
+ with NamedTemporaryFile(
38
+ mode="w+t",
39
+ encoding="utf-8",
40
+ suffix=".html",
41
+ delete=False,
42
+ ) as temp:
43
+ temp.write(html)
44
+ path = temp.name
45
+
46
+ webbrowser.open_new_tab(f"file://{path}")
@@ -0,0 +1,7 @@
1
+ import importlib.metadata
2
+
3
+ try:
4
+ __version__ = importlib.metadata.version("narada")
5
+ except Exception:
6
+ # Fallback version if package metadata is not available
7
+ __version__ = "unknown"
@@ -1,21 +0,0 @@
1
- import webbrowser
2
- from tempfile import NamedTemporaryFile
3
-
4
-
5
- def render_html(html: str) -> None:
6
- """
7
- Renders HTML content by opening it in the default browser.
8
-
9
- Args:
10
- html: The HTML content to render.
11
- """
12
- with NamedTemporaryFile(
13
- mode="w+t",
14
- encoding="utf-8",
15
- suffix=".html",
16
- delete=False,
17
- ) as temp:
18
- temp.write(html)
19
- path = temp.name
20
-
21
- webbrowser.open_new_tab(f"file://{path}")
@@ -1,5 +0,0 @@
1
- from typing import Never
2
-
3
-
4
- def assert_never() -> Never:
5
- raise AssertionError("Expected code to be unreachable")
File without changes
File without changes
File without changes
File without changes
File without changes