hyperpocket 0.1.10__py3-none-any.whl → 0.2.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 (92) hide show
  1. hyperpocket/__init__.py +4 -4
  2. hyperpocket/auth/__init__.py +12 -7
  3. hyperpocket/auth/calendly/oauth2_handler.py +24 -17
  4. hyperpocket/auth/calendly/oauth2_schema.py +3 -1
  5. hyperpocket/auth/context.py +2 -1
  6. hyperpocket/auth/github/oauth2_handler.py +13 -8
  7. hyperpocket/auth/github/token_handler.py +27 -21
  8. hyperpocket/auth/google/context.py +1 -3
  9. hyperpocket/auth/google/oauth2_context.py +1 -1
  10. hyperpocket/auth/google/oauth2_handler.py +22 -17
  11. hyperpocket/auth/gumloop/token_context.py +1 -4
  12. hyperpocket/auth/gumloop/token_handler.py +48 -20
  13. hyperpocket/auth/gumloop/token_schema.py +2 -1
  14. hyperpocket/auth/handler.py +21 -6
  15. hyperpocket/auth/linear/token_context.py +2 -5
  16. hyperpocket/auth/linear/token_handler.py +45 -21
  17. hyperpocket/auth/notion/context.py +2 -2
  18. hyperpocket/auth/notion/token_context.py +2 -4
  19. hyperpocket/auth/notion/token_handler.py +45 -21
  20. hyperpocket/auth/notion/token_schema.py +0 -1
  21. hyperpocket/auth/reddit/oauth2_handler.py +9 -10
  22. hyperpocket/auth/reddit/oauth2_schema.py +0 -2
  23. hyperpocket/auth/schema.py +4 -1
  24. hyperpocket/auth/slack/oauth2_context.py +3 -1
  25. hyperpocket/auth/slack/oauth2_handler.py +55 -35
  26. hyperpocket/auth/slack/token_context.py +2 -4
  27. hyperpocket/auth/slack/token_handler.py +42 -19
  28. hyperpocket/builtin.py +4 -2
  29. hyperpocket/cli/__main__.py +4 -2
  30. hyperpocket/cli/auth.py +59 -28
  31. hyperpocket/cli/codegen/auth/auth_context_template.py +3 -2
  32. hyperpocket/cli/codegen/auth/auth_token_context_template.py +3 -2
  33. hyperpocket/cli/codegen/auth/auth_token_handler_template.py +6 -5
  34. hyperpocket/cli/codegen/auth/auth_token_schema_template.py +3 -2
  35. hyperpocket/cli/codegen/auth/server_auth_template.py +3 -2
  36. hyperpocket/cli/pull.py +5 -5
  37. hyperpocket/config/__init__.py +3 -8
  38. hyperpocket/config/auth.py +3 -1
  39. hyperpocket/config/logger.py +20 -15
  40. hyperpocket/config/session.py +4 -2
  41. hyperpocket/config/settings.py +19 -2
  42. hyperpocket/futures/__init__.py +1 -1
  43. hyperpocket/futures/futurestore.py +3 -2
  44. hyperpocket/pocket_auth.py +171 -84
  45. hyperpocket/pocket_core.py +51 -33
  46. hyperpocket/pocket_main.py +122 -93
  47. hyperpocket/prompts.py +2 -2
  48. hyperpocket/repository/__init__.py +1 -1
  49. hyperpocket/repository/lock.py +47 -33
  50. hyperpocket/repository/lockfile.py +2 -2
  51. hyperpocket/repository/repository.py +1 -1
  52. hyperpocket/server/__init__.py +1 -1
  53. hyperpocket/server/auth/github.py +2 -1
  54. hyperpocket/server/auth/linear.py +1 -3
  55. hyperpocket/server/auth/notion.py +2 -5
  56. hyperpocket/server/auth/slack.py +1 -3
  57. hyperpocket/server/auth/token.py +17 -11
  58. hyperpocket/server/proxy.py +29 -13
  59. hyperpocket/server/server.py +75 -31
  60. hyperpocket/server/tool/dto/script.py +15 -10
  61. hyperpocket/server/tool/wasm.py +14 -11
  62. hyperpocket/session/__init__.py +6 -2
  63. hyperpocket/session/in_memory.py +44 -24
  64. hyperpocket/session/interface.py +42 -24
  65. hyperpocket/session/redis.py +48 -31
  66. hyperpocket/tool/__init__.py +10 -10
  67. hyperpocket/tool/function/__init__.py +1 -5
  68. hyperpocket/tool/function/annotation.py +11 -9
  69. hyperpocket/tool/function/tool.py +37 -27
  70. hyperpocket/tool/tool.py +59 -36
  71. hyperpocket/tool/wasm/__init__.py +1 -1
  72. hyperpocket/tool/wasm/browser.py +15 -10
  73. hyperpocket/tool/wasm/invoker.py +16 -16
  74. hyperpocket/tool/wasm/script.py +27 -14
  75. hyperpocket/tool/wasm/templates/__init__.py +22 -15
  76. hyperpocket/tool/wasm/templates/node.py +2 -2
  77. hyperpocket/tool/wasm/templates/python.py +2 -2
  78. hyperpocket/tool/wasm/tool.py +27 -14
  79. hyperpocket/tool_like.py +3 -3
  80. hyperpocket/util/__init__.py +1 -1
  81. hyperpocket/util/extract_func_param_desc_from_docstring.py +23 -7
  82. hyperpocket/util/find_all_leaf_class_in_package.py +4 -3
  83. hyperpocket/util/find_all_subclass_in_package.py +4 -2
  84. hyperpocket/util/flatten_json_schema.py +10 -6
  85. hyperpocket/util/function_to_model.py +33 -12
  86. hyperpocket/util/get_objects_from_subpackage.py +1 -1
  87. hyperpocket/util/json_schema_to_model.py +14 -5
  88. {hyperpocket-0.1.10.dist-info → hyperpocket-0.2.0.dist-info}/METADATA +11 -5
  89. hyperpocket-0.2.0.dist-info/RECORD +137 -0
  90. hyperpocket-0.1.10.dist-info/RECORD +0 -137
  91. {hyperpocket-0.1.10.dist-info → hyperpocket-0.2.0.dist-info}/WHEEL +0 -0
  92. {hyperpocket-0.1.10.dist-info → hyperpocket-0.2.0.dist-info}/entry_points.txt +0 -0
