hyperpocket 0.1.9__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.
- hyperpocket/__init__.py +4 -4
- hyperpocket/auth/__init__.py +12 -7
- hyperpocket/auth/calendly/oauth2_handler.py +24 -17
- hyperpocket/auth/calendly/oauth2_schema.py +3 -1
- hyperpocket/auth/context.py +2 -1
- hyperpocket/auth/github/oauth2_handler.py +13 -8
- hyperpocket/auth/github/token_handler.py +27 -21
- hyperpocket/auth/google/context.py +1 -3
- hyperpocket/auth/google/oauth2_context.py +1 -1
- hyperpocket/auth/google/oauth2_handler.py +22 -17
- hyperpocket/auth/gumloop/token_context.py +1 -4
- hyperpocket/auth/gumloop/token_handler.py +48 -20
- hyperpocket/auth/gumloop/token_schema.py +2 -1
- hyperpocket/auth/handler.py +21 -6
- hyperpocket/auth/linear/token_context.py +2 -5
- hyperpocket/auth/linear/token_handler.py +45 -21
- hyperpocket/auth/notion/context.py +2 -2
- hyperpocket/auth/notion/token_context.py +2 -4
- hyperpocket/auth/notion/token_handler.py +45 -21
- hyperpocket/auth/notion/token_schema.py +0 -1
- hyperpocket/auth/reddit/oauth2_handler.py +9 -10
- hyperpocket/auth/reddit/oauth2_schema.py +0 -2
- hyperpocket/auth/schema.py +4 -1
- hyperpocket/auth/slack/oauth2_context.py +3 -1
- hyperpocket/auth/slack/oauth2_handler.py +55 -35
- hyperpocket/auth/slack/token_context.py +2 -4
- hyperpocket/auth/slack/token_handler.py +42 -19
- hyperpocket/builtin.py +4 -2
- hyperpocket/cli/__main__.py +4 -2
- hyperpocket/cli/auth.py +59 -28
- hyperpocket/cli/codegen/auth/auth_context_template.py +3 -2
- hyperpocket/cli/codegen/auth/auth_token_context_template.py +3 -2
- hyperpocket/cli/codegen/auth/auth_token_handler_template.py +6 -5
- hyperpocket/cli/codegen/auth/auth_token_schema_template.py +3 -2
- hyperpocket/cli/codegen/auth/server_auth_template.py +3 -2
- hyperpocket/cli/pull.py +5 -5
- hyperpocket/config/__init__.py +3 -8
- hyperpocket/config/auth.py +3 -1
- hyperpocket/config/logger.py +20 -15
- hyperpocket/config/session.py +4 -2
- hyperpocket/config/settings.py +19 -2
- hyperpocket/futures/__init__.py +1 -1
- hyperpocket/futures/futurestore.py +3 -2
- hyperpocket/pocket_auth.py +171 -84
- hyperpocket/pocket_core.py +51 -33
- hyperpocket/pocket_main.py +122 -93
- hyperpocket/prompts.py +2 -2
- hyperpocket/repository/__init__.py +1 -1
- hyperpocket/repository/lock.py +47 -33
- hyperpocket/repository/lockfile.py +2 -2
- hyperpocket/repository/repository.py +1 -1
- hyperpocket/server/__init__.py +1 -1
- hyperpocket/server/auth/github.py +2 -1
- hyperpocket/server/auth/linear.py +1 -3
- hyperpocket/server/auth/notion.py +2 -5
- hyperpocket/server/auth/slack.py +1 -3
- hyperpocket/server/auth/token.py +17 -11
- hyperpocket/server/proxy.py +29 -13
- hyperpocket/server/server.py +75 -31
- hyperpocket/server/tool/dto/script.py +15 -10
- hyperpocket/server/tool/wasm.py +14 -11
- hyperpocket/session/__init__.py +6 -2
- hyperpocket/session/in_memory.py +44 -24
- hyperpocket/session/interface.py +42 -24
- hyperpocket/session/redis.py +48 -31
- hyperpocket/tool/__init__.py +10 -10
- hyperpocket/tool/function/__init__.py +1 -5
- hyperpocket/tool/function/annotation.py +11 -9
- hyperpocket/tool/function/tool.py +37 -27
- hyperpocket/tool/tool.py +59 -36
- hyperpocket/tool/wasm/__init__.py +1 -1
- hyperpocket/tool/wasm/browser.py +15 -10
- hyperpocket/tool/wasm/invoker.py +16 -16
- hyperpocket/tool/wasm/script.py +27 -14
- hyperpocket/tool/wasm/templates/__init__.py +22 -15
- hyperpocket/tool/wasm/templates/node.py +2 -2
- hyperpocket/tool/wasm/templates/python.py +2 -2
- hyperpocket/tool/wasm/tool.py +27 -14
- hyperpocket/tool_like.py +3 -3
- hyperpocket/util/__init__.py +1 -1
- hyperpocket/util/extract_func_param_desc_from_docstring.py +33 -14
- hyperpocket/util/find_all_leaf_class_in_package.py +4 -3
- hyperpocket/util/find_all_subclass_in_package.py +4 -2
- hyperpocket/util/flatten_json_schema.py +10 -6
- hyperpocket/util/function_to_model.py +33 -12
- hyperpocket/util/get_objects_from_subpackage.py +1 -1
- hyperpocket/util/json_schema_to_model.py +14 -5
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/METADATA +29 -24
- hyperpocket-0.2.0.dist-info/RECORD +137 -0
- hyperpocket-0.1.9.dist-info/RECORD +0 -137
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/WHEEL +0 -0
- {hyperpocket-0.1.9.dist-info → hyperpocket-0.2.0.dist-info}/entry_points.txt +0 -0
hyperpocket/session/redis.py
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
import json
|
2
|
-
from typing import List, Optional
|
2
|
+
from typing import Any, List, Optional
|
3
3
|
|
4
4
|
import redis
|
5
5
|
|
6
|
-
from hyperpocket.auth import
|
6
|
+
from hyperpocket.auth import AUTH_CONTEXT_MAP, AuthProvider
|
7
7
|
from hyperpocket.auth.context import AuthContext
|
8
|
-
from hyperpocket.config.session import SessionConfigRedis
|
9
|
-
from hyperpocket.
|
10
|
-
|
11
|
-
BaseSessionValue,
|
8
|
+
from hyperpocket.config.session import SessionConfigRedis, SessionType
|
9
|
+
from hyperpocket.session.interface import (
|
10
|
+
SESSION_KEY_DELIMITER,
|
11
|
+
BaseSessionValue,
|
12
|
+
K,
|
13
|
+
SessionStorageInterface,
|
14
|
+
V,
|
15
|
+
)
|
12
16
|
|
13
17
|
RedisSessionKey = str
|
14
18
|
RedisSessionValue = BaseSessionValue
|
@@ -24,7 +28,9 @@ class RedisSessionStorage(SessionStorageInterface[RedisSessionKey, RedisSessionV
|
|
24
28
|
def session_storage_type(cls) -> SessionType:
|
25
29
|
return SessionType.REDIS
|
26
30
|
|
27
|
-
def get(
|
31
|
+
def get(
|
32
|
+
self, auth_provider: AuthProvider, thread_id: str, profile: str, **kwargs
|
33
|
+
) -> Optional[V]:
|
28
34
|
key = self._make_session_key(auth_provider.name, thread_id, profile)
|
29
35
|
raw_session: Any = self.client.get(key)
|
30
36
|
if raw_session is None:
|
@@ -33,7 +39,9 @@ class RedisSessionStorage(SessionStorageInterface[RedisSessionKey, RedisSessionV
|
|
33
39
|
session = self._deserialize(raw_session)
|
34
40
|
return session
|
35
41
|
|
36
|
-
def get_by_thread_id(
|
42
|
+
def get_by_thread_id(
|
43
|
+
self, thread_id: str, auth_provider: Optional[AuthProvider] = None, **kwargs
|
44
|
+
) -> List[V]:
|
37
45
|
if auth_provider is None:
|
38
46
|
auth_provider_name = "*"
|
39
47
|
else:
|
@@ -57,19 +65,24 @@ class RedisSessionStorage(SessionStorageInterface[RedisSessionKey, RedisSessionV
|
|
57
65
|
|
58
66
|
return session_list
|
59
67
|
|
60
|
-
def set(
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
68
|
+
def set(
|
69
|
+
self,
|
70
|
+
auth_provider: AuthProvider,
|
71
|
+
thread_id: str,
|
72
|
+
profile: str,
|
73
|
+
auth_scopes: List[str],
|
74
|
+
auth_resolve_uid: Optional[str],
|
75
|
+
auth_context: Optional[AuthContext],
|
76
|
+
is_auth_scope_universal: bool,
|
77
|
+
**kwargs,
|
78
|
+
) -> V:
|
67
79
|
session = self._make_session(
|
68
80
|
auth_provider_name=auth_provider.name,
|
69
81
|
auth_scopes=auth_scopes,
|
70
82
|
auth_context=auth_context,
|
71
83
|
auth_resolve_uid=auth_resolve_uid,
|
72
|
-
is_auth_scope_universal=is_auth_scope_universal
|
84
|
+
is_auth_scope_universal=is_auth_scope_universal,
|
85
|
+
)
|
73
86
|
|
74
87
|
key = self._make_session_key(auth_provider.name, thread_id, profile)
|
75
88
|
|
@@ -77,7 +90,9 @@ class RedisSessionStorage(SessionStorageInterface[RedisSessionKey, RedisSessionV
|
|
77
90
|
self.client.set(key, raw_session)
|
78
91
|
return session
|
79
92
|
|
80
|
-
def delete(
|
93
|
+
def delete(
|
94
|
+
self, auth_provider: AuthProvider, thread_id: str, profile: str, **kwargs
|
95
|
+
) -> bool:
|
81
96
|
key = self._make_session_key(auth_provider.name, thread_id, profile)
|
82
97
|
return self.client.delete(key) == 1
|
83
98
|
|
@@ -92,17 +107,18 @@ class RedisSessionStorage(SessionStorageInterface[RedisSessionKey, RedisSessionV
|
|
92
107
|
|
93
108
|
@staticmethod
|
94
109
|
def _make_session(
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
110
|
+
auth_provider_name: str,
|
111
|
+
auth_scopes: List[str],
|
112
|
+
auth_context: AuthContext,
|
113
|
+
auth_resolve_uid: str,
|
114
|
+
is_auth_scope_universal: bool,
|
115
|
+
) -> V:
|
100
116
|
return RedisSessionValue(
|
101
117
|
auth_provider_name=auth_provider_name,
|
102
118
|
auth_scopes=set(auth_scopes),
|
103
119
|
auth_context=auth_context,
|
104
120
|
auth_resolve_uid=auth_resolve_uid,
|
105
|
-
scoped=is_auth_scope_universal
|
121
|
+
scoped=is_auth_scope_universal,
|
106
122
|
)
|
107
123
|
|
108
124
|
@staticmethod
|
@@ -116,13 +132,14 @@ class RedisSessionStorage(SessionStorageInterface[RedisSessionKey, RedisSessionV
|
|
116
132
|
if auth_scopes:
|
117
133
|
auth_scopes = list(auth_scopes)
|
118
134
|
|
119
|
-
serialized = {
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
135
|
+
serialized = {
|
136
|
+
"auth_context_value": auth_context_value,
|
137
|
+
"auth_context_type": auth_context_type,
|
138
|
+
"auth_provider_name": session.auth_provider_name,
|
139
|
+
"scoped": session.scoped,
|
140
|
+
"auth_scopes": auth_scopes,
|
141
|
+
"auth_resolve_uid": session.auth_resolve_uid,
|
142
|
+
}
|
126
143
|
|
127
144
|
return json.dumps(serialized)
|
128
145
|
|
@@ -146,5 +163,5 @@ class RedisSessionStorage(SessionStorageInterface[RedisSessionKey, RedisSessionV
|
|
146
163
|
auth_scopes=auth_scopes,
|
147
164
|
auth_resolve_uid=session_dict["auth_resolve_uid"],
|
148
165
|
scoped=session_dict["scoped"],
|
149
|
-
auth_context=auth_context
|
166
|
+
auth_context=auth_context,
|
150
167
|
)
|
hyperpocket/tool/__init__.py
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
from hyperpocket.tool.function import from_dock, from_func, function_tool
|
2
|
-
from hyperpocket.tool.tool import Tool,
|
3
|
-
from hyperpocket.tool.wasm.tool import
|
2
|
+
from hyperpocket.tool.tool import Tool, ToolAuth, ToolRequest
|
3
|
+
from hyperpocket.tool.wasm.tool import from_git, from_local
|
4
4
|
|
5
5
|
__all__ = [
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
6
|
+
"Tool",
|
7
|
+
"ToolAuth",
|
8
|
+
"ToolRequest",
|
9
|
+
"from_local",
|
10
|
+
"from_git",
|
11
|
+
"from_dock",
|
12
|
+
"from_func",
|
13
|
+
"function_tool",
|
14
14
|
]
|
@@ -1,12 +1,18 @@
|
|
1
|
-
from typing import
|
1
|
+
from typing import Callable, List, Optional
|
2
2
|
|
3
3
|
from hyperpocket.auth import AuthProvider
|
4
4
|
from hyperpocket.tool.function.tool import FunctionTool
|
5
5
|
from hyperpocket.tool.tool import ToolAuth
|
6
6
|
|
7
7
|
|
8
|
-
def function_tool(
|
9
|
-
|
8
|
+
def function_tool(
|
9
|
+
func: Optional[Callable] = None,
|
10
|
+
*,
|
11
|
+
auth_provider: AuthProvider = None,
|
12
|
+
scopes: List[str] = None,
|
13
|
+
auth_handler: str = None,
|
14
|
+
tool_vars: dict[str, str] = None,
|
15
|
+
):
|
10
16
|
def decorator(inner_func: Callable):
|
11
17
|
if not callable(inner_func):
|
12
18
|
raise ValueError("FunctionTool can only be created from a callable")
|
@@ -15,14 +21,10 @@ def function_tool(func: Optional[Callable] = None, *, auth_provider: AuthProvide
|
|
15
21
|
auth = ToolAuth(
|
16
22
|
auth_provider=auth_provider,
|
17
23
|
scopes=scopes if scopes else [],
|
18
|
-
auth_handler=auth_handler
|
24
|
+
auth_handler=auth_handler,
|
19
25
|
)
|
20
26
|
|
21
|
-
return FunctionTool.from_func(
|
22
|
-
func=inner_func,
|
23
|
-
auth=auth,
|
24
|
-
tool_vars=tool_vars
|
25
|
-
)
|
27
|
+
return FunctionTool.from_func(func=inner_func, auth=auth, tool_vars=tool_vars)
|
26
28
|
|
27
29
|
if func is not None:
|
28
30
|
return decorator(func)
|
@@ -1,8 +1,7 @@
|
|
1
1
|
import asyncio
|
2
2
|
import copy
|
3
3
|
import inspect
|
4
|
-
from typing import Any, Coroutine
|
5
|
-
from typing import Callable, Optional
|
4
|
+
from typing import Any, Callable, Coroutine, Optional
|
6
5
|
|
7
6
|
from pydantic import BaseModel
|
8
7
|
|
@@ -15,6 +14,7 @@ class FunctionTool(Tool):
|
|
15
14
|
"""
|
16
15
|
FunctionTool is Tool executing local python method.
|
17
16
|
"""
|
17
|
+
|
18
18
|
func: Optional[Callable[..., str]]
|
19
19
|
afunc: Optional[Callable[..., Coroutine[Any, Any, str]]]
|
20
20
|
|
@@ -90,14 +90,14 @@ class FunctionTool(Tool):
|
|
90
90
|
@classmethod
|
91
91
|
def from_func(
|
92
92
|
cls,
|
93
|
-
func: Callable |
|
94
|
-
afunc: Callable[..., Coroutine[Any, Any, str]] |
|
93
|
+
func: Callable | "FunctionTool",
|
94
|
+
afunc: Callable[..., Coroutine[Any, Any, str]] | "FunctionTool" = None,
|
95
95
|
auth: Optional[ToolAuth] = None,
|
96
96
|
tool_vars: dict[str, str] = None,
|
97
97
|
) -> "FunctionTool":
|
98
98
|
if tool_vars is None:
|
99
99
|
tool_vars = dict()
|
100
|
-
|
100
|
+
|
101
101
|
if isinstance(func, FunctionTool):
|
102
102
|
if tool_vars is not None:
|
103
103
|
func.override_tool_variables(tool_vars)
|
@@ -108,7 +108,7 @@ class FunctionTool(Tool):
|
|
108
108
|
return afunc
|
109
109
|
elif not callable(func) and not callable(afunc):
|
110
110
|
raise ValueError("FunctionTool can only be created from a callable")
|
111
|
-
|
111
|
+
|
112
112
|
model = function_to_model(func)
|
113
113
|
argument_json_schema = flatten_json_schema(model.model_json_schema())
|
114
114
|
|
@@ -119,9 +119,9 @@ class FunctionTool(Tool):
|
|
119
119
|
description=func.__doc__ if func.__doc__ is not None else "",
|
120
120
|
argument_json_schema=argument_json_schema,
|
121
121
|
auth=auth,
|
122
|
-
default_tool_vars=tool_vars
|
122
|
+
default_tool_vars=tool_vars,
|
123
123
|
)
|
124
|
-
|
124
|
+
|
125
125
|
@classmethod
|
126
126
|
def from_dock(
|
127
127
|
cls,
|
@@ -138,29 +138,39 @@ class FunctionTool(Tool):
|
|
138
138
|
model = function_to_model(func)
|
139
139
|
argument_json_schema = flatten_json_schema(model.model_json_schema())
|
140
140
|
if not callable(func):
|
141
|
-
raise ValueError(
|
141
|
+
raise ValueError(
|
142
|
+
f"Dock element should be a list of functions, but found {func}"
|
143
|
+
)
|
142
144
|
is_coroutine = inspect.iscoroutinefunction(func)
|
143
145
|
auth = None
|
144
146
|
if func.__dict__.get("__auth__") is not None:
|
145
147
|
auth = ToolAuth(**func.__dict__["__auth__"])
|
146
148
|
if is_coroutine:
|
147
|
-
tools.append(
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
149
|
+
tools.append(
|
150
|
+
cls(
|
151
|
+
func=None,
|
152
|
+
afunc=func,
|
153
|
+
name=func.__name__,
|
154
|
+
description=func.__doc__,
|
155
|
+
argument_json_schema=argument_json_schema,
|
156
|
+
auth=auth,
|
157
|
+
default_tool_vars=(
|
158
|
+
tool_vars | func.__dict__.get("__vars__", {})
|
159
|
+
),
|
160
|
+
)
|
161
|
+
)
|
156
162
|
else:
|
157
|
-
tools.append(
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
163
|
+
tools.append(
|
164
|
+
cls(
|
165
|
+
func=func,
|
166
|
+
afunc=None,
|
167
|
+
name=func.__name__,
|
168
|
+
description=func.__doc__,
|
169
|
+
argument_json_schema=argument_json_schema,
|
170
|
+
auth=auth,
|
171
|
+
default_tool_vars=(
|
172
|
+
tool_vars | func.__dict__.get("__vars__", {})
|
173
|
+
),
|
174
|
+
)
|
175
|
+
)
|
166
176
|
return tools
|
hyperpocket/tool/tool.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import abc
|
2
|
-
from typing import Optional, Type
|
2
|
+
from typing import Callable, Optional, Type
|
3
3
|
|
4
4
|
from pydantic import BaseModel, Field
|
5
5
|
|
@@ -13,23 +13,29 @@ class ToolAuth(BaseModel):
|
|
13
13
|
"""
|
14
14
|
ToolAuth is an object that represents the authentication information required to invoke a tool
|
15
15
|
"""
|
16
|
+
|
16
17
|
scopes: list[str] = Field(
|
17
18
|
default=None,
|
18
19
|
description="Indicates which authentication provider’s credentials are required to invoke the tool. "
|
19
|
-
|
20
|
+
"If auth_provider is not specified, the tool is considered to require no authentication.",
|
21
|
+
)
|
20
22
|
auth_provider: Optional[AuthProvider] = Field(
|
21
23
|
default=None,
|
22
24
|
description="Specifies which authentication handler should be used when invoking the tool. "
|
23
|
-
|
25
|
+
"If auth_handler is not specified, the default handler of the authentication provider will be used.",
|
26
|
+
)
|
24
27
|
auth_handler: Optional[str] = Field(
|
25
28
|
default=None,
|
26
29
|
description="Indicates the authentication scopes required to invoke the tool. "
|
27
|
-
|
30
|
+
"If authentication is not performed or the authentication handler is non-scoped, the value should be None.",
|
31
|
+
)
|
28
32
|
|
29
33
|
|
30
34
|
class ToolRequest(abc.ABC):
|
31
35
|
postprocessings: Optional[list[Callable]] = None
|
32
|
-
overridden_tool_vars: dict[str, str] = Field(
|
36
|
+
overridden_tool_vars: dict[str, str] = Field(
|
37
|
+
default_factory=dict, description="overridden tool variables"
|
38
|
+
)
|
33
39
|
|
34
40
|
@abc.abstractmethod
|
35
41
|
def __str__(self):
|
@@ -52,7 +58,7 @@ class ToolRequest(abc.ABC):
|
|
52
58
|
self.postprocessings.extend(postprocessings)
|
53
59
|
return self
|
54
60
|
|
55
|
-
def override_tool_variables(self, override_vars: dict[str, str]) ->
|
61
|
+
def override_tool_variables(self, override_vars: dict[str, str]) -> "ToolRequest":
|
56
62
|
self.overridden_tool_vars = override_vars
|
57
63
|
return self
|
58
64
|
|
@@ -61,14 +67,24 @@ class Tool(BaseModel, abc.ABC):
|
|
61
67
|
"""
|
62
68
|
Pocket Tool Interface
|
63
69
|
"""
|
70
|
+
|
64
71
|
name: str = Field(description="tool name")
|
65
72
|
description: str = Field(description="tool description")
|
66
|
-
argument_json_schema: Optional[dict] = Field(
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
73
|
+
argument_json_schema: Optional[dict] = Field(
|
74
|
+
default=None, description="tool argument json schema"
|
75
|
+
)
|
76
|
+
auth: Optional[ToolAuth] = Field(
|
77
|
+
default=None, description="authentication information to invoke tool"
|
78
|
+
)
|
79
|
+
postprocessings: Optional[list[Callable]] = Field(
|
80
|
+
default=None, description="postprocessing functions after tool is invoked"
|
81
|
+
)
|
82
|
+
default_tool_vars: dict[str, str] = Field(
|
83
|
+
default_factory=dict, description="default tool variables"
|
84
|
+
)
|
85
|
+
overridden_tool_vars: dict[str, str] = Field(
|
86
|
+
default_factory=dict, description="overridden tool variables"
|
87
|
+
)
|
72
88
|
use_profile: bool = False
|
73
89
|
|
74
90
|
@abc.abstractmethod
|
@@ -89,7 +105,9 @@ class Tool(BaseModel, abc.ABC):
|
|
89
105
|
Returns a schema_model that wraps the existing argument_json_schema
|
90
106
|
to include profile and thread_id as arguments when the tool is invoked
|
91
107
|
"""
|
92
|
-
return self._get_schema_model(
|
108
|
+
return self._get_schema_model(
|
109
|
+
self.name, self.argument_json_schema, use_profile=use_profile
|
110
|
+
)
|
93
111
|
|
94
112
|
def get_description(self, use_profile: bool = False) -> str:
|
95
113
|
if use_profile:
|
@@ -97,7 +115,7 @@ class Tool(BaseModel, abc.ABC):
|
|
97
115
|
else:
|
98
116
|
return self.description
|
99
117
|
|
100
|
-
def override_tool_variables(self, override_vars: dict[str, str]) ->
|
118
|
+
def override_tool_variables(self, override_vars: dict[str, str]) -> "Tool":
|
101
119
|
self.overridden_tool_vars = override_vars
|
102
120
|
return self
|
103
121
|
|
@@ -106,50 +124,55 @@ class Tool(BaseModel, abc.ABC):
|
|
106
124
|
return self.default_tool_vars | self.overridden_tool_vars
|
107
125
|
|
108
126
|
@classmethod
|
109
|
-
def from_tool_request(cls, tool_req: ToolRequest, **kwargs) ->
|
127
|
+
def from_tool_request(cls, tool_req: ToolRequest, **kwargs) -> "Tool":
|
110
128
|
from hyperpocket.tool.wasm.tool import WasmTool, WasmToolRequest
|
129
|
+
|
111
130
|
if isinstance(tool_req, WasmToolRequest):
|
112
131
|
return WasmTool.from_tool_request(tool_req, **kwargs)
|
113
|
-
raise ValueError(
|
132
|
+
raise ValueError("Unknown tool request type")
|
114
133
|
|
115
134
|
@classmethod
|
116
|
-
def _get_schema_model(
|
135
|
+
def _get_schema_model(
|
136
|
+
cls, name: str, json_schema: Optional[dict], use_profile: bool
|
137
|
+
) -> Optional[Type[BaseModel]]:
|
117
138
|
try:
|
118
139
|
if not json_schema:
|
119
140
|
pocket_logger.info(f"{name} tool's json_schema is none.")
|
120
141
|
return None
|
121
|
-
if
|
122
|
-
json_schema[
|
142
|
+
if "description" not in json_schema:
|
143
|
+
json_schema["description"] = "The argument of the tool."
|
123
144
|
|
124
145
|
if use_profile:
|
125
146
|
json_schema = {
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
147
|
+
"title": name,
|
148
|
+
"type": "object",
|
149
|
+
"properties": {
|
150
|
+
"thread_id": {
|
151
|
+
"type": "string",
|
152
|
+
"default": "default",
|
153
|
+
"description": "The ID of the chat thread where the tool is invoked. Omitted when unknown.",
|
133
154
|
},
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
155
|
+
"profile": {
|
156
|
+
"type": "string",
|
157
|
+
"default": "default",
|
158
|
+
"description": """The profile of the user invoking the tool. Inferred from user's messages.
|
138
159
|
Users can request tools to be invoked in specific personas, which is called a profile.
|
139
160
|
If the user's profile name can be inferred from the query, pass it as a string in the 'profile'
|
140
|
-
JSON property. Omitted when unknown.
|
161
|
+
JSON property. Omitted when unknown.""",
|
141
162
|
},
|
142
|
-
|
163
|
+
"body": json_schema,
|
143
164
|
},
|
144
|
-
|
145
|
-
|
146
|
-
]
|
165
|
+
"required": [
|
166
|
+
"body",
|
167
|
+
],
|
147
168
|
}
|
148
169
|
|
149
170
|
model = json_schema_to_model(json_schema, name)
|
150
171
|
return model
|
151
172
|
except Exception as e:
|
152
|
-
pocket_logger.warning(
|
173
|
+
pocket_logger.warning(
|
174
|
+
f"failed to get tool({name}) schema model. error : {e}"
|
175
|
+
)
|
153
176
|
pass
|
154
177
|
|
155
178
|
def with_postprocessing(self, postprocessing: Callable):
|
hyperpocket/tool/wasm/browser.py
CHANGED
@@ -1,11 +1,16 @@
|
|
1
1
|
import asyncio
|
2
|
-
import os
|
3
2
|
|
4
|
-
from playwright.async_api import
|
3
|
+
from playwright.async_api import (
|
4
|
+
BrowserContext,
|
5
|
+
Page,
|
6
|
+
Playwright,
|
7
|
+
Route,
|
8
|
+
async_playwright,
|
9
|
+
)
|
5
10
|
|
6
11
|
|
7
12
|
class InvokerBrowser(object):
|
8
|
-
_instance:
|
13
|
+
_instance: "InvokerBrowser" = None
|
9
14
|
_lock = asyncio.Lock()
|
10
15
|
playwright: Playwright
|
11
16
|
browser_context: BrowserContext
|
@@ -18,9 +23,9 @@ class InvokerBrowser(object):
|
|
18
23
|
self.browser_context = await self.playwright.chromium.launch_persistent_context(
|
19
24
|
headless=True,
|
20
25
|
args=[
|
21
|
-
|
26
|
+
"--disable-web-security=True",
|
22
27
|
],
|
23
|
-
user_data_dir=
|
28
|
+
user_data_dir="/tmp/chrome_dev_user",
|
24
29
|
)
|
25
30
|
|
26
31
|
@classmethod
|
@@ -44,13 +49,13 @@ class InvokerBrowser(object):
|
|
44
49
|
body=body,
|
45
50
|
headers={
|
46
51
|
**response.headers,
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
}
|
52
|
+
"Cross-Origin-Opener-Policy": "same-origin",
|
53
|
+
"Cross-Origin-Embedder-Policy": "require-corp",
|
54
|
+
"Cross-Origin-Resource-Policy": "cross-origin",
|
55
|
+
},
|
51
56
|
)
|
52
57
|
|
53
|
-
await page.route(
|
58
|
+
await page.route("**/*", _hijack_route)
|
54
59
|
return page
|
55
60
|
|
56
61
|
async def teardown(self):
|
hyperpocket/tool/wasm/invoker.py
CHANGED
@@ -7,34 +7,34 @@ from urllib.parse import urljoin
|
|
7
7
|
from hyperpocket.config import config
|
8
8
|
from hyperpocket.futures import FutureStore
|
9
9
|
from hyperpocket.tool.wasm.browser import InvokerBrowser
|
10
|
-
from hyperpocket.tool.wasm.script import ScriptRuntime, ScriptStore
|
10
|
+
from hyperpocket.tool.wasm.script import Script, ScriptRuntime, ScriptStore
|
11
11
|
from hyperpocket.tool.wasm.templates import render
|
12
12
|
|
13
13
|
|
14
14
|
class WasmInvoker(object):
|
15
|
-
def invoke(
|
16
|
-
|
17
|
-
|
18
|
-
body: Any,
|
19
|
-
envs: dict,
|
20
|
-
**kwargs) -> str:
|
15
|
+
def invoke(
|
16
|
+
self, tool_path: str, runtime: ScriptRuntime, body: Any, envs: dict, **kwargs
|
17
|
+
) -> str:
|
21
18
|
loop = asyncio.get_running_loop()
|
22
|
-
return loop.run_until_complete(
|
19
|
+
return loop.run_until_complete(
|
20
|
+
self.ainvoke(tool_path, runtime, body, envs, **kwargs)
|
21
|
+
)
|
23
22
|
|
24
|
-
async def ainvoke(
|
25
|
-
|
26
|
-
|
27
|
-
body: Any,
|
28
|
-
envs: dict,
|
29
|
-
**kwargs) -> str:
|
23
|
+
async def ainvoke(
|
24
|
+
self, tool_path: str, runtime: ScriptRuntime, body: Any, envs: dict, **kwargs
|
25
|
+
) -> str:
|
30
26
|
uid = str(uuid.uuid4())
|
31
27
|
html = render(runtime.value, uid, envs, json.dumps(body))
|
32
|
-
script = Script(
|
28
|
+
script = Script(
|
29
|
+
id=uid, tool_path=tool_path, rendered_html=html, runtime=runtime
|
30
|
+
)
|
33
31
|
ScriptStore.add_script(script=script)
|
34
32
|
future_data = FutureStore.create_future(uid=uid)
|
35
33
|
browser = await InvokerBrowser.get_instance()
|
36
34
|
page = await browser.new_page()
|
37
|
-
url = urljoin(
|
35
|
+
url = urljoin(
|
36
|
+
config().internal_base_url + "/", f"tools/wasm/scripts/{uid}/browse"
|
37
|
+
)
|
38
38
|
await page.goto(url)
|
39
39
|
stdout = await future_data.future
|
40
40
|
await page.close()
|