hyperpocket 0.3.7__py3-none-any.whl → 0.4.1__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (96) hide show
  1. hyperpocket/auth/activeloop/token_handler.py +4 -0
  2. hyperpocket/auth/adobe/token_handler.py +4 -0
  3. hyperpocket/auth/affinity/token_handler.py +4 -0
  4. hyperpocket/auth/agentql/token_handler.py +4 -0
  5. hyperpocket/auth/ahrefs/token_handler.py +4 -0
  6. hyperpocket/auth/airtable/token_handler.py +4 -0
  7. hyperpocket/auth/alchemy/token_handler.py +4 -0
  8. hyperpocket/auth/altoviz/token_handler.py +4 -0
  9. hyperpocket/auth/bamboohr/token_handler.py +4 -0
  10. hyperpocket/auth/bitwarden/token_handler.py +4 -0
  11. hyperpocket/auth/brevo/README.md +1 -1
  12. hyperpocket/auth/brevo/token_handler.py +4 -0
  13. hyperpocket/auth/brex/token_handler.py +4 -0
  14. hyperpocket/auth/cal/token_handler.py +4 -0
  15. hyperpocket/auth/canvas/token_handler.py +4 -0
  16. hyperpocket/auth/clickup/token_handler.py +4 -0
  17. hyperpocket/auth/cloudflare/token_handler.py +4 -0
  18. hyperpocket/auth/dailybot/token_handler.py +4 -0
  19. hyperpocket/auth/datadog/token_handler.py +4 -0
  20. hyperpocket/auth/discordbot/README.md +0 -1
  21. hyperpocket/auth/discordbot/token_handler.py +4 -0
  22. hyperpocket/auth/elevenlabs/token_handler.py +4 -0
  23. hyperpocket/auth/exa/token_handler.py +4 -0
  24. hyperpocket/auth/facebook/oauth2_handler.py +4 -0
  25. hyperpocket/auth/finage/token_handler.py +4 -0
  26. hyperpocket/auth/happyrobot/token_handler.py +4 -0
  27. hyperpocket/auth/heygen/token_handler.py +4 -0
  28. hyperpocket/auth/klaviyo/token_handler.py +4 -0
  29. hyperpocket/auth/lever/token_handler.py +4 -0
  30. hyperpocket/auth/lever_sandbox/token_handler.py +4 -0
  31. hyperpocket/auth/listennotes/token_handler.py +4 -0
  32. hyperpocket/auth/mem0/token_handler.py +4 -0
  33. hyperpocket/auth/microsoft_clarity/token_handler.py +4 -0
  34. hyperpocket/auth/neon/token_handler.py +4 -0
  35. hyperpocket/auth/ngrok/token_handler.py +4 -0
  36. hyperpocket/auth/oncehub/token_handler.py +4 -0
  37. hyperpocket/auth/pagerduty/token_handler.py +4 -0
  38. hyperpocket/auth/pandadoc/token_handler.py +4 -0
  39. hyperpocket/auth/pipedrive/token_handler.py +4 -0
  40. hyperpocket/auth/posthog/token_handler.py +4 -0
  41. hyperpocket/auth/provider.py +1 -0
  42. hyperpocket/auth/ravenseotools/token_handler.py +4 -0
  43. hyperpocket/auth/semantic_scholar/token_handler.py +4 -0
  44. hyperpocket/auth/sendgrid/token_handler.py +4 -0
  45. hyperpocket/auth/stripe/token_handler.py +4 -0
  46. hyperpocket/auth/supabase/token_handler.py +4 -0
  47. hyperpocket/auth/tavily/token_handler.py +4 -0
  48. hyperpocket/auth/timekit/token_handler.py +4 -0
  49. hyperpocket/auth/trello/token_handler.py +4 -0
  50. hyperpocket/auth/wandb/token_handler.py +4 -0
  51. hyperpocket/auth/weaviate/context.py +12 -0
  52. hyperpocket/auth/weaviate/token_context.py +11 -0
  53. hyperpocket/auth/weaviate/token_handler.py +68 -0
  54. hyperpocket/auth/weaviate/token_schema.py +7 -0
  55. hyperpocket/auth/workiom/token_handler.py +4 -0
  56. hyperpocket/auth/zinc/token_handler.py +4 -0
  57. hyperpocket/cli/eject.py +2 -7
  58. hyperpocket/cli/pull.py +2 -7
  59. hyperpocket/config/settings.py +2 -1
  60. hyperpocket/pocket_core.py +41 -68
  61. hyperpocket/pocket_main.py +37 -16
  62. hyperpocket/repository/__init__.py +3 -4
  63. hyperpocket/repository/repository.py +6 -41
  64. hyperpocket/repository/tool_reference.py +28 -0
  65. hyperpocket/server/auth/weaviate.py +27 -0
  66. hyperpocket/server/server.py +127 -61
  67. hyperpocket/session/in_memory.py +13 -3
  68. hyperpocket/tool/__init__.py +0 -3
  69. hyperpocket/tool/dock/__init__.py +3 -0
  70. hyperpocket/tool/dock/dock.py +34 -0
  71. hyperpocket/tool/function/__init__.py +1 -1
  72. hyperpocket/tool/function/tool.py +62 -32
  73. hyperpocket/tool/tool.py +1 -9
  74. hyperpocket/tool_like.py +2 -1
  75. hyperpocket/util/generate_slug.py +4 -0
  76. hyperpocket/util/json_schema_to_model.py +5 -1
  77. {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.1.dist-info}/METADATA +4 -1
  78. {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.1.dist-info}/RECORD +81 -87
  79. hyperpocket/cli/sync.py +0 -17
  80. hyperpocket/repository/lock.py +0 -240
  81. hyperpocket/repository/lockfile.py +0 -62
  82. hyperpocket/server/tool/__init__.py +0 -10
  83. hyperpocket/server/tool/dto/script.py +0 -33
  84. hyperpocket/server/tool/wasm.py +0 -46
  85. hyperpocket/tool/wasm/README.md +0 -166
  86. hyperpocket/tool/wasm/__init__.py +0 -3
  87. hyperpocket/tool/wasm/browser.py +0 -63
  88. hyperpocket/tool/wasm/invoker.py +0 -41
  89. hyperpocket/tool/wasm/script.py +0 -134
  90. hyperpocket/tool/wasm/templates/__init__.py +0 -35
  91. hyperpocket/tool/wasm/templates/node.py +0 -87
  92. hyperpocket/tool/wasm/templates/python.py +0 -93
  93. hyperpocket/tool/wasm/tool.py +0 -163
  94. /hyperpocket/{server/tool/dto → auth/weaviate}/__init__.py +0 -0
  95. {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.1.dist-info}/WHEEL +0 -0
  96. {hyperpocket-0.3.7.dist-info → hyperpocket-0.4.1.dist-info}/entry_points.txt +0 -0