@@ -14,29 +14,38 @@ class ScriptRuntime(enum.Enum):
14
14
  Python = "python"
15
15
  Wasm = "wasm"
16
16
 
17
+
17
18
  _RuntimePackageFiles = {
18
19
  ScriptRuntime.Node: ["dist/index.js"],
19
20
  ScriptRuntime.Python: ["main.py", "requirements.txt"],
20
21
  ScriptRuntime.Wasm: ["dist/index.wasm"],
21
22
  }
22
23
 
24
+
23
25
  class ScriptFileNodeContent(BaseModel):
24
26
  contents: str
25
27
 
28
+
26
29
  class ScriptFileNode(BaseModel):
27
- directory: Optional[dict[str, 'ScriptFileNode']] = None
30
+ directory: Optional[dict[str, "ScriptFileNode"]] = None
28
31
  file: Optional[ScriptFileNodeContent] = None
29
-
32
+
30
33
  @classmethod
31
- def create_file_tree(cls, path: str, contents: str) -> dict[str, 'ScriptFileNode']:
34
+ def create_file_tree(cls, path: str, contents: str) -> dict[str, "ScriptFileNode"]:
32
35
  path_split = path.split("/")
33
36
  if len(path_split) == 1:
34
- return {path_split[0]: ScriptFileNode(file=ScriptFileNodeContent(contents=contents))}
35
- node = cls.create_file_tree('/'.join(path_split[1:]), contents)
37
+ return {
38
+ path_split[0]: ScriptFileNode(
39
+ file=ScriptFileNodeContent(contents=contents)
40
+ )
41
+ }
42
+ node = cls.create_file_tree("/".join(path_split[1:]), contents)
36
43
  return {path_split[0]: ScriptFileNode(directory=node)}
37
-
44
+
38
45
  @staticmethod
39
- def merge(a: dict[str, 'ScriptFileNode'], b: [str, 'ScriptFileNode']) -> dict[str, 'ScriptFileNode']:
46
+ def merge(
47
+ a: dict[str, "ScriptFileNode"], b: [str, "ScriptFileNode"]
48
+ ) -> dict[str, "ScriptFileNode"]:
40
49
  for k, v in b.items():
