hyperpocket 0.1.9__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.
- 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()
|