@@ -10,7 +10,6 @@ from uvicorn import Config, Server
10
10
  from hyperpocket.config import config, pocket_logger
11
11
  from hyperpocket.pocket_core import PocketCore
12
12
  from hyperpocket.server.auth import auth_router
13
- from hyperpocket.server.tool import tool_router
14
13
 
15
14
 
16
15
  class PocketServerOperations(enum.Enum):
@@ -18,10 +17,12 @@ class PocketServerOperations(enum.Enum):
18
17
  PREPARE_AUTH = "prepare_auth"
19
18
  AUTHENTICATE = "authenticate"
20
19
  TOOL_CALL = "tool_call"
20
+ PLUG_CORE = "plug_core"
21
21
 
22
22
 
23
23
  class PocketServer(object):
24
- main_server: Server
24
+ fastapi_app: Optional[FastAPI]
25
+ main_server: Optional[Server]
25
26
  internal_server_port: int
26
27
  proxy_server: Optional[Server]
27
28
  proxy_port: int
@@ -29,15 +30,64 @@ class PocketServer(object):
29
30
  process: mp.Process
30
31
  future_store: dict[str, asyncio.Future]
31
32
  torn_down: bool = False
32
-
33
- def __init__(
34
- self,
35
- internal_server_port: int = config().internal_server_port,
36
- proxy_port: int = config().public_server_port,
37
- ):
38
- self.internal_server_port = internal_server_port
39
- self.proxy_port = proxy_port
33
+ _uidset: set
34
+ _cores: dict[str, PocketCore]
35
+
36
+ def __init__(self):
37
+ self._initialized = False
38
+ self.internal_server_port = config().internal_server_port
39
+ self.proxy_port = config().public_server_port
40
+ self._uidset = set()
40
41
  self.future_store = dict()