41
50
  if k in a:
42
51
  if a[k].directory and v.directory:
@@ -53,19 +62,21 @@ class Script(BaseModel):
53
62
  tool_path: str
54
63
  rendered_html: str
55
64
  runtime: ScriptRuntime
56
-
65
+
57
66
  def load_file_tree(self) -> dict[str, ScriptFileNode]:
58
67
  relpaths = _RuntimePackageFiles[self.runtime]
59
68
  file_tree = dict()
60
69
  for p in relpaths:
61
70
  filepath = pathlib.Path(self.tool_path) / p
62
71
  with filepath.open("r") as f:
63
- contents = f.read().encode('utf-8')
72
+ contents = f.read().encode("utf-8")
64
73
  encoded_bytes = base64.b64encode(contents)
65
74
  encoded_str = encoded_bytes.decode()
66
- file_tree = ScriptFileNode.merge(file_tree, ScriptFileNode.create_file_tree(p, encoded_str))
75
+ file_tree = ScriptFileNode.merge(
76
+ file_tree, ScriptFileNode.create_file_tree(p, encoded_str)
77
+ )
67
78
  return file_tree
68
-
79
+
69
80
  @property
70
81
  def package_name(self) -> Optional[str]:
71
82
  if self.runtime != ScriptRuntime.Python:
@@ -78,7 +89,7 @@ class Script(BaseModel):
78
89
  if not name:
79
90
  raise ValueError("Could not find package name")
80
91
  return name.replace("-", "_")
81
-
92
+
82
93
  @property
83
94
  def entrypoint(self) -> str:
84
95
  pocket_logger.info(self.tool_path)
@@ -99,10 +110,11 @@ class Script(BaseModel):
99
110
  if not wheel_path.exists():
100
111
  raise ValueError(f"Wheel file {wheel_path} does not exist")
101
112
  return wheel_name
102
-
113
+
103
114
  def dist_file_path(self, file_name: str) -> str:
104
115
  return str(pathlib.Path(self.tool_path) / "dist" / file_name)
105
116
 
117
+
106
118
  class _ScriptStore(object):
107
119
  scripts: dict[str, Script] = {}
108
120
 
@@ -113,9 +125,10 @@ class _ScriptStore(object):
113
125
  if script.id in self.scripts:
114
126
  raise ValueError("Script id already exists")
115
127
  self.scripts[script.id] = script
116
-
128
+
117
129
  def get_script(self, script_id: str) -> Script:
118
130
  # ValueError exception is intentional
119
131
  return self.scripts[script_id]
120
132
 
133
+
121
134
  ScriptStore = _ScriptStore()
@@ -1,28 +1,35 @@
1
1
  import base64
2
2
  import json
3
3
 
4
- from jinja2 import Environment, DictLoader
4
+ from jinja2 import DictLoader, Environment
5
5
 
6
6
  from hyperpocket.tool.wasm.templates.node import node_template
7
7
  from hyperpocket.tool.wasm.templates.python import python_template
8
8
 
9
9
  TemplateEnvironments = Environment(
10
- loader=DictLoader({
11
- 'python.html': python_template,
12
- 'node.html': node_template,
13
- }),
14
- autoescape=False
10
+ loader=DictLoader(
11
+ {
12
+ "python.html": python_template,
13
+ "node.html": node_template,
14
+ }
15
+ ),
16
+ autoescape=False,
15
17
  )
16
18
 
17
19
 
18
- def render(language: str, script_id: str, env: dict[str, str], body: str, **kwargs) -> str:
20
+ def render(
21
+ language: str, script_id: str, env: dict[str, str], body: str, **kwargs
22
+ ) -> str:
19
23
  env_json = json.dumps(env)
20
- template = TemplateEnvironments.get_template(f'{language.lower()}.html')
21
- body_bytes = body.encode('utf-8')
24
+ template = TemplateEnvironments.get_template(f"{language.lower()}.html")
25
+ body_bytes = body.encode("utf-8")
22
26
  body_b64_bytes = base64.b64encode(body_bytes)
