notte-sdk 0.0.dev0__py3-none-any.whl

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.
notte_sdk/vault.py ADDED
@@ -0,0 +1,68 @@
1
+ from __future__ import annotations
2
+
3
+ import traceback
4
+ from typing import final
5
+
6
+ from loguru import logger
7
+ from notte_core.credentials.base import (
8
+ BaseVault,
9
+ CredentialField,
10
+ VaultCredentials,
11
+ )
12
+ from notte_core.utils.url import get_root_domain
13
+ from typing_extensions import override
14
+
15
+ from notte_sdk.endpoints.personas import PersonasClient
16
+
17
+
18
+ @final
19
+ class NotteVault(BaseVault):
20
+ """Vault that fetches credentials stored using the sdk"""
21
+
22
+ def __init__(self, persona_client: PersonasClient, persona_id: str):
23
+ self.persona_client = persona_client
24
+ self.persona_id = persona_id
25
+
26
+ @property
27
+ def vault_id(self):
28
+ return self.persona_id
29
+
30
+ @override
31
+ def _set_singleton_credentials(self, creds: list[CredentialField]) -> None:
32
+ for cred in creds:
33
+ if not cred.singleton:
34
+ raise ValueError(f"{cred.__class__} can't be set as singleton credential: url-specific only")
35
+
36
+ creds_dict = BaseVault.credential_fields_to_dict(creds)
37
+ _ = self.persona_client.add_credentials(self.persona_id, url=None, **creds_dict)
38
+
39
+ @override
40
+ def get_singleton_credentials(self) -> list[CredentialField]:
41
+ try:
42
+ return self.persona_client.get_credentials(self.persona_id, url=None).credentials
43
+ except Exception as e:
44
+ logger.warning(f"Could not get singleton credentials: {e} {traceback.format_exc()}")
45
+ return []
46
+
47
+ @override
48
+ def _add_credentials(self, creds: VaultCredentials) -> None:
49
+ for cred in creds.creds:
50
+ if cred.singleton:
51
+ raise ValueError(f"{cred.__class__} can't be set as url specific credential: singleton only")
52
+
53
+ domain = get_root_domain(creds.url)
54
+ creds_dict = BaseVault.credential_fields_to_dict(creds.creds)
55
+ _ = self.persona_client.add_credentials(self.persona_id, url=domain, **creds_dict)
56
+
57
+ @override
58
+ def _get_credentials_impl(self, url: str) -> VaultCredentials | None:
59
+ try:
60
+ domain = get_root_domain(url)
61
+ creds = self.persona_client.get_credentials(self.persona_id, url=domain).credentials
62
+ return VaultCredentials(url=url, creds=creds)
63
+ except Exception:
64
+ logger.warning(f"Failed to get creds: {traceback.format_exc()}")
65
+
66
+ @override
67
+ def remove_credentials(self, url: str | None) -> None:
68
+ _ = self.persona_client.delete_credentials(self.persona_id, url=url)
File without changes
@@ -0,0 +1,106 @@
1
+ import asyncio
2
+ import threading
3
+ from collections.abc import AsyncIterator
4
+ from pathlib import Path
5
+ from typing import Callable
6
+
7
+ import websockets.client
8
+ from loguru import logger
9
+ from notte_core.common.resource import SyncResource
10
+ from pydantic import BaseModel, Field, PrivateAttr
11
+ from typing_extensions import override
12
+
13
+ # def save_frames_to_video(frames: list[bytes], output_path: Path, fps: int = 10):
14
+ # import numpy as np
15
+ # import imagio
16
+ # with imageio.get_writer('output.mp4', fps=fps) as writer:
17
+ # for img in frames:
18
+ # writer.append_data(np.array(img))
19
+
20
+
21
+ class JupyterKernelViewer:
22
+ @staticmethod
23
+ def display_image(image_data: bytes):
24
+ from IPython.display import clear_output, display # type: ignore
25
+ from notte_core.utils.image import image_from_bytes
26
+
27
+ image = image_from_bytes(image_data)
28
+ clear_output(wait=True)
29
+ return display(image)
30
+
31
+
32
+ class SessionRecordingWebSocket(BaseModel, SyncResource): # type: ignore
33
+ """WebSocket client for receiving session recording data in binary format."""
34
+
35
+ wss_url: str
36
+ fps: int = 10
37
+ max_frames: int = 300
38
+ frames: list[bytes] = Field(default_factory=list)
39
+ on_frame: Callable[[bytes], None] | None = None
40
+ output_path: Path | None = None
41
+ _thread: threading.Thread | None = PrivateAttr(default=None)
42
+ _stop_event: threading.Event | None = PrivateAttr(default=None)
43
+ display_image: bool = True
44
+
45
+ def _run_async_loop(self) -> None:
46
+ """Run the async event loop in a separate thread."""
47
+ loop = asyncio.new_event_loop()
48
+ asyncio.set_event_loop(loop)
49
+ try:
50
+ loop.run_until_complete(self.watch())
51
+ finally:
52
+ loop.close()
53
+
54
+ @override
55
+ def start(self) -> None:
56
+ """Start recording in a separate thread."""
57
+ self._stop_event = threading.Event()
58
+ self._thread = threading.Thread(target=self._run_async_loop)
59
+ self._thread.start()
60
+
61
+ @override
62
+ def stop(self) -> None:
63
+ """Stop the recording thread."""
64
+ if self._stop_event:
65
+ self._stop_event.set()
66
+ if self._thread:
67
+ self._thread.join()
68
+ self._thread = None
69
+ self._stop_event = None
70
+
71
+ async def connect(self) -> AsyncIterator[bytes]:
72
+ """Connect to the WebSocket and yield binary recording data.
73
+
74
+ Yields:
75
+ Binary data chunks from the recording stream
76
+ """
77
+ try:
78
+ async with websockets.client.connect(self.wss_url) as websocket:
79
+ async for message in websocket:
80
+ if isinstance(message, bytes):
81
+ if len(self.frames) >= self.max_frames:
82
+ break
83
+ self.frames.append(message)
84
+ yield message
85
+ else:
86
+ logger.warning(f"[Session Recording] Received non-binary message: {message}")
87
+ except websockets.exceptions.WebSocketException as e:
88
+ logger.error(f"[Session Recording] WebSocket error: {e}")
89
+ raise
90
+
91
+ async def watch(self) -> None:
92
+ """Save the recording stream to a file."""
93
+ output_path = self.output_path or Path(".recordings/{dt.strftime('%Y-%m-%d_%H-%M-%S')}/")
94
+ output_path = Path(output_path)
95
+ output_path.parent.mkdir(parents=True, exist_ok=True)
96
+
97
+ with output_path.open("wb") as f:
98
+ async for chunk in self.connect():
99
+ if self._stop_event and self._stop_event.is_set():
100
+ break
101
+ _ = f.write(chunk)
102
+ if self.on_frame:
103
+ self.on_frame(chunk)
104
+ if self.display_image:
105
+ _ = JupyterKernelViewer.display_image(chunk)
106
+ logger.info(f"[Session Recording] Recording saved to {output_path}")
@@ -0,0 +1,8 @@
1
+ Metadata-Version: 2.4
2
+ Name: notte-sdk
3
+ Version: 0.0.dev0
4
+ Summary: The SDK for Notte
5
+ Author-email: Notte Team <hello@notte.cc>
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: notte-core==0.0.dev
8
+ Requires-Dist: websockets>=13.1
@@ -0,0 +1,18 @@
1
+ notte_sdk/__init__.py,sha256=cco3JQI3_a0U1S-4V2XER7w5YuGCwyNGQpkhAkCj5RA,160
2
+ notte_sdk/client.py,sha256=uMg7X4wmxBlX50Kfk17v_55srGiV3-dzoxjauzFU0dg,1807
3
+ notte_sdk/errors.py,sha256=yETg6rOO817W5XE37YiFFZxwiqj1z4uHOa6d0tyv31w,1429
4
+ notte_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ notte_sdk/types.py,sha256=1D4HWtDc1ZACWOeaUSPupjLhO55igBOzekMnl-m2q7A,28523
6
+ notte_sdk/vault.py,sha256=t2dP54RAZRCo25DZKx2h0LNVXwBh_M1LQzrPUIJ2ivA,2450
7
+ notte_sdk/endpoints/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ notte_sdk/endpoints/agents.py,sha256=wqHtgQF_65uGNJA153LW0vi2hifkWDZwkM8Y7pd0Ov0,19039
9
+ notte_sdk/endpoints/base.py,sha256=wloIHfEV5WTv82yaTczN8IjqXretMk6ov66wqvzNF-w,10272
10
+ notte_sdk/endpoints/page.py,sha256=RuZk0MfyDZSd-W7VMN2lPyTxeiMQmj2uheM-pPIf_QE,8835
11
+ notte_sdk/endpoints/personas.py,sha256=7KHzcRIoqSYfJZNVLhnBhAny-9adJRn0EjojmXAc1E4,10542
12
+ notte_sdk/endpoints/sessions.py,sha256=F8cjcWmhSihQtEYoCSeHgica-bSNdPTdap3Q-UreZQ4,21347
13
+ notte_sdk/endpoints/vaults.py,sha256=1qvKYXsJzjJjcS8xLDnrGQ_o6yCKEQ-T11uORUjR5R4,2614
14
+ notte_sdk/websockets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ notte_sdk/websockets/recording.py,sha256=4vi-GNW_LKLK3vHPAxG36VJVR7k2RhFRykRsediE6R8,3868
16
+ notte_sdk-0.0.dev0.dist-info/METADATA,sha256=Qt1vM2FiBAWh1cCsDsLzn7Ok-I0NUTpte1JD1jDEtUU,217
17
+ notte_sdk-0.0.dev0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ notte_sdk-0.0.dev0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any