42
+ self.fastapi_app = None
43
+ self.main_server = None
44
+ self._cores = dict()
45
+
46
+ # should be called in child process
47
+ def _plug_core(self, pocket_uid: str, pocket_core: PocketCore, *_a, **_kw):
48
+ # extend http routers from each docks
49
+ dock_routes = set([str(r) for r in self.fastapi_app.routes])
50
+
51
+ for dock in pocket_core.docks:
52
+ # check duplicated api route
53
+ dock_route = set([str(r) for r in dock.router.routes])
54
+ if dock_route in dock_routes:
55
+ continue
56
+
57
+ dock_routes.update(dock_route)
58
+ self.fastapi_app.include_router(dock.router)
59
+
60
+ # keep pocket core
61
+ self._cores[pocket_uid] = pocket_core
62
+
63
+ # should be called in parent process
64
+ async def plug_core(self, pocket_uid: str, pocket_core: PocketCore):
65
+ await self.call_in_subprocess(
66
+ PocketServerOperations.PLUG_CORE,
67
+ pocket_uid,
68
+ tuple(),
69
+ {
70
+ "pocket_uid": pocket_uid,
71
+ "pocket_core": pocket_core,
72
+ },
73
+ )
74
+
75
+ @classmethod
76
+ def get_instance_and_refcnt_up(cls, uid: str):
77
+ if cls.__dict__.get("_instance") is None:
78
+ cls._instance = cls()
79
+ cls._instance.run()
80
+ cls._instance.refcnt_up(uid)
81
+ return cls._instance
82
+
83
+ def refcnt_up(self, uid: str):
84
+ self._uidset.add(uid)
85
+
86
+ def refcnt_down(self, uid: str):
87
+ if uid in self._uidset:
88
+ self._uidset.remove(uid)
89
+ if len(self._uidset) == 0:
90
+ self._instance.teardown()
41
91
 
42
92
  def teardown(self):
43
93
  # @XXX(seokju) is it ok to call this method both in __del__ and __exit__?
@@ -63,20 +113,21 @@ class PocketServer(object):
63
113
  loop = asyncio.get_running_loop()
64
114
  _, conn = self.pipe
65
115
 
66
- async def _acall(_conn, _op, _uid, a, kw):
116
+ async def _acall(_conn, _future_uid, _core_uid, _args, _kwargs):
67
117
  try:
68
- result = await self.pocket_core.acall(*a, **kw)
118
+ core = self._cores[_core_uid]
119
+ result = await core.acall(*_args, **_kwargs)
69
120
  error = None
70
121
  except Exception as e:
71
- pocket_logger.error(f"failed to acall in pocket subprocess. error: {e}")
122
+ pocket_logger.error(f"failed in pocket subprocess. error: {e}")
72
123
  result = None
73
124
  error = e
125
+ _conn.send((_future_uid, result, error))
74
126
 
75
- _conn.send((_op, _uid, result, error))
76
-
77
- async def _prepare(_conn, _op, _uid, a, kw):
127
+ async def _prepare(_conn, _future_uid, _core_uid, a, kw):
78
128
  try:
79
- result = self.pocket_core.prepare_auth(*a, **kw)
129
+ core = self._cores[_core_uid]
130
+ result = core.prepare_auth(*a, **kw)
80
131
  error = None
81
132
  except Exception as e:
82
133
  pocket_logger.error(
@@ -85,11 +136,12 @@ class PocketServer(object):
85
136
  result = None
86
137
  error = e
87
138
 
88
- _conn.send((_op, _uid, result, error))
139
+ _conn.send((_future_uid, result, error))
89
140
 
90
- async def _authenticate(_conn, _op, _uid, a, kw):
141
+ async def _authenticate(_conn, _future_uid, _core_uid, a, kw):
91
142
  try:
92
- result = await self.pocket_core.authenticate(*a, **kw)
143
+ core = self._cores[_core_uid]
144
+ result = await core.authenticate(*a, **kw)
93
145
  error = None
94
146
  except Exception as e:
95
147
  pocket_logger.error(
@@ -98,11 +150,12 @@ class PocketServer(object):
98
150
  result = None
99
151
  error = e
100
152
 
101
- _conn.send((_op, _uid, result, error))
153
+ _conn.send((_future_uid, result, error))
102
154
 
103
- async def _tool_call(_conn, _op, _uid, a, kw):
155
+ async def _tool_call(_conn, _future_uid, _core_uid, a, kw):
104
156
  try:
105
- result = await self.pocket_core.tool_call(*a, **kw)
157
+ core = self._cores[_core_uid]
158
+ result = await core.tool_call(*a, **kw)
106
159
  error = None
107
160
  except Exception as e:
108
161
  pocket_logger.error(
@@ -111,38 +164,50 @@ class PocketServer(object):
111
164
  result = None
112
165
  error = e
113
166
 
114
- _conn.send((_op, _uid, result, error))
167
+ _conn.send((_future_uid, result, error))
168
+
169
+ async def _plug_core(_conn, _future_uid, _core_uid, a, kw):
170
+ try:
171
+ self._plug_core(*a, **kw)
172
+ _conn.send((_future_uid, None, None))
173
+ except Exception as e:
174
+ pocket_logger.error(
175
+ f"failed to plug_core in pocket subprocess. error: {e}"
176
+ )
177
+ _conn.send((_future_uid, None, e))
115
178
 
116
179
  while True:
117
180
  if conn.poll():
118
- op, uid, args, kwargs = conn.recv()
119
- if op == PocketServerOperations.CALL.value:
120
- loop.create_task(_acall(conn, op, uid, args, kwargs))
121
- elif op == PocketServerOperations.PREPARE_AUTH.value:
122
- loop.create_task(_prepare(conn, op, uid, args, kwargs))
123
- elif op == PocketServerOperations.AUTHENTICATE.value:
124
- loop.create_task(_authenticate(conn, op, uid, args, kwargs))
125
- elif op == PocketServerOperations.TOOL_CALL.value:
126
- loop.create_task(_tool_call(conn, op, uid, args, kwargs))
181
+ op, future_uid, core_uid, args, kwargs = conn.recv()
182
+ if op == PocketServerOperations.CALL:
183
+ loop.create_task(_acall(conn, future_uid, core_uid, args, kwargs))
184
+ elif op == PocketServerOperations.PREPARE_AUTH:
185
+ loop.create_task(_prepare(conn, future_uid, core_uid, args, kwargs))
186
+ elif op == PocketServerOperations.AUTHENTICATE:
187
+ loop.create_task(_authenticate(conn, future_uid, core_uid, args, kwargs))
188
+ elif op == PocketServerOperations.TOOL_CALL:
189
+ loop.create_task(_tool_call(conn, future_uid, core_uid, args, kwargs))
190
+ elif op == PocketServerOperations.PLUG_CORE:
191
+ loop.create_task(_plug_core(conn, future_uid, core_uid, args, kwargs))
127
192
  else:
128
- raise AttributeError(f"Can't find operations. op:{op}")
193
+ raise ValueError(f"Unknown operation: {op}")
129
194
  else:
130
195
  await asyncio.sleep(0)
131
196
 
132
- def send_in_parent(self, op: PocketServerOperations, args: tuple, kwargs: dict):
197
+ def send_in_parent(self, op: PocketServerOperations, pocket_uid: str, args: tuple, kwargs: dict):
133
198
  conn, _ = self.pipe
134
- uid = str(uuid.uuid4())
135
- message = (op.value, uid, args, kwargs)
199
+ future_uid = str(uuid.uuid4())
200
+ message = (op, future_uid, pocket_uid, args, kwargs)
136
201
  future = asyncio.Future()
137
- self.future_store[uid] = future
202
+ self.future_store[future_uid] = future
138
203
  conn.send(message)
139
- return uid
204
+ return future_uid
140
205
 
141
206
  async def poll_in_parent(self):
142
207
  conn, _ = self.pipe
143
208
  while True:
144
209
  if conn.poll():
145
- op, uid, result, error = conn.recv()
210
+ uid, result, error = conn.recv()
146
211
  future = self.future_store[uid]
147
212
  if error:
148
213
  future.set_exception(error)
@@ -153,19 +218,21 @@ class PocketServer(object):
153
218
  await asyncio.sleep(0)
154
219
 
155
220
  async def call_in_subprocess(
156
- self, op: PocketServerOperations, args: tuple, kwargs: dict
221
+ self, op: PocketServerOperations, pocket_uid: str, args: tuple, kwargs: dict
157
222
  ):
158
- uid = self.send_in_parent(op, args, kwargs)
223
+ uid = self.send_in_parent(op, pocket_uid, args, kwargs)
159
224
  loop = asyncio.get_running_loop()
160
225
  loop.create_task(self.poll_in_parent())
161
226
  return await self.future_store[uid]
162
227
 
163
- def run(self, pocket_core: PocketCore):
228
+ def run(self):
164
229
  self._set_mp_start_method()
165
230
 
166
231
  error_queue = mp.Queue()
167
232
  self.pipe = mp.Pipe()
168
- self.process = mp.Process(target=self._run, args=(pocket_core,))
233
+ self.process = mp.Process(
234
+ target=self._run
235
+ )
169
236
  self.process.start() # process start
170
237
 
171
238
  if not error_queue.empty():
@@ -174,27 +241,25 @@ class PocketServer(object):
174
241
 
175
242
  def _report_initialized(self, error: Optional[Exception] = None):
176
243
  _, conn = self.pipe
177
- conn.send(
178
- (
179
- "server-initialization",
180
- error,
181
- )
182
- )
244
+ conn.send(('server-initialization', error,))
183
245
 
184
246
  def wait_initialized(self):
247
+ if self._initialized:
248
+ return
185
249
  conn, _ = self.pipe
186
250
  while True:
187
251
  if conn.poll():
188
252
  _, error = conn.recv()
189
253
  if error:
190
254
  raise error
191
- return
255
+ break
256
+ self._initialized = True
192
257
 
193
- def _run(self, pocket_core):
258
+ def _run(self):
194
259
  try:
195
260
  # init process
196
- self.pocket_core = pocket_core
197
- self.main_server = self._create_main_server()
261
+ self.fastapi_app = self._create_fastapi_app()
262
+ self.main_server = self._create_main_server(self.fastapi_app)
198
263
  self.proxy_server = self._create_https_proxy_server()
199
264
  self._report_initialized()
200
265
 
@@ -204,20 +269,21 @@ class PocketServer(object):
204
269
  except Exception as error:
205
270
  self._report_initialized(error)
206
271
 
207
- def _create_main_server(self) -> Server:
272
+ def _create_fastapi_app(self) -> FastAPI:
208
273
  app = FastAPI()
274
+ app.include_router(auth_router)
275
+ app.add_api_route("/health", lambda: {"status": "ok"}, methods=["GET"])
276
+ return app
277
+
278
+ def _create_main_server(self, app: FastAPI) -> Server:
209
279
  _config = Config(
210
280
  app,
211
281
  host="0.0.0.0",
212
282
  port=self.internal_server_port,
213
283
  log_level=config().log_level,
214
284
  )
215
- app.include_router(tool_router)
216
- app.include_router(auth_router)
217
- app.add_api_route("/health", lambda: {"status": "ok"}, methods=["GET"])
218
-
219
- app = Server(_config)
220
- return app
285
+ server = Server(_config)
286
+ return server
221
287
 
222
288
  def _create_https_proxy_server(self) -> Optional[Server]:
223
289
  if not config().enable_local_callback_proxy:
@@ -19,10 +19,20 @@ InMemorySessionValue = BaseSessionValue
19
19
  class InMemorySessionStorage(
20
20
  SessionStorageInterface[InMemorySessionKey, InMemorySessionValue]
21
21
  ):
22
- # TODO(moon) : Force it to always take SessionConfig as an input
22
+ _instance = None
23
+ _is_initialized = False
24
+
25
+ def __new__(cls, *args, **kwargs):
26
+ if cls._instance is None:
27
+ cls._instance = super(InMemorySessionStorage, cls).__new__(cls)
28
+ cls._instance._is_initialized = False
29
+ return cls._instance
30
+
23
31
  def __init__(self, session_config: SessionConfigInMemory):
24
- super().__init__()
25
- self.storage: Dict[InMemorySessionKey, InMemorySessionValue] = {}
32
+ if not self._is_initialized:
33
+ super().__init__()
34
+ self.storage: Dict[InMemorySessionKey, InMemorySessionValue] = {}
35
+ self._is_initialized = True
26
36
 
27
37
  @classmethod
28
38
  def session_storage_type(cls) -> SessionType:
@@ -1,13 +1,10 @@
1
1
  from hyperpocket.tool.function import from_dock, from_func, function_tool
2
2
  from hyperpocket.tool.tool import Tool, ToolAuth, ToolRequest
3
- from hyperpocket.tool.wasm.tool import from_git, from_local
4
3
 
5
4
  __all__ = [
6
5
  "Tool",
7
6
  "ToolAuth",
8
7
  "ToolRequest",
9
- "from_local",
10
- "from_git",
11
8
  "from_dock",
12
9
  "from_func",
13
10
  "function_tool",
@@ -0,0 +1,3 @@
1
+ from hyperpocket.tool.dock.dock import Dock
2
+
3
+ __all__ = ["Dock"]
@@ -0,0 +1,34 @@
1
+ import abc
2
+ from typing import Any
3
+
4
+ from fastapi import APIRouter
5
+
6
+ from hyperpocket.tool import ToolRequest
7
+ from hyperpocket.tool.function import FunctionTool
8
+
9
+
10
+ class Dock(abc.ABC):
11
+ _tool_requests: list[ToolRequest]
12
+ _dock_http_router: APIRouter
13
+ _dock_vars: dict[str, str]
14
+
15
+ def __init__(self, dock_vars: dict[str, str] = None):
16
+ self._dock_http_router = APIRouter()
17
+ self._tool_requests = []
18
+ self._dock_vars = dock_vars if dock_vars is not None else {}
19
+
20
+ @property
21
+ def router(self):
22
+ return self._dock_http_router
23
+
24
+ @abc.abstractmethod
25
+ def plug(self, req_like: Any, **kwargs):
26
+ raise NotImplementedError
27
+
28
+ @abc.abstractmethod
29
+ def tools(self) -> list[FunctionTool]:
30
+ raise NotImplementedError
31
+
32
+ @abc.abstractmethod
33
+ async def teardown(self):
34
+ raise NotImplementedError
@@ -4,4 +4,4 @@ from hyperpocket.tool.function.tool import FunctionTool
4
4
  from_func = FunctionTool.from_func
5
5
  from_dock = FunctionTool.from_dock
6
6
 
7
- __all__ = ["from_func", "from_dock", "function_tool"]
7
+ __all__ = ["from_func", "from_dock", "function_tool", "FunctionTool"]
@@ -17,19 +17,36 @@ class FunctionTool(Tool):
17
17
 
18
18
  func: Optional[Callable[..., str]]
19
19
  afunc: Optional[Callable[..., Coroutine[Any, Any, str]]]
20
+ keep_structured_arguments: bool = False
20
21
 
21
22
  def invoke(self, **kwargs) -> str:
22
23
  binding_args = self._get_binding_args(kwargs)
23
24
  if self.func is None:
24
25
  if self.afunc is None:
25
26
  raise ValueError("Both func and afunc are None")
27
+ try:
28
+ loop = asyncio.get_running_loop()
29
+ return str(loop.run_until_complete(self.afunc(**binding_args)))
30
+ except RuntimeError:
31
+ pass
32
+ except Exception as e:
33
+ import traceback
34
+ traceback.print_exc()
35
+ traceback.print_stack()
36
+ return "There was an error while executing the tool: " + str(e)
26
37
  try:
27
38
  return str(asyncio.run(self.afunc(**binding_args)))
28
39
  except Exception as e:
40
+ import traceback
41
+ traceback.print_exc()
42
+ traceback.print_stack()
29
43
  return "There was an error while executing the tool: " + str(e)
30
44
  try:
31
45
  return str(self.func(**binding_args))
32
46
  except Exception as e:
47
+ import traceback
48
+ traceback.print_exc()
49
+ traceback.print_stack()
33
50
  return "There was an error while executing the tool: " + str(e)
34
51
 
35
52
  async def ainvoke(self, **kwargs) -> str:
@@ -39,9 +56,16 @@ class FunctionTool(Tool):
39
56
  binding_args = self._get_binding_args(kwargs)
40
57
  return str(await self.afunc(**binding_args))
41
58
  except Exception as e:
59
+ import traceback
60
+ traceback.print_exc()
61
+ traceback.print_stack()
42
62
  return "There was an error while executing the tool: " + str(e)
43
63
 
44
64
  def _get_binding_args(self, kwargs):
65
+ if self.keep_structured_arguments:
66
+ if kwargs.get("envs") is not None:
67
+ kwargs["envs"] |= self.tool_vars
68
+ return kwargs
45
69
  _kwargs = copy.deepcopy(kwargs)
46
70
 
47
71
  # make body args to model
@@ -54,28 +78,23 @@ class FunctionTool(Tool):
54
78
 
55
79
  # binding args
56
80
  binding_args = {}
57
- if self.func.__dict__.get("__model__") is not None:
58
- # when a function signature is not inferrable from the function itself
59
- binding_args = args.copy()
60
- binding_args |= _kwargs.get("envs", {}) | self.tool_vars
61
- else:
62
- sig = inspect.signature(self.func)
63
- for param_name, param in sig.parameters.items():
64
- if param_name not in args:
65
- continue
81
+ sig = inspect.signature(self.func)
82
+ for param_name, param in sig.parameters.items():
83
+ if param_name not in args:
84
+ continue
66
85
 
67
- if param.kind == param.VAR_KEYWORD:
68
- # var keyword args should be passed by plain dict
69
- binding_args |= args[param_name]
70
- binding_args |= _kwargs.get("envs", {}) | self.tool_vars
86
+ if param.kind == param.VAR_KEYWORD:
87
+ # var keyword args should be passed by plain dict
88
+ binding_args |= args[param_name]
89
+ binding_args |= _kwargs.get("envs", {}) | self.tool_vars
71
90
 
72
- if "envs" in _kwargs:
73
- _kwargs.pop("envs")
91
+ if "envs" in _kwargs:
92
+ _kwargs.pop("envs")
74
93
 
75
- binding_args |= _kwargs # add other kwargs
76
- continue
94
+ binding_args |= _kwargs # add other kwargs
95
+ continue
77
96
 
78
- binding_args[param_name] = args[param_name]
97
+ binding_args[param_name] = args[param_name]
79
98
 
80
99
  return binding_args
81
100
 
@@ -89,11 +108,15 @@ class FunctionTool(Tool):
89
108
 
90
109
  @classmethod
91
110
  def from_func(
92
- cls,
93
- func: Callable | "FunctionTool",
94
- afunc: Callable[..., Coroutine[Any, Any, str]] | "FunctionTool" = None,
95
- auth: Optional[ToolAuth] = None,
96
- tool_vars: dict[str, str] = None,
111
+ cls,
112
+ func: Callable | "FunctionTool",
113
+ afunc: Callable[..., Coroutine[Any, Any, str]] | "FunctionTool" = None,
114
+ name: str = None,
115
+ description: str = None,
116
+ json_schema: dict[str, Any] = None,
117
+ auth: Optional[ToolAuth] = None,
118
+ tool_vars: dict[str, str] = None,
119
+ keep_structured_arguments: bool = False,
97
120
  ) -> "FunctionTool":
98
121
  if tool_vars is None:
99
122
  tool_vars = dict()
@@ -109,28 +132,35 @@ class FunctionTool(Tool):
109
132
  elif not callable(func) and not callable(afunc):
110
133
  raise ValueError("FunctionTool can only be created from a callable")
111
134
 
112
- model = function_to_model(func)
113
- argument_json_schema = flatten_json_schema(model.model_json_schema())
135
+ name = name or (func and func.__name__) or (afunc and afunc.__name__)
136
+ description = description or (func and func.__doc__) or (afunc and func.__doc__)
137
+ schema = json_schema or \
138
+ (func and function_to_model(func).model_json_schema()) or \
139
+ (afunc and function_to_model(afunc).model_json_schema())
140
+ argument_json_schema = flatten_json_schema(schema)
114
141
 
115
142
  return cls(
116
143
  func=func,
117
144
  afunc=afunc,
118
- name=func.__name__,
119
- description=func.__doc__ if func.__doc__ is not None else "",
145
+ name=name,
146
+ description=description,
120
147
  argument_json_schema=argument_json_schema,
121
148
  auth=auth,
122
149
  default_tool_vars=tool_vars,
150
+ keep_structured_arguments=keep_structured_arguments,
123
151
  )
124
152
 
125
153
  @classmethod
126
154
  def from_dock(
127
- cls,
128
- dock: list[Callable[..., str]],
129
- tool_vars: Optional[dict[str, str]] = None,
155
+ cls,
156
+ dock: list[Callable[..., str]],
157
+ tool_vars: Optional[dict[str, str]] = None,
130
158
  ) -> list["FunctionTool"]:
131
159
  if tool_vars is None:
132
160
  tool_vars = dict()
133
161
  tools = []
162
+
163
+ # @moon: will be refactored.
134
164
  for func in dock:
135
165
  if (_model := func.__dict__.get("__model__")) is not None:
136
166
  model = _model
@@ -155,7 +185,7 @@ class FunctionTool(Tool):
155
185
  argument_json_schema=argument_json_schema,
156
186
  auth=auth,
157
187
  default_tool_vars=(
158
- tool_vars | func.__dict__.get("__vars__", {})
188
+ tool_vars | func.__dict__.get("__vars__", {})
159
189
  ),
160
190
  )
161
191
  )
@@ -169,7 +199,7 @@ class FunctionTool(Tool):
169
199
  argument_json_schema=argument_json_schema,
170
200
  auth=auth,
171
201
  default_tool_vars=(
172
- tool_vars | func.__dict__.get("__vars__", {})
202
+ tool_vars | func.__dict__.get("__vars__", {})
173
203
  ),
174
204
  )
175
205
  )