23
- body_b64 = body_b64_bytes.decode('ascii')
24
- return template.render(**{
25
- 'SCRIPT_ID': script_id,
26
- 'ENV_JSON': env_json,
27
- 'BODY_JSON_B64': body_b64,
28
- } | kwargs)
27
+ body_b64 = body_b64_bytes.decode("ascii")
28
+ return template.render(
29
+ **{
30
+ "SCRIPT_ID": script_id,
31
+ "ENV_JSON": env_json,
32
+ "BODY_JSON_B64": body_b64,
33
+ }
34
+ | kwargs
35
+ )
@@ -1,4 +1,4 @@
1
- node_template = '''
1
+ node_template = """
2
2
  <!DOCTYPE html>
3
3
  <html lang="en">
4
4
  <head>
@@ -84,4 +84,4 @@ node_template = '''
84
84
  </script>
85
85
  </body>
86
86
  </html>
87
- '''
87
+ """
@@ -1,4 +1,4 @@
1
- python_template = '''
1
+ python_template = """
2
2
  <!DOCTYPE html>
3
3
  <html lang="en">
4
4
  <head>
@@ -90,4 +90,4 @@ ${packageName}.main()
90
90
  </script>
91
91
  </body>
92
92
  </html>
93
- '''
93
+ """
@@ -1,11 +1,11 @@
1
- import os
2
1
  import json
3
2
  import pathlib
4
3
  from typing import Any, Optional
5
4
 
6
5
  import toml
6
+
7
7
  from hyperpocket.auth import AuthProvider
8
- from hyperpocket.config import settings, pocket_logger
8
+ from hyperpocket.config import pocket_logger
9
9
  from hyperpocket.repository import Lock, Lockfile
10
10
  from hyperpocket.repository.lock import GitLock, LocalLock
11
11
  from hyperpocket.tool import Tool, ToolRequest
@@ -26,15 +26,24 @@ class WasmToolRequest(ToolRequest):
26
26
  def __str__(self):
27
27
  return f"ToolRequest(lock={self.lock}, rel_path={self.rel_path})"
28
28
 
29
- def from_local(path: str, tool_vars: Optional[dict[str, str]] = None) -> WasmToolRequest:
29
+
30
+ def from_local(
31
+ path: str, tool_vars: Optional[dict[str, str]] = None
32
+ ) -> WasmToolRequest:
30
33
  if tool_vars is None:
31
34
  tool_vars = dict()
32
35
  return WasmToolRequest(LocalLock(path), "", tool_vars)
33
36
 
34
- def from_git(repository: str, ref: str, rel_path: str, tool_vars: Optional[dict[str, str]] = None) -> WasmToolRequest:
37
+
38
+ def from_git(
39
+ repository: str, ref: str, rel_path: str, tool_vars: Optional[dict[str, str]] = None
40
+ ) -> WasmToolRequest:
35
41
  if not tool_vars:
36
42
  tool_vars = dict()
37
- return WasmToolRequest(GitLock(repository_url=repository, git_ref=ref), rel_path, tool_vars)
43
+ return WasmToolRequest(
44
+ GitLock(repository_url=repository, git_ref=ref), rel_path, tool_vars
45
+ )
46
+
38
47
 
39
48
  class WasmTool(Tool):
