hyperpocket 0.4.4__py3-none-any.whl → 0.5.0__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.
Files changed (34) hide show
  1. hyperpocket/auth/kraken/README.md +8 -0
  2. hyperpocket/auth/kraken/context.py +14 -0
  3. hyperpocket/auth/kraken/keypair_context.py +17 -0
  4. hyperpocket/auth/kraken/keypair_handler.py +97 -0
  5. hyperpocket/auth/kraken/keypair_schema.py +10 -0
  6. hyperpocket/auth/provider.py +1 -0
  7. hyperpocket/auth/valyu/token_handler.py +1 -2
  8. hyperpocket/builtin.py +3 -3
  9. hyperpocket/cli/__main__.py +0 -2
  10. hyperpocket/config/logger.py +2 -3
  11. hyperpocket/futures/futurestore.py +7 -2
  12. hyperpocket/pocket_auth.py +10 -8
  13. hyperpocket/pocket_main.py +251 -100
  14. hyperpocket/server/auth/kraken.py +58 -0
  15. hyperpocket/server/server.py +70 -239
  16. hyperpocket/session/in_memory.py +20 -26
  17. hyperpocket/tool/__init__.py +1 -2
  18. hyperpocket/tool/dock/dock.py +6 -25
  19. hyperpocket/tool/function/tool.py +1 -1
  20. hyperpocket/tool/tool.py +6 -35
  21. hyperpocket/tool_like.py +2 -3
  22. hyperpocket/util/git_parser.py +63 -0
  23. hyperpocket/util/json_schema_to_model.py +2 -2
  24. hyperpocket/util/short_hashing_str.py +5 -0
  25. {hyperpocket-0.4.4.dist-info → hyperpocket-0.5.0.dist-info}/METADATA +5 -5
  26. {hyperpocket-0.4.4.dist-info → hyperpocket-0.5.0.dist-info}/RECORD +29 -26
  27. hyperpocket/pocket_core.py +0 -283
  28. hyperpocket/repository/__init__.py +0 -4
  29. hyperpocket/repository/repository.py +0 -8
  30. hyperpocket/repository/tool_reference.py +0 -28
  31. hyperpocket/util/generate_slug.py +0 -4
  32. /hyperpocket/{tool/tests → auth/kraken}/__init__.py +0 -0
  33. {hyperpocket-0.4.4.dist-info → hyperpocket-0.5.0.dist-info}/WHEEL +0 -0
  34. {hyperpocket-0.4.4.dist-info → hyperpocket-0.5.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,8 @@
1
+ # Valyu Authentication
2
+
3
+ ## Supported Authentication Types
4
+ - Token
5
+
6
+ ## Additional Resources
7
+
8
+ - [Valyu API Documentation](https://www.valyu.network/)
@@ -0,0 +1,14 @@
1
+ from hyperpocket.auth.context import AuthContext
2
+
3
+
4
+ class KrakenAuthContext(AuthContext):
5
+ def to_dict(self) -> dict[str, str]:
6
+ return {
7
+ 'KRAKEN_API_KEY': self.detail["KRAKEN_API_KEY"],
8
+ 'KRAKEN_API_SECRET': self.detail["KRAKEN_API_SECRET"],
9
+ }
10
+
11
+ def to_profiled_dict(self, profile: str) -> dict[str, str]:
12
+ return {
13
+ f"{profile.upper()}_{self._ACCESS_TOKEN_KEY}": self.access_token,
14
+ }
@@ -0,0 +1,17 @@
1
+ from hyperpocket.auth.kraken.context import KrakenAuthContext
2
+ from hyperpocket.auth.kraken.keypair_schema import KrakenKeypairResponse
3
+
4
+
5
+ class KrakenKeypairAuthContext(KrakenAuthContext):
6
+ @classmethod
7
+ def from_kraken_keypair_response(cls, response: KrakenKeypairResponse):
8
+ description = f"Kraken Keypair Context logged in"
9
+ return cls(
10
+ access_token="",
11
+ detail={
12
+ "KRAKEN_API_KEY": response.kraken_api_key,
13
+ "KRAKEN_API_SECRET": response.kraken_api_secret,
14
+ },
15
+ description=description,
16
+ expires_at=None,
17
+ )
@@ -0,0 +1,97 @@
1
+ from typing import Optional
2
+ from urllib.parse import urljoin, urlencode
3
+
4
+ from hyperpocket.auth import AuthProvider
5
+ from hyperpocket.auth.ahrefs.token_schema import AhrefsTokenRequest
6
+ from hyperpocket.auth.context import AuthContext
7
+ from hyperpocket.auth.handler import AuthHandlerInterface
8
+ from hyperpocket.auth.kraken.keypair_context import KrakenKeypairAuthContext
9
+ from hyperpocket.auth.kraken.keypair_schema import KrakenKeypairRequest, KrakenKeypairResponse
10
+ from hyperpocket.config import config
11
+ from hyperpocket.futures import FutureStore
12
+
13
+
14
+ class KrakeyKeypairAuthHandler(AuthHandlerInterface):
15
+ name: str = "kraken-keypair"
16
+ description: str = (
17
+ "This handler is used to authenticate users using the Kraken keypair."
18
+ )
19
+ scoped: bool = False
20
+
21
+ _TOKEN_URL: str = urljoin(
22
+ config().public_base_url + "/",
23
+ f"{config().callback_url_rewrite_prefix}/auth/kraken/keypair",
24
+ )
25
+
26
+ @staticmethod
27
+ def provider() -> AuthProvider:
28
+ return AuthProvider.KRAKEN
29
+
30
+ @staticmethod
31
+ def provider_default() -> bool:
32
+ return True
33
+
34
+ @staticmethod
35
+ def recommended_scopes() -> set[str]:
36
+ return set()
37
+
38
+ def prepare(
39
+ self,
40
+ auth_req: AhrefsTokenRequest,
41
+ thread_id: str,
42
+ profile: str,
43
+ future_uid: str,
44
+ *args,
45
+ **kwargs,
46
+ ) -> str:
47
+ redirect_uri = urljoin(
48
+ config().public_base_url + "/",
49
+ f"{config().callback_url_rewrite_prefix}/auth/kraken/keypair/callback",
50
+ )
51
+ url = self._make_auth_url(
52
+ auth_req=auth_req, redirect_uri=redirect_uri, state=future_uid
53
+ )
54
+ FutureStore.create_future(
55
+ future_uid,
56
+ data={
57
+ "redirect_uri": redirect_uri,
58
+ "thread_id": thread_id,
59
+ "profile": profile,
60
+ },
61
+ )
62
+
63
+ return f"User needs to authenticate using the following URL: {url}"
64
+
65
+ async def authenticate(
66
+ self, auth_req: KrakenKeypairRequest, future_uid: str, *args, **kwargs
67
+ ) -> AuthContext:
68
+ future_data = FutureStore.get_future(future_uid)
69
+ kraken_api_keypair = await future_data.future
70
+
71
+ response = KrakenKeypairResponse(
72
+ kraken_api_key=kraken_api_keypair["kraken_api_key"],
73
+ kraken_api_secret=kraken_api_keypair["kraken_api_secret"],
74
+ )
75
+ context = KrakenKeypairAuthContext.from_kraken_keypair_response(response)
76
+
77
+ return context
78
+
79
+ async def refresh(
80
+ self, auth_req: KrakenKeypairRequest, context: AuthContext, *args, **kwargs
81
+ ) -> AuthContext:
82
+ raise Exception("Kraken keypair doesn't support refresh")
83
+
84
+ def _make_auth_url(
85
+ self, auth_req: KrakenKeypairRequest, redirect_uri: str, state: str
86
+ ):
87
+ params = {
88
+ "redirect_uri": redirect_uri,
89
+ "state": state,
90
+ }
91
+ auth_url = f"{self._TOKEN_URL}?{urlencode(params)}"
92
+ return auth_url
93
+
94
+ def make_request(
95
+ self, auth_scopes: Optional[list[str]] = None, **kwargs
96
+ ) -> KrakenKeypairRequest:
97
+ return KrakenKeypairRequest()
@@ -0,0 +1,10 @@
1
+ from hyperpocket.auth.schema import AuthenticateRequest, AuthenticateResponse
2
+
3
+
4
+ class KrakenKeypairRequest(AuthenticateRequest):
5
+ pass
6
+
7
+
8
+ class KrakenKeypairResponse(AuthenticateResponse):
9
+ kraken_api_key: str
10
+ kraken_api_secret: str
@@ -74,6 +74,7 @@ class AuthProvider(Enum):
74
74
  SEMANTIC_SCHOLAR = "semantic_scholar"
75
75
  WEAVIATE = "weaviate"
76
76
  VALYU = "valyu"
77
+ KRAKEN = "kraken"
77
78
 
78
79
  @classmethod
79
80
  def get_auth_provider(cls, auth_provider_name: str) -> "AuthProvider":
@@ -2,7 +2,6 @@ from typing import Optional
2
2
  from urllib.parse import urljoin, urlencode
3
3
 
4
4
  from hyperpocket.auth import AuthProvider
5
- from hyperpocket.auth.ahrefs.token_schema import AhrefsTokenRequest
6
5
  from hyperpocket.auth.context import AuthContext
7
6
  from hyperpocket.auth.handler import AuthHandlerInterface
8
7
  from hyperpocket.auth.valyu.token_context import ValyuTokenAuthContext
@@ -37,7 +36,7 @@ class ValyuTokenAuthHandler(AuthHandlerInterface):
37
36
 
38
37
  def prepare(
39
38
  self,
40
- auth_req: AhrefsTokenRequest,
39
+ auth_req: ValyuTokenRequest,
41
40
  thread_id: str,
42
41
  profile: str,
43
42
  future_uid: str,
hyperpocket/builtin.py CHANGED
@@ -12,7 +12,7 @@ def get_builtin_tools(pocket_auth: PocketAuth) -> List[Tool]:
12
12
  Builtin Tool can access to Pocket Core.
13
13
  """
14
14
 
15
- def __get_current_thread_session_state(thread_id: str = "default") -> str:
15
+ async def __get_current_thread_session_state(thread_id: str = "default") -> str:
16
16
  """
17
17
  This tool retrieves the current session state list for the specified thread.
18
18
 
@@ -31,7 +31,7 @@ def get_builtin_tools(pocket_auth: PocketAuth) -> List[Tool]:
31
31
 
32
32
  This tool ensures transparency about the current session but must respect user-driven intent and should never be called automatically or without a specific user request.
33
33
  """
34
- session_list = pocket_auth.list_session_state(thread_id)
34
+ session_list = await pocket_auth.list_session_state(thread_id)
35
35
  return str(session_list)
36
36
 
37
37
  def __delete_session(
@@ -58,7 +58,7 @@ def get_builtin_tools(pocket_auth: PocketAuth) -> List[Tool]:
58
58
  return str(is_deleted)
59
59
 
60
60
  builtin_tools = [
61
- from_func(func=__get_current_thread_session_state),
61
+ from_func(func=__get_current_thread_session_state, afunc=__get_current_thread_session_state),
62
62
  from_func(func=__delete_session),
63
63
  ]
64
64
 
@@ -2,7 +2,6 @@ import click
2
2
 
3
3
  from hyperpocket.cli.eject import eject
4
4
  from hyperpocket.cli.pull import pull
5
- from hyperpocket.cli.sync import sync
6
5
  from hyperpocket.cli.eject import eject
7
6
  from hyperpocket.cli.auth_token import create_token_auth_template
8
7
  from hyperpocket.cli.auth_oauth2 import create_oauth2_auth_template
@@ -28,7 +27,6 @@ devtool.add_command(build_tool)
28
27
  devtool.add_command(export_tool)
29
28
 
30
29
  cli.add_command(pull)
31
- cli.add_command(sync)
32
30
  cli.add_command(eject)
33
31
 
34
32
  cli()
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  import os
3
+ import pathlib
3
4
  from logging.handlers import RotatingFileHandler
4
5
  from pathlib import Path
5
6
 
@@ -27,9 +28,7 @@ class ColorFormatter(logging.Formatter):
27
28
 
28
29
 
29
30
  def get_logger():
30
- # init log file
31
- package_path = Path(os.path.dirname(hyperpocket.__file__))
32
- log_dir = package_path / ".log"
31
+ log_dir = pathlib.Path(os.getcwd()) / ".log"
33
32
  os.makedirs(log_dir, exist_ok=True)
34
33
  log_file = log_dir / "pocket.log"
35
34
  if not log_file.exists():
@@ -25,7 +25,6 @@ class FutureStore(object):
25
25
  f"the future already exists. the existing future is returned. uid: {uid}"
26
26
  )
27
27
  return future
28
-
29
28
  loop = asyncio.get_running_loop()
30
29
  future = loop.create_future()
31
30
  future_data = FutureData(future=future, data=data)
@@ -42,7 +41,13 @@ class FutureStore(object):
42
41
  if not future_data:
43
42
  raise ValueError(f"Future not found for uid={uid}")
44
43
  if not future_data.future.done():
45
- future_data.future.set_result(value)
44
+ # if the future loop is running, it should be executed in same event loop
45
+ loop = future_data.future.get_loop()
46
+ if loop.is_running():
47
+ loop.call_soon_threadsafe(future_data.future.set_result, value)
48
+ # if the future loop is not running, it can be executed from anywhere.
49
+ else:
50
+ future_data.future.set_result(value)
46
51
 
47
52
  def delete_future(self, uid: str):
48
53
  self.futures.pop(uid, None)
@@ -78,7 +78,7 @@ class PocketAuth(object):
78
78
  handler = self.find_handler_instance(auth_handler_name, auth_provider)
79
79
  return handler.make_request(auth_scopes, **kwargs)
80
80
 
81
- def check(
81
+ async def check(
82
82
  self,
83
83
  auth_req: AuthenticateRequest,
84
84
  auth_handler_name: Optional[str] = None,
@@ -112,12 +112,12 @@ class PocketAuth(object):
112
112
  """
113
113
  handler = self.find_handler_instance(auth_handler_name, auth_provider)
114
114
  session = self.session_storage.get(handler.provider(), thread_id, profile)
115
- auth_state = self.get_session_state(session=session, auth_req=auth_req)
115
+ auth_state = await self.get_session_state(session=session, auth_req=auth_req)
116
116
 
117
117
  return auth_state
118
118
 
119
119
  @staticmethod
120
- def get_session_state(
120
+ async def get_session_state(
121
121
  session: Optional[BaseSessionValue], auth_req: Optional[AuthenticateRequest]
122
122
  ) -> AuthState:
123
123
  if not session:
@@ -125,6 +125,8 @@ class PocketAuth(object):
125
125
 
126
126
  if session.auth_resolve_uid:
127
127
  future_data = FutureStore.get_future(session.auth_resolve_uid)
128
+ # it yields before checking future's state, because the future is being resolved on another thread's event loop.
129
+ await asyncio.sleep(0)
128
130
  if future_data is not None and future_data.future.done():
129
131
  return AuthState.RESOLVED
130
132
 
@@ -140,7 +142,7 @@ class PocketAuth(object):
140
142
 
141
143
  return AuthState.SKIP_AUTH
142
144
 
143
- def prepare(
145
+ async def prepare(
144
146
  self,
145
147
  auth_req: AuthenticateRequest,
146
148
  auth_handler_name: Optional[str] = None,
@@ -166,7 +168,7 @@ class PocketAuth(object):
166
168
  Returns:
167
169
  Optional[str]: authentication URL
168
170
  """
169
- auth_state = self.check(
171
+ auth_state = await self.check(
170
172
  auth_req=auth_req,
171
173
  auth_handler_name=auth_handler_name,
172
174
  auth_provider=auth_provider,
@@ -263,7 +265,7 @@ class PocketAuth(object):
263
265
  Returns:
264
266
  AuthContext: authentication context
265
267
  """
266
- auth_state = self.check(
268
+ auth_state = await self.check(
267
269
  auth_req=auth_req,
268
270
  auth_handler_name=auth_handler_name,
269
271
  auth_provider=auth_provider,
@@ -353,7 +355,7 @@ class PocketAuth(object):
353
355
 
354
356
  return session.auth_context
355
357
 
356
- def list_session_state(
358
+ async def list_session_state(
357
359
  self, thread_id: str, auth_provider: Optional[AuthProvider] = None
358
360
  ):
359
361
  session_list = self.session_storage.get_by_thread_id(
@@ -361,7 +363,7 @@ class PocketAuth(object):
361
363
  )
362
364
  session_state_list = []
363
365
  for session in session_list:
364
- state = self.get_session_state(session=session, auth_req=None)
366
+ state = await self.get_session_state(session=session, auth_req=None)
365
367
 
366
368
  session_state_list.append(
367
369
  {