hyperpocket/tool/tool.py CHANGED
@@ -116,21 +116,13 @@ class Tool(BaseModel, abc.ABC):
116
116
  return self.description
117
117
 
118
118
  def override_tool_variables(self, override_vars: dict[str, str]) -> "Tool":
119
- self.overridden_tool_vars = override_vars
119
+ self.overridden_tool_vars |= override_vars
120
120
  return self
121
121
 
122
122
  @property
123
123
  def tool_vars(self) -> dict[str, str]:
124
124
  return self.default_tool_vars | self.overridden_tool_vars
125
125
 
126
- @classmethod
127
- def from_tool_request(cls, tool_req: ToolRequest, **kwargs) -> "Tool":
128
- from hyperpocket.tool.wasm.tool import WasmTool, WasmToolRequest
129
-
130
- if isinstance(tool_req, WasmToolRequest):
131
- return WasmTool.from_tool_request(tool_req, **kwargs)
132
- raise ValueError("Unknown tool request type")
133
-
134
126
  @classmethod
135
127
  def _get_schema_model(
136
128
  cls, name: str, json_schema: Optional[dict], use_profile: bool
hyperpocket/tool_like.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from typing import Callable, Union
2
2
 
3
3
  from hyperpocket.tool import Tool, ToolRequest
4
+ from hyperpocket.tool.dock import Dock
4
5
 
5
- ToolLike = Union[Tool, str, Callable, ToolRequest]
6
+ ToolLike = Union[Tool, str, tuple, Callable, ToolRequest, Dock]
@@ -0,0 +1,4 @@
1
+ import string, random
2
+
3
+ def generate_slug(length: int = 6) -> str:
4
+ return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
@@ -60,13 +60,17 @@ def json_schema_to_model(
60
60
  fields["additional_properties"] = (dict[str, additional_model], {})
61
61
 
62
62
  # Create the model
63
- model = create_model(model_name, **fields)
63
+ model = create_model(f"{model_name}", **fields)
64
64
 
65
65
  # Add custom Config class to handle extra properties
66
66
  class Config:
67
67
  extra = config_extra
68
68
 
69
69
  model.Config = Config
70
+
71
+ # workaround for pickling dynamic classes
72
+ model.__module__ = "__main__"
73
+ model.__qualname__ = model.__name__.split('.')[-1]
70
74
 
71
75
  return model
72
76
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hyperpocket
3
- Version: 0.3.7
3
+ Version: 0.4.1
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
@@ -13,6 +13,7 @@ Requires-Dist: gitpython>=3.1.43
13
13
  Requires-Dist: httpx==0.27
14
14
  Requires-Dist: jinja2>=3.1.4
15
15
  Requires-Dist: multiprocess>=0.70.17
16
+ Requires-Dist: nest-asyncio>=1.6.0
16
17
  Requires-Dist: playwright>=1.49.0
17
18
  Requires-Dist: pydantic>=2.10.2
18
19
  Requires-Dist: pygithub>=2.5.0
@@ -21,6 +22,8 @@ Requires-Dist: redis>=5.2.1
21
22
  Requires-Dist: requests>=2.32.3
22
23
  Requires-Dist: toml>=0.10.2
23
24
  Requires-Dist: uvicorn>=0.32.1
25
+ Provides-Extra: standard
26
+ Requires-Dist: hyperdock-container; extra == 'standard'
24
27
  Description-Content-Type: text/markdown
25
28
 
26
29
  <p align="center">