40
49
  """
@@ -55,7 +64,9 @@ class WasmTool(Tool):
55
64
  return self._invoker
56
65
 
57
66
  @classmethod
58
- def from_tool_request(cls, tool_req: WasmToolRequest, lockfile: Lockfile = None, **kwargs) -> 'WasmTool':
67
+ def from_tool_request(
68
+ cls, tool_req: WasmToolRequest, lockfile: Lockfile = None, **kwargs
69
+ ) -> "WasmTool":
59
70
  if not lockfile:
60
71
  raise ValueError("lockfile is required")
61
72
  tool_req.lock = lockfile.get_lock(tool_req.lock.key())
@@ -70,20 +81,22 @@ class WasmTool(Tool):
70
81
  with schema_path.open("r") as f:
71
82
  json_schema = json.load(f)
72
83
  except Exception as e:
73
- pocket_logger.warning(f"{toolpkg_path} failed to load json schema. error : {e}")
84
+ pocket_logger.warning(
85
+ f"{toolpkg_path} failed to load json schema. error : {e}"
86
+ )
74
87
  json_schema = None
75
88
 
76
89
  default_tool_vars = dict()
77
90
  try:
78
91
  with config_path.open("r") as f:
79
92
  config = toml.load(f)
80
- name = config.get('name')
81
- description = config.get('description')
82
- if language := config.get('language'):
93
+ name = config.get("name")
94
+ description = config.get("description")
95
+ if language := config.get("language"):
83
96
  lang = language.lower()
84
- if lang == 'python':
97
+ if lang == "python":
85
98
  runtime = ScriptRuntime.Python
86
- elif lang == 'node':
99
+ elif lang == "node":
87
100
  runtime = ScriptRuntime.Node
88
101
  else:
89
102
  raise ValueError(f"The language `{lang}` is not supported.")
@@ -99,7 +112,7 @@ class WasmTool(Tool):
99
112
  readme = f.read()
100
113
  else:
101
114
  readme = None
102
-
115
+
103
116
  return cls(
104
117
  name=name,
105
118
  description=description,
@@ -127,7 +140,7 @@ class WasmTool(Tool):
127
140
  auth_handler=auth_handler,
128
141
  scopes=scopes,
129
142
  )
130
-
143
+
131
144
  def template_arguments(self) -> dict[str, str]:
132
145
  return {}
133
146
 
hyperpocket/tool_like.py CHANGED
@@ -1,5 +1,5 @@
1
- from typing import Union, Callable
1
+ from typing import Callable, Union
2
2
 
3
- from hyperpocket.tool import ToolRequest, Tool
3
+ from hyperpocket.tool import Tool, ToolRequest
4
4
 
5
- ToolLike = Union[Tool, str, Callable, ToolRequest]
5
+ ToolLike = Union[Tool, str, Callable, ToolRequest]
@@ -1 +1 @@
1
- __all__ = ['json_schema_to_model']
1
+ __all__ = ["json_schema_to_model"]
@@ -25,13 +25,17 @@ def extract_param_docstring_mapping(func) -> dict[str, str]:
25
25
 
26
26
  param_mapping = extract_param_desc_by_google_stype_docstring(docstring, func_params)
27
27
  if param_mapping:
28
- pocket_logger.debug(f"success extract docstring of {func.__name__} by google style!")
28
+ pocket_logger.debug(
29
+ f"success extract docstring of {func.__name__} by google style!"
30
+ )
29
31
  return param_mapping
30
32
  pocket_logger.debug(f"not found param desc of {func.__name__} by google style..")
31
33
 
32
34
  param_mapping = extract_param_desc_by_other_styles(docstring, func_params)
33
35
  if param_mapping:
34
- pocket_logger.debug(f"success extract docstring of {func.__name__} by other style!")
36
+ pocket_logger.debug(
37
+ f"success extract docstring of {func.__name__} by other style!"
38
+ )
35
39
  return param_mapping
36
40
  pocket_logger.debug(f"not found param desc of {func.__name__} by other styles..")
37
41
 
@@ -50,7 +54,9 @@ def extract_param_docstring_mapping(func) -> dict[str, str]:
50
54
  param_descriptions.append((cleaned_param_name, description))
51
55
 
52
56
  # Ensure no duplicates and match with function parameters
53
- param_mapping = {param: desc for param, desc in param_descriptions if param in func_params}
57
+ param_mapping = {
58
+ param: desc for param, desc in param_descriptions if param in func_params
59
+ }
54
60
  pocket_logger.debug(f"final param_mapping of {func.__name__} : {param_mapping}")
55
61
 
56
62
  return param_mapping
@@ -74,11 +80,15 @@ def extract_param_desc_by_other_styles(docstring, func_params) -> dict[str, str]
74
80
  cleaned_param = clean_bracket_content(param)
75
81
  param_descriptions.append((cleaned_param, desc.strip()))
76
82
  # Ensure no duplicates and match with function parameters
77
- param_mapping = {param: desc for param, desc in param_descriptions if param in func_params}
83
+ param_mapping = {
84
+ param: desc for param, desc in param_descriptions if param in func_params
85
+ }
78
86
  return param_mapping
79
87
 
80
88
 
81
- def extract_param_desc_by_google_stype_docstring(docstring, func_params) -> dict[str, str]:
89
+ def extract_param_desc_by_google_stype_docstring(
90
+ docstring, func_params
91
+ ) -> dict[str, str]:
82
92
  # Regex pattern to extract parameter descriptions in Google style
83
93
  param_pattern = r"Args:\n(.*?)(?=\n\S|$)" # Matches the Args: section
84
94
  match = re.search(param_pattern, docstring, re.DOTALL)
@@ -90,11 +100,17 @@ def extract_param_desc_by_google_stype_docstring(docstring, func_params) -> dict
90
100
  param_descriptions = {}
91
101
  for line in param_lines:
92
102
  # Match parameter line with "name (type): description"
93
- param_match = re.match(r"^[^a-zA-Z_]*([a-zA-Z_]\w*)\s*[\(\[]\s*(.*?)\s*[\)\]]\s*:\s*(.*)", line)
103
+ param_match = re.match(
104
+ r"^[^a-zA-Z_]*([a-zA-Z_]\w*)\s*[\(\[]\s*(.*?)\s*[\)\]]\s*:\s*(.*)", line
105
+ )
94
106
  if param_match:
95
107
  param, _, desc = param_match.groups()
96
108
  cleaned_param = clean_bracket_content(param)
97
109
  param_descriptions[cleaned_param] = desc.strip()
98
110
  # Match parameters to descriptions
99
- param_mapping = {param: desc for param, desc in param_descriptions.items() if param in func_params}
111
+ param_mapping = {
112
+ param: desc
113
+ for param, desc in param_descriptions.items()
114
+ if param in func_params
115
+ }
100
116
  return param_mapping
@@ -1,12 +1,13 @@
1
- from typing import TypeVar, Type, List
2
-
1
+ from typing import List, Type, TypeVar
3
2
 
4
3
  from hyperpocket.util.find_all_subclass_in_package import find_all_subclass_in_package
5
4
 
6
5
  T = TypeVar("T")
7
6
 
8
7
 
9
- def find_all_leaf_class_in_package(package_name: str, interface_type: Type[T]) -> List[T]:
8
+ def find_all_leaf_class_in_package(
9
+ package_name: str, interface_type: Type[T]
10
+ ) -> List[T]:
10
11
  parent_class_set = set()
11
12
  subclasses = find_all_subclass_in_package(package_name, interface_type)
12
13
 
@@ -1,7 +1,7 @@
1
1
  import importlib
2
2
  import inspect
3
3
  import pkgutil
4
- from typing import TypeVar, Type, List
4
+ from typing import List, Type, TypeVar
5
5
 
6
6
  from hyperpocket.config import pocket_logger
7
7
 
@@ -12,7 +12,9 @@ def find_all_subclass_in_package(package_name: str, interface_type: Type[T]) ->
12
12
  subclasses = set()
13
13
  package = importlib.import_module(package_name)
14
14
 
15
- for _, module_name, is_pkg in pkgutil.walk_packages(package.__path__, package.__name__ + "."):
15
+ for _, module_name, is_pkg in pkgutil.walk_packages(
16
+ package.__path__, package.__name__ + "."
17
+ ):
16
18
  try:
17
19
  if "tests" in module_name or is_pkg:
18
20
  continue
@@ -6,7 +6,7 @@ def flatten_json_schema(schema: dict):
6
6
  Flatten JSON Schema by resolving all $refs using definitions in $defs
7
7
  and convert to a fully nested schema.
8
8
  """
