hyperpocket 0.1.10__py3-none-any.whl → 0.2.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
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.