9
- definitions = schema.get('$defs', {})
9
+ definitions = schema.get("$defs", {})
10
10
  schema_copy = copy.deepcopy(schema)
11
11
 
12
12
  # Resolve references within $defs first
@@ -15,7 +15,7 @@ def flatten_json_schema(schema: dict):
15
15
  resolved_definitions[key] = resolve_refs(value, definitions)
16
16
 
17
17
  # Resolve references in the main schema
18
- schema_copy.pop('$defs', None) # Remove $defs
18
+ schema_copy.pop("$defs", None) # Remove $defs
19
19
  return resolve_refs(schema_copy, resolved_definitions)
20
20
 
21
21
 
@@ -28,12 +28,16 @@ def resolve_refs(schema, definitions):
28
28
  resolved_schema = {}
29
29
  for key, value in schema.items():
30
30
  # If $ref exists, resolve the reference
31
- if key == '$ref':
32
- ref_path = schema['$ref']
33
- ref_name = ref_path.split('/')[-1] # Extract the reference name from $defs/Req -> Req
31
+ if key == "$ref":
32
+ ref_path = schema["$ref"]
33
+ ref_name = ref_path.split("/")[
34
+ -1
35
+ ] # Extract the reference name from $defs/Req -> Req
34
36
  resolved = definitions.get(ref_name)
35
37
  if resolved:
36
- resolved_schema |= resolve_refs(copy.deepcopy(resolved), definitions)
38
+ resolved_schema |= resolve_refs(
39
+ copy.deepcopy(resolved), definitions
40
+ )
37
41
  else:
38
42
  resolved_schema[key] = resolve_refs(value, definitions)
39
43
  return resolved_schema
@@ -1,25 +1,29 @@
1
1
  import inspect
2
- from inspect import signature, Parameter
3
- from typing import Any, Dict, Type, Tuple
2
+ from inspect import Parameter, signature
3
+ from typing import Any, Dict, Tuple, Type
4
4
 
5
5
  from pydantic import BaseModel, create_model
6
6
  from pydantic.fields import FieldInfo
7
7
 
8
8
  from hyperpocket.config import pocket_logger
9
- from hyperpocket.util.extract_func_param_desc_from_docstring import extract_param_docstring_mapping
9
+ from hyperpocket.util.extract_func_param_desc_from_docstring import (
10
+ extract_param_docstring_mapping,
11
+ )
10
12
 
11
13
 
12
14
  def function_to_model(func: callable) -> Type[BaseModel]:
13
15
  docstring = inspect.getdoc(func)
14
16
  if docstring is None:
15
- pocket_logger.info("not found docstring. use function name as description instead.")
17
+ pocket_logger.info(
18
+ "not found docstring. use function name as description instead."
19
+ )
16
20
  docstring = func.__name__
17
21
  fields: Dict[str, Tuple[Type, Any]] = {}
18
22
  sig = signature(func)
19
23
  param_desc_map = extract_param_docstring_mapping(func)
20
24
 
21
25
  for param_name, param in sig.parameters.items():
22
- if param_name in ('self', 'cls'):
26
+ if param_name in ("self", "cls"):
23
27
  continue
24
28
 
25
29
  if param.kind == Parameter.VAR_POSITIONAL:
@@ -31,19 +35,36 @@ def function_to_model(func: callable) -> Type[BaseModel]:
31
35
  continue
32
36
 
33
37
  if param.annotation is Parameter.empty:
34
- raise Exception(f"Should all arguments be annotated but {param_name} is not annotated")
38
+ raise Exception(
39
+ f"Should all arguments be annotated but {param_name} is not annotated"
40
+ )
35
41
 
36
- if param.annotation.__module__ == "typing" and param.annotation.__name__ == "Optional":
42
+ if (
43
+ param.annotation.__module__ == "typing"
44
+ and param.annotation.__name__ == "Optional"
45
+ ):
37
46
  fields[param_name] = (
38
- param.annotation.__args__[0], FieldInfo(default=param.default, description=param_desc_map.get(param_name, "")))
39
- elif param.annotation.__module__ != "builtins" and not issubclass(param.annotation, BaseModel):
47
+ param.annotation.__args__[0],
48
+ FieldInfo(
49
+ default=param.default,
50
+ description=param_desc_map.get(param_name, ""),
51
+ ),
52
+ )
53
+ elif param.annotation.__module__ != "builtins" and not issubclass(
54
+ param.annotation, BaseModel
55
+ ):
40
56
  raise Exception(
41
- f"currently only support builtin types and pydantic BaseModel but {param_name} is not builtin type")
57
+ f"currently only support builtin types and pydantic BaseModel but {param_name} is not builtin type"
58
+ )
42
59
 
43
60
  default = param.default if param.default is not Parameter.empty else ...
44
61
 
45
62
  fields[param_name] = (
46
- param.annotation, FieldInfo(default=default, description=param_desc_map.get(param_name, "")))
63
+ param.annotation,
64
+ FieldInfo(default=default, description=param_desc_map.get(param_name, "")),
65
+ )
47
66
 
48
- model = create_model(f"{func.__name__.capitalize()}Model", **fields, __doc__=docstring)
67
+ model = create_model(
68
+ f"{func.__name__.capitalize()}Model", **fields, __doc__=docstring
69
+ )
49
70
  return model
@@ -1,6 +1,6 @@
1
1
  import importlib
2
2
  import pkgutil
3
- from typing import Type, Optional
3
+ from typing import Optional, Type
4
4
 
5
5
 
6
6
  def get_objects_from_subpackage(package_name, interface_type: Optional[Type] = None):
@@ -1,10 +1,12 @@
1
1
  from typing import Type, Union
2
2
 
3
- from pydantic import BaseModel, create_model, Field
3
+ from pydantic import BaseModel, Field, create_model
4
4
 
5
5
 
6
6
  # Convert JSON Schema to a Pydantic model
7
- def json_schema_to_model(schema: dict, model_name: str = "DynamicModel") -> Type[BaseModel]:
7
+ def json_schema_to_model(
8
+ schema: dict, model_name: str = "DynamicModel"
9
+ ) -> Type[BaseModel]:
8
10
  """Recursively create a Pydantic model from a JSON Schema."""
9
11
  fields = {}
10
12
  config_extra = "forbid"
@@ -16,12 +18,16 @@ def json_schema_to_model(schema: dict, model_name: str = "DynamicModel") -> Type
16
18
  if "anyOf" in property_schema:
17
19
  types = []
18
20
  for item in property_schema["anyOf"]:
19
- sub_type = _convert_to_python_type(item["type"], model_name, property_schema)
21
+ sub_type = _convert_to_python_type(
22
+ item["type"], model_name, property_schema
23
+ )
20
24
  types.append(sub_type)
21
25
 
22
26
  field_type = Union[tuple(types)]
23
27
  elif "type" in property_schema:
24
- field_type = _convert_to_python_type(property_schema["type"], model_name, property_schema)
28
+ field_type = _convert_to_python_type(
29
+ property_schema["type"], model_name, property_schema
30
+ )
25
31
  else:
26
32
  raise RuntimeError("have no type in json schema.")
27
33
 
@@ -35,7 +41,10 @@ def json_schema_to_model(schema: dict, model_name: str = "DynamicModel") -> Type
35
41
  if required:
36
42
  fields[property_name] = (field_type, Field(description=field_description))
37
43
  else:
38
- fields[property_name] = (field_type, Field(default=field_default, description=field_description))
44
+ fields[property_name] = (
45
+ field_type,
46
+ Field(default=field_default, description=field_description),
47
+ )
39
48
 
40
49
  # Handle additionalProperties
41
50
  if "additionalProperties" in schema:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperpocket
3
- Version: 0.1.10
3
+ Version: 0.2.0
4
4
  Summary: Building AI agent with hyperpocket tool in a flash
5
5
  Project-URL: Homepage, https://vessl-ai.github.io/hyperpocket
6
6
  Project-URL: Repository, https://github.com/vessl-ai/hyperpocket
@@ -23,13 +23,14 @@ Requires-Dist: toml>=0.10.2
23
23
  Requires-Dist: uvicorn>=0.32.1
24
24
  Description-Content-Type: text/markdown
25
25
 
26
+ <p align="center">
27
+ <img src="../../logo.png" alt="hyperpocket" width="570"/>
28
+ </p>
29
+
26
30
  # Hyperpocket 👛
27
31
 
28
32
  Hyperpocket is where tools belong. Power your agent up with a pocket of tools. 👛
29
33
 
30
- <figure>
31
- <img src="../../logo.png" alt="hyperpocket" width="200"/>
32
- </figure>
33
34
 
34
35
  ## Introduction
35
36
 
@@ -160,13 +161,15 @@ graph_builder.compile()
160
161
  ```
161
162
 
162
163
  ```python
164
+ import os
165
+
163
166
  from llama_index.core.agent import FunctionCallingAgent
164
167
  from llama_index.llms.openai import OpenAI
165
168
 
166
169
  from hyperpocket.config import secret
167
170
  from hyperpocket_llamaindex import PocketLlamaindex
168
171
 
169
- llm = OpenAI(api_key=secret["OPENAI_API_KEY"])
172
+ llm = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
170
173
  pocket = PocketLlamaindex(
171
174
  tools=[
172
175
  "https://github.com/vessl-ai/hyperpocket/tree/main/tools/slack/get-message",
@@ -318,3 +321,6 @@ scopes = []
318
321
  )
319
322
  def my_function(**kwargs):
320
323
  ```
324
+
325
+ ## Special thanks
326
+ - [tott](https://x.com/tott____) for drawing the cute possum in a pocket.