hyperpocket 0.0.1__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- hyperpocket/__init__.py +7 -0
- hyperpocket/auth/README.KR.md +309 -0
- hyperpocket/auth/README.md +323 -0
- hyperpocket/auth/__init__.py +24 -0
- hyperpocket/auth/calendly/__init__.py +0 -0
- hyperpocket/auth/calendly/context.py +13 -0
- hyperpocket/auth/calendly/oauth2_context.py +25 -0
- hyperpocket/auth/calendly/oauth2_handler.py +146 -0
- hyperpocket/auth/calendly/oauth2_schema.py +16 -0
- hyperpocket/auth/context.py +38 -0
- hyperpocket/auth/github/__init__.py +0 -0
- hyperpocket/auth/github/context.py +13 -0
- hyperpocket/auth/github/oauth2_context.py +25 -0
- hyperpocket/auth/github/oauth2_handler.py +143 -0
- hyperpocket/auth/github/oauth2_schema.py +16 -0
- hyperpocket/auth/github/token_context.py +12 -0
- hyperpocket/auth/github/token_handler.py +79 -0
- hyperpocket/auth/github/token_schema.py +9 -0
- hyperpocket/auth/google/__init__.py +0 -0
- hyperpocket/auth/google/context.py +15 -0
- hyperpocket/auth/google/oauth2_context.py +31 -0
- hyperpocket/auth/google/oauth2_handler.py +137 -0
- hyperpocket/auth/google/oauth2_schema.py +18 -0
- hyperpocket/auth/handler.py +171 -0
- hyperpocket/auth/linear/__init__.py +0 -0
- hyperpocket/auth/linear/context.py +15 -0
- hyperpocket/auth/linear/token_context.py +15 -0
- hyperpocket/auth/linear/token_handler.py +68 -0
- hyperpocket/auth/linear/token_schema.py +9 -0
- hyperpocket/auth/provider.py +16 -0
- hyperpocket/auth/schema.py +19 -0
- hyperpocket/auth/slack/__init__.py +0 -0
- hyperpocket/auth/slack/context.py +15 -0
- hyperpocket/auth/slack/oauth2_context.py +40 -0
- hyperpocket/auth/slack/oauth2_handler.py +151 -0
- hyperpocket/auth/slack/oauth2_schema.py +40 -0
- hyperpocket/auth/slack/tests/__init__.py +0 -0
- hyperpocket/auth/slack/tests/test_oauth2_handler.py +32 -0
- hyperpocket/auth/slack/tests/test_token_handler.py +23 -0
- hyperpocket/auth/slack/token_context.py +14 -0
- hyperpocket/auth/slack/token_handler.py +64 -0
- hyperpocket/auth/slack/token_schema.py +9 -0
- hyperpocket/auth/tests/__init__.py +0 -0
- hyperpocket/auth/tests/test_google_oauth2_handler.py +147 -0
- hyperpocket/auth/tests/test_slack_oauth2_handler.py +147 -0
- hyperpocket/auth/tests/test_slack_token_handler.py +66 -0
- hyperpocket/cli/__init__.py +0 -0
- hyperpocket/cli/__main__.py +12 -0
- hyperpocket/cli/pull.py +18 -0
- hyperpocket/cli/sync.py +17 -0
- hyperpocket/config/__init__.py +9 -0
- hyperpocket/config/auth.py +36 -0
- hyperpocket/config/git.py +17 -0
- hyperpocket/config/logger.py +81 -0
- hyperpocket/config/session.py +35 -0
- hyperpocket/config/settings.py +62 -0
- hyperpocket/constants.py +0 -0
- hyperpocket/curated_tools.py +10 -0
- hyperpocket/external/__init__.py +7 -0
- hyperpocket/external/github_client.py +19 -0
- hyperpocket/futures/__init__.py +7 -0
- hyperpocket/futures/futurestore.py +48 -0
- hyperpocket/pocket_auth.py +344 -0
- hyperpocket/pocket_main.py +351 -0
- hyperpocket/prompts.py +15 -0
- hyperpocket/repository/__init__.py +5 -0
- hyperpocket/repository/lock.py +156 -0
- hyperpocket/repository/lockfile.py +56 -0
- hyperpocket/repository/repository.py +18 -0
- hyperpocket/server/__init__.py +3 -0
- hyperpocket/server/auth/__init__.py +15 -0
- hyperpocket/server/auth/calendly.py +16 -0
- hyperpocket/server/auth/github.py +25 -0
- hyperpocket/server/auth/google.py +16 -0
- hyperpocket/server/auth/linear.py +18 -0
- hyperpocket/server/auth/slack.py +28 -0
- hyperpocket/server/auth/token.py +51 -0
- hyperpocket/server/proxy.py +63 -0
- hyperpocket/server/server.py +178 -0
- hyperpocket/server/tool/__init__.py +10 -0
- hyperpocket/server/tool/dto/__init__.py +0 -0
- hyperpocket/server/tool/dto/script.py +15 -0
- hyperpocket/server/tool/wasm.py +31 -0
- hyperpocket/session/README.KR.md +62 -0
- hyperpocket/session/README.md +61 -0
- hyperpocket/session/__init__.py +4 -0
- hyperpocket/session/in_memory.py +76 -0
- hyperpocket/session/interface.py +118 -0
- hyperpocket/session/redis.py +126 -0
- hyperpocket/session/tests/__init__.py +0 -0
- hyperpocket/session/tests/test_in_memory.py +145 -0
- hyperpocket/session/tests/test_redis.py +151 -0
- hyperpocket/tests/__init__.py +0 -0
- hyperpocket/tests/test_pocket.py +118 -0
- hyperpocket/tests/test_pocket_auth.py +982 -0
- hyperpocket/tool/README.KR.md +68 -0
- hyperpocket/tool/README.md +75 -0
- hyperpocket/tool/__init__.py +13 -0
- hyperpocket/tool/builtins/__init__.py +0 -0
- hyperpocket/tool/builtins/example/__init__.py +0 -0
- hyperpocket/tool/builtins/example/add_tool.py +18 -0
- hyperpocket/tool/function/README.KR.md +159 -0
- hyperpocket/tool/function/README.md +169 -0
- hyperpocket/tool/function/__init__.py +9 -0
- hyperpocket/tool/function/annotation.py +30 -0
- hyperpocket/tool/function/tool.py +87 -0
- hyperpocket/tool/tests/__init__.py +0 -0
- hyperpocket/tool/tests/test_function_tool.py +266 -0
- hyperpocket/tool/tool.py +106 -0
- hyperpocket/tool/wasm/README.KR.md +144 -0
- hyperpocket/tool/wasm/README.md +144 -0
- hyperpocket/tool/wasm/__init__.py +3 -0
- hyperpocket/tool/wasm/browser.py +63 -0
- hyperpocket/tool/wasm/invoker.py +41 -0
- hyperpocket/tool/wasm/script.py +82 -0
- hyperpocket/tool/wasm/templates/__init__.py +28 -0
- hyperpocket/tool/wasm/templates/node.py +87 -0
- hyperpocket/tool/wasm/templates/python.py +75 -0
- hyperpocket/tool/wasm/tool.py +147 -0
- hyperpocket/util/__init__.py +1 -0
- hyperpocket/util/extract_func_param_desc_from_docstring.py +97 -0
- hyperpocket/util/find_all_leaf_class_in_package.py +17 -0
- hyperpocket/util/find_all_subclass_in_package.py +29 -0
- hyperpocket/util/flatten_json_schema.py +45 -0
- hyperpocket/util/function_to_model.py +46 -0
- hyperpocket/util/get_objects_from_subpackage.py +28 -0
- hyperpocket/util/json_schema_to_model.py +69 -0
- hyperpocket-0.0.1.dist-info/METADATA +304 -0
- hyperpocket-0.0.1.dist-info/RECORD +131 -0
- hyperpocket-0.0.1.dist-info/WHEEL +4 -0
- hyperpocket-0.0.1.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
# Tool
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
## ToolRequest
|
6
|
+
|
7
|
+
각 Tool을 초기화할 때 필요한 정보를 담고 있는 인터페이스
|
8
|
+
|
9
|
+
## ToolAuth
|
10
|
+
|
11
|
+
ToolAuth는 Tool을 호출하기 위해 필요한 Authentication 정보를 나타내는 객체
|
12
|
+
|
13
|
+
다음과 같은 필드가 존재
|
14
|
+
|
15
|
+
- auth_provider : tool 호출 시에 어떤 authentication provider의 인증 정보가 필요한지 표시
|
16
|
+
- auth_provider가 명시되지 않는 경우, authentication이 필요없는 Tool로 인식된다.
|
17
|
+
- auth_handler : tool 호출 시 어떤 authentication handler를 사용할 것인지를 표시
|
18
|
+
- auth_handler가 명시되지 않는 경우, authentication provider의 default handler가 사용된다.
|
19
|
+
- scopes : tool 호출 시에 어떤 authentication scopes가 필요한지를 표시
|
20
|
+
- authentication을 수행하지 않거나, authentication handler가 non-scoped인 경우에는 None값을 사용
|
21
|
+
|
22
|
+
## Tool
|
23
|
+
|
24
|
+
실제 Tool을 실행하는 인터페이스
|
25
|
+
|
26
|
+
```python
|
27
|
+
class Tool(BaseModel, abc.ABC):
|
28
|
+
name: str = Field(description="tool name")
|
29
|
+
description: str = Field(description="tool description")
|
30
|
+
argument_json_schema: Optional[dict] = Field(default=None, description="tool argument json schema")
|
31
|
+
auth: Optional[ToolAuth] = Field(default=None, description="authentication information to invoke tool")
|
32
|
+
```
|
33
|
+
|
34
|
+
### schema_model
|
35
|
+
|
36
|
+
Tool.schema_model은 Tool이 호출 될 때 인자로 `profile`과 `thread_id`를 받기 위해 기존 Tool의 argument_json_schema을 wrapping한
|
37
|
+
model을 반환
|
38
|
+
|
39
|
+
이때 기존의 argument_json_schema는 `body` 필드로 이동
|
40
|
+
|
41
|
+
## How to implement
|
42
|
+
|
43
|
+
1. Tool을 상속한 클래스를 생성
|
44
|
+
- `invoke` 또는 `ainvoke` 내에서 툴 호출 시 수행할 작업을 정의
|
45
|
+
- Tool을 초기화할 factory method를 정의
|
46
|
+
- 이때 위의 필수 필드에 대한 값을 주입
|
47
|
+
|
48
|
+
2. `Pocket` 내의 `ToolLike`에 클래스를 추가(Optional)
|
49
|
+
- `WasmTool`의 경우 ToolRequest, str을 입력으로 받을 수 있음
|
50
|
+
- `FunctionTool`의 경우 Callable을 입력으로 받을 수 있음
|
51
|
+
|
52
|
+
3. `Pocket.__init__` 또는 `Pocket._load_tool` 함수 내에서 `ToolLike`값을 기반으로 Tool 초기화 수행
|
53
|
+
|
54
|
+
## Invoke Flow
|
55
|
+
|
56
|
+
1. `Pocket.invoke`를 통해 최초 Tool calling이 수행된다.
|
57
|
+
2. Tool 호출을 위해 authentication이 필요한 경우 `PocketAuth.invoke`를 통해 authentication을 수행
|
58
|
+
3. authentication이 완료된 후 `PocketAuth`에서 얻은 authentication 정보를 tool 호출 시 같이 전달
|
59
|
+
|
60
|
+
```mermaid
|
61
|
+
flowchart TD
|
62
|
+
Z["Agent"] -->|"Invoke tool"| A["Pocket"]
|
63
|
+
A["Pocket"] -->|Requires Authentication?| B{"Yes"}
|
64
|
+
B --> D["PocketAuth: Provide Authentication Info"]
|
65
|
+
D --> E["Pocket: Invoke Tool with Auth Info"]
|
66
|
+
B -->|No| F
|
67
|
+
E --> F["Tool: Perform Operations"]
|
68
|
+
```
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# Tool
|
2
|
+
|
3
|
+
---
|
4
|
+
|
5
|
+
## ToolRequest
|
6
|
+
|
7
|
+
The interface that contains information to initiate each tool
|
8
|
+
|
9
|
+
## ToolAuth
|
10
|
+
|
11
|
+
The class that contains authentication information to invoke tool
|
12
|
+
|
13
|
+
authentication information fields are:
|
14
|
+
|
15
|
+
- `auth_provider`: Indicates which authentication provider’s credentials are required to invoke the tool.
|
16
|
+
- If auth_provider is not specified, the tool is considered to require no authentication.
|
17
|
+
|
18
|
+
- `auth_handler`: Specifies which authentication handler should be used when invoking the tool.
|
19
|
+
- If auth_handler is not specified, the default handler of the authentication provider will be used.
|
20
|
+
|
21
|
+
- `scopes`: Indicates the authentication scopes required to invoke the tool.
|
22
|
+
- If authentication is not performed or the authentication handler is non-scoped, the value should be None.
|
23
|
+
|
24
|
+
## Tool
|
25
|
+
|
26
|
+
The interface to execute tool actually
|
27
|
+
|
28
|
+
```python
|
29
|
+
class Tool(BaseModel, abc.ABC):
|
30
|
+
name: str = Field(description="tool name")
|
31
|
+
description: str = Field(description="tool description")
|
32
|
+
argument_json_schema: Optional[dict] = Field(default=None, description="tool argument json schema")
|
33
|
+
auth: Optional[ToolAuth] = Field(default=None, description="authentication information to invoke tool")
|
34
|
+
```
|
35
|
+
|
36
|
+
### schema_model
|
37
|
+
|
38
|
+
`Tool.schema_model` returns a model that wraps the existing `argument_json_schema` to include `profile` and `thread_id` as
|
39
|
+
arguments when the tool is invoked.
|
40
|
+
|
41
|
+
In this process, the original `argument_json_schema` is moved under the `body` field.
|
42
|
+
|
43
|
+
## How to implement
|
44
|
+
|
45
|
+
1. Create a class that inherits from Tool
|
46
|
+
- Define the tasks to be performed when invoking the tool in invoke or ainvoke.
|
47
|
+
- Define a factory method to initialize the tool.(Optional)
|
48
|
+
- Inject values for the required fields during initialization.
|
49
|
+
|
50
|
+
2. Add the class to `ToolLike` in Pocket (Optional)
|
51
|
+
- For `WasmTool`, the input can be a ToolRequest or a str.
|
52
|
+
- For `FunctionTool`, the input can be a Callable.
|
53
|
+
|
54
|
+
3. Perform tool initialization in `Pocket.__init__` or `Pocket._load_tool`
|
55
|
+
- The initialization is based on the provided ToolLike value.
|
56
|
+
|
57
|
+
## Invoke Flow
|
58
|
+
|
59
|
+
1. The initial tool invocation occurs via `Pocket.invoke`.
|
60
|
+
2. If authentication is required for tool invocation, authentication is performed using `PocketAuth.invoke`.
|
61
|
+
3. After successful authentication, the authentication information obtained from PocketAuth is included when invoking
|
62
|
+
the tool.
|
63
|
+
|
64
|
+
```mermaid
|
65
|
+
flowchart TD
|
66
|
+
Z["Agent"] -->|"Invoke tool"| A["Pocket"]
|
67
|
+
A["Pocket"] -->|Requires Authentication?| B{"Yes"}
|
68
|
+
B --> D["PocketAuth: Provide Authentication Info"]
|
69
|
+
D --> E["Pocket: Invoke Tool with Auth Info"]
|
70
|
+
B -->|No| F
|
71
|
+
E --> F["Tool: Perform Operations"]
|
72
|
+
```
|
73
|
+
|
74
|
+
|
75
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
from hyperpocket.tool.function import from_func, function_tool
|
2
|
+
from hyperpocket.tool.tool import Tool, ToolRequest, ToolAuth
|
3
|
+
from hyperpocket.tool.wasm.tool import from_local, from_git
|
4
|
+
|
5
|
+
__all__ = [
|
6
|
+
'Tool',
|
7
|
+
'ToolAuth',
|
8
|
+
'ToolRequest',
|
9
|
+
'from_local',
|
10
|
+
'from_git',
|
11
|
+
"from_func",
|
12
|
+
"function_tool"
|
13
|
+
]
|
File without changes
|
File without changes
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# from pydantic import BaseModel, Field
|
2
|
+
#
|
3
|
+
# from pocket.tool.builtin import BuiltinTool
|
4
|
+
# from pocket.tool.tool import TAny, TModelGeneric
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# class Schema(BaseModel):
|
8
|
+
# a: int = Field(description="first number to add")
|
9
|
+
# b: int = Field(description="second number to add")
|
10
|
+
#
|
11
|
+
#
|
12
|
+
# class AddTool(BuiltinTool):
|
13
|
+
# name: str = "add_two_number_tool"
|
14
|
+
# description: str = "add two numbers"
|
15
|
+
# argument_schema: TModelGeneric = Schema
|
16
|
+
#
|
17
|
+
# def invoke(self, a: int, b: int) -> TAny:
|
18
|
+
# return a + b
|
@@ -0,0 +1,159 @@
|
|
1
|
+
# Function
|
2
|
+
|
3
|
+
python 함수를 tool로 등록해 사용
|
4
|
+
|
5
|
+
---
|
6
|
+
|
7
|
+
## How To Use
|
8
|
+
|
9
|
+
### No Auth
|
10
|
+
|
11
|
+
python method를 pocket tool에 그대로 입력
|
12
|
+
|
13
|
+
```python
|
14
|
+
from hyperpocket import Pocket
|
15
|
+
|
16
|
+
pocket = Pocket(tools=[
|
17
|
+
my_function]
|
18
|
+
)
|
19
|
+
```
|
20
|
+
|
21
|
+
### Auth
|
22
|
+
|
23
|
+
python method에 `@function_tool`를 사용해 필요한 authentication provider를 명시
|
24
|
+
|
25
|
+
```python
|
26
|
+
from hyperpocket import Pocket
|
27
|
+
from hyperpocket.auth import AuthProvider
|
28
|
+
from hyperpocket.tool import function_tool
|
29
|
+
|
30
|
+
|
31
|
+
@function_tool(
|
32
|
+
auth_provider=AuthProvider.SLACK,
|
33
|
+
scopes=["channels:history", "im:history", "mpim:history", "groups:history", "reactions:read", "mpim:read",
|
34
|
+
"im:read"]
|
35
|
+
)
|
36
|
+
def my_function():
|
37
|
+
pass
|
38
|
+
|
39
|
+
|
40
|
+
pocket = Pocket(
|
41
|
+
tools=[
|
42
|
+
my_function
|
43
|
+
]
|
44
|
+
)
|
45
|
+
```
|
46
|
+
|
47
|
+
`from_function`을 통해 pocket을 초기화할 때에만 일시적으로 `FunctionTool`로 변환해 넣어줄 수 있다.
|
48
|
+
|
49
|
+
```python
|
50
|
+
from hyperpocket import Pocket
|
51
|
+
from hyperpocket.auth import AuthProvider
|
52
|
+
from hyperpocket.tool import from_func
|
53
|
+
from hyperpocket.tool.tool import ToolAuth
|
54
|
+
|
55
|
+
Pocket(tools=[
|
56
|
+
from_func(
|
57
|
+
func=my_function,
|
58
|
+
auth=ToolAuth(
|
59
|
+
auth_provider=AuthProvider.SLACK,
|
60
|
+
scopes=["channels:history", "im:history", "mpim:history", "groups:history", "reactions:read", "mpim:read",
|
61
|
+
"im:read"]
|
62
|
+
)
|
63
|
+
)
|
64
|
+
])
|
65
|
+
```
|
66
|
+
|
67
|
+
authentication access token은 python 함수의 variable keyword로 매핑되어 전달된다.
|
68
|
+
|
69
|
+
```python
|
70
|
+
from hyperpocket.tool import function_tool
|
71
|
+
from hyperpocket.auth import AuthProvider
|
72
|
+
|
73
|
+
|
74
|
+
@function_tool(
|
75
|
+
auth_provider=AuthProvider.SLACK
|
76
|
+
)
|
77
|
+
def my_function(**kwargs):
|
78
|
+
token = kwargs["SLACK_BOT_TOKEN"]
|
79
|
+
|
80
|
+
# ...
|
81
|
+
```
|
82
|
+
|
83
|
+
- 각 provider 별 mapping key값은 각 provider의 `_ACCESS_TOKEN_KEY` 필드를 확인
|
84
|
+
|
85
|
+
## Docstring parsing
|
86
|
+
|
87
|
+
docstring을 parsing해 argument 의미 파악
|
88
|
+
|
89
|
+
다음 docstring style을 지원
|
90
|
+
|
91
|
+
- google style
|
92
|
+
- sphinx
|
93
|
+
- javadoc
|
94
|
+
- plain
|
95
|
+
|
96
|
+
### Google Style
|
97
|
+
|
98
|
+
```python
|
99
|
+
def my_function(a: int, b: int):
|
100
|
+
"""
|
101
|
+
My Function
|
102
|
+
|
103
|
+
Args:
|
104
|
+
a(int): first argument
|
105
|
+
b(int): second argument
|
106
|
+
"""
|
107
|
+
pass
|
108
|
+
```
|
109
|
+
|
110
|
+
### Sphinx Style
|
111
|
+
|
112
|
+
```python
|
113
|
+
def my_function(a: int, b: int):
|
114
|
+
"""
|
115
|
+
My Function
|
116
|
+
|
117
|
+
:param a: first argument
|
118
|
+
:param b: second argument
|
119
|
+
"""
|
120
|
+
pass
|
121
|
+
```
|
122
|
+
|
123
|
+
## Javadoc Style
|
124
|
+
|
125
|
+
```python
|
126
|
+
def my_function(a: int, b: int):
|
127
|
+
"""
|
128
|
+
My Function
|
129
|
+
|
130
|
+
@param a: first argument
|
131
|
+
@param b: second argument
|
132
|
+
"""
|
133
|
+
pass
|
134
|
+
```
|
135
|
+
|
136
|
+
## Other Style
|
137
|
+
|
138
|
+
```python
|
139
|
+
def my_function(a: int, b: int):
|
140
|
+
"""
|
141
|
+
My Function
|
142
|
+
|
143
|
+
@arg a: first argument
|
144
|
+
@arg b: second argument
|
145
|
+
|
146
|
+
or
|
147
|
+
|
148
|
+
:arg a: first argument
|
149
|
+
:arg b: second argument
|
150
|
+
"""
|
151
|
+
pass
|
152
|
+
```
|
153
|
+
|
154
|
+
하나의 docstring에서 여러 style이 혼용되는 경우는 고려하지 않음
|
155
|
+
|
156
|
+
## Limits
|
157
|
+
|
158
|
+
- input type은 python builtin type과 pydantic만 지원
|
159
|
+
|
@@ -0,0 +1,169 @@
|
|
1
|
+
# Function
|
2
|
+
|
3
|
+
Register and use Python functions as tools.
|
4
|
+
|
5
|
+
---
|
6
|
+
|
7
|
+
## How To Use
|
8
|
+
|
9
|
+
### No Authentication Required
|
10
|
+
|
11
|
+
Directly pass Python methods into the pocket tool.
|
12
|
+
|
13
|
+
```python
|
14
|
+
from hyperpocket import Pocket
|
15
|
+
|
16
|
+
pocket = Pocket(tools=[
|
17
|
+
my_function
|
18
|
+
])
|
19
|
+
```
|
20
|
+
|
21
|
+
### With Authentication
|
22
|
+
|
23
|
+
Use the `@function_tool` decorator to specify the required authentication provider for a Python method.
|
24
|
+
|
25
|
+
```python
|
26
|
+
from hyperpocket import Pocket
|
27
|
+
from hyperpocket.auth import AuthProvider
|
28
|
+
from hyperpocket.tool import function_tool
|
29
|
+
|
30
|
+
|
31
|
+
@function_tool(
|
32
|
+
auth_provider=AuthProvider.SLACK,
|
33
|
+
scopes=["channels:history", "im:history", "mpim:history", "groups:history", "reactions:read", "mpim:read",
|
34
|
+
"im:read"]
|
35
|
+
)
|
36
|
+
def my_function():
|
37
|
+
pass
|
38
|
+
|
39
|
+
|
40
|
+
pocket = Pocket(
|
41
|
+
tools=[
|
42
|
+
my_function
|
43
|
+
]
|
44
|
+
)
|
45
|
+
```
|
46
|
+
|
47
|
+
You can temporarily convert a function into a FunctionTool when initializing a Pocket instance using `from_func`.
|
48
|
+
|
49
|
+
```python
|
50
|
+
from hyperpocket import Pocket
|
51
|
+
from hyperpocket.auth import AuthProvider
|
52
|
+
from hyperpocket.tool import from_func
|
53
|
+
from hyperpocket.tool.tool import ToolAuth
|
54
|
+
|
55
|
+
Pocket(tools=[
|
56
|
+
from_func(
|
57
|
+
func=my_function,
|
58
|
+
auth=ToolAuth(
|
59
|
+
auth_provider=AuthProvider.SLACK,
|
60
|
+
scopes=["channels:history", "im:history", "mpim:history", "groups:history", "reactions:read", "mpim:read",
|
61
|
+
"im:read"]
|
62
|
+
)
|
63
|
+
)
|
64
|
+
])
|
65
|
+
```
|
66
|
+
|
67
|
+
Authentication access tokens are passed to the Python function as __variable keyword__ arguments.
|
68
|
+
|
69
|
+
```python
|
70
|
+
from hyperpocket.tool import function_tool
|
71
|
+
from hyperpocket.auth import AuthProvider
|
72
|
+
|
73
|
+
|
74
|
+
@function_tool(
|
75
|
+
auth_provider=AuthProvider.SLACK
|
76
|
+
)
|
77
|
+
def my_function(**kwargs):
|
78
|
+
token = kwargs["SLACK_BOT_TOKEN"]
|
79
|
+
|
80
|
+
# ...
|
81
|
+
```
|
82
|
+
|
83
|
+
- Check the `_ACCESS_TOKEN_KEY` field of each provider for the mapping key of their access tokens.
|
84
|
+
|
85
|
+
## Docstring Parsing
|
86
|
+
|
87
|
+
Parse docstrings to understand the meanings of arguments.
|
88
|
+
|
89
|
+
The following docstring styles are supported:
|
90
|
+
|
91
|
+
- Google Style
|
92
|
+
- Sphinx
|
93
|
+
- Javadoc
|
94
|
+
- Plain
|
95
|
+
|
96
|
+
### Google Style
|
97
|
+
|
98
|
+
```python
|
99
|
+
def my_function(a: int, b: int):
|
100
|
+
"""
|
101
|
+
My Function
|
102
|
+
|
103
|
+
Args:
|
104
|
+
a (int): First argument
|
105
|
+
b (int): Second argument
|
106
|
+
"""
|
107
|
+
pass
|
108
|
+
```
|
109
|
+
|
110
|
+
### Sphinx Style
|
111
|
+
|
112
|
+
```python
|
113
|
+
def my_function(a: int, b: int):
|
114
|
+
"""
|
115
|
+
My Function
|
116
|
+
|
117
|
+
:param a: First argument
|
118
|
+
:param b: Second argument
|
119
|
+
"""
|
120
|
+
pass
|
121
|
+
```
|
122
|
+
|
123
|
+
### Javadoc Style
|
124
|
+
|
125
|
+
```python
|
126
|
+
def my_function(a: int, b: int):
|
127
|
+
"""
|
128
|
+
My Function
|
129
|
+
|
130
|
+
@param a: First argument
|
131
|
+
@param b: Second argument
|
132
|
+
"""
|
133
|
+
pass
|
134
|
+
```
|
135
|
+
|
136
|
+
### Other Style
|
137
|
+
|
138
|
+
```python
|
139
|
+
def my_function(a: int, b: int):
|
140
|
+
"""
|
141
|
+
My Function
|
142
|
+
|
143
|
+
@arg a: First argument
|
144
|
+
@arg b: Second argument
|
145
|
+
|
146
|
+
or
|
147
|
+
|
148
|
+
:arg a: First argument
|
149
|
+
:arg b: Second argument
|
150
|
+
"""
|
151
|
+
pass
|
152
|
+
```
|
153
|
+
|
154
|
+
### Plain
|
155
|
+
|
156
|
+
```python
|
157
|
+
def my_function(a: int, b: int):
|
158
|
+
"""
|
159
|
+
My Function
|
160
|
+
|
161
|
+
a(int): First argument
|
162
|
+
b(int): Second argument
|
163
|
+
"""
|
164
|
+
pass
|
165
|
+
```
|
166
|
+
|
167
|
+
## Limitations
|
168
|
+
|
169
|
+
Input types are limited to Python built-in types and Pydantic.
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from typing import List, Callable, Optional
|
2
|
+
|
3
|
+
from hyperpocket.auth import AuthProvider
|
4
|
+
from hyperpocket.tool.function.tool import FunctionTool
|
5
|
+
from hyperpocket.tool.tool import ToolAuth
|
6
|
+
|
7
|
+
|
8
|
+
def function_tool(func: Optional[Callable] = None, *, auth_provider: AuthProvider = None, scopes: List[str] = None,
|
9
|
+
auth_handler: str = None):
|
10
|
+
def decorator(inner_func: Callable):
|
11
|
+
if not callable(inner_func):
|
12
|
+
raise ValueError("FunctionTool can only be created from a callable")
|
13
|
+
auth = None
|
14
|
+
if auth_provider is not None:
|
15
|
+
auth = ToolAuth(
|
16
|
+
auth_provider=auth_provider,
|
17
|
+
scopes=scopes if scopes else [],
|
18
|
+
auth_handler=auth_handler
|
19
|
+
)
|
20
|
+
|
21
|
+
return FunctionTool.from_func(
|
22
|
+
func=inner_func,
|
23
|
+
auth=auth,
|
24
|
+
)
|
25
|
+
|
26
|
+
if func is not None:
|
27
|
+
return decorator(func)
|
28
|
+
|
29
|
+
# return FunctionTool
|
30
|
+
return decorator
|
@@ -0,0 +1,87 @@
|
|
1
|
+
import copy
|
2
|
+
import inspect
|
3
|
+
from typing import Callable, Awaitable, Optional
|
4
|
+
|
5
|
+
from pydantic import BaseModel
|
6
|
+
|
7
|
+
from hyperpocket.tool.tool import Tool, ToolAuth
|
8
|
+
from hyperpocket.util.flatten_json_schema import flatten_json_schema
|
9
|
+
from hyperpocket.util.function_to_model import function_to_model
|
10
|
+
|
11
|
+
|
12
|
+
class FunctionTool(Tool):
|
13
|
+
"""
|
14
|
+
FunctionTool is Tool executing local python method.
|
15
|
+
"""
|
16
|
+
func: Callable
|
17
|
+
afunc: Optional[Callable[..., Awaitable[str]]]
|
18
|
+
|
19
|
+
def invoke(self, **kwargs) -> str:
|
20
|
+
binding_args = self._get_binding_args(kwargs)
|
21
|
+
return str(self.func(**binding_args))
|
22
|
+
|
23
|
+
async def ainvoke(self, **kwargs) -> str:
|
24
|
+
binding_args = self._get_binding_args(kwargs)
|
25
|
+
if self.afunc is None:
|
26
|
+
return str(self.func(**binding_args))
|
27
|
+
return str(await self.afunc(**binding_args))
|
28
|
+
|
29
|
+
def _get_binding_args(self, kwargs):
|
30
|
+
_kwargs = copy.deepcopy(kwargs)
|
31
|
+
|
32
|
+
# make body args to model
|
33
|
+
schema_model = self.schema_model()
|
34
|
+
model = schema_model(body=_kwargs["body"])
|
35
|
+
_kwargs.pop("body")
|
36
|
+
|
37
|
+
# body model to dict
|
38
|
+
args = self.model_to_kwargs(model.body)
|
39
|
+
|
40
|
+
# binding args
|
41
|
+
binding_args = {}
|
42
|
+
sig = inspect.signature(self.func)
|
43
|
+
for param_name, param in sig.parameters.items():
|
44
|
+
if param_name not in args:
|
45
|
+
continue
|
46
|
+
|
47
|
+
if param.kind == param.VAR_KEYWORD:
|
48
|
+
# var keyword args should be passed by plain dict
|
49
|
+
binding_args |= args[param_name]
|
50
|
+
binding_args |= _kwargs.get("envs", {})
|
51
|
+
|
52
|
+
if "envs" in _kwargs:
|
53
|
+
_kwargs.pop("envs")
|
54
|
+
|
55
|
+
binding_args |= _kwargs # add other kwargs
|
56
|
+
continue
|
57
|
+
|
58
|
+
binding_args[param_name] = args[param_name]
|
59
|
+
|
60
|
+
return binding_args
|
61
|
+
|
62
|
+
@staticmethod
|
63
|
+
def model_to_kwargs(model: BaseModel) -> dict:
|
64
|
+
kwargs = {}
|
65
|
+
for field, value in model.model_fields.items():
|
66
|
+
attr_value = getattr(model, field)
|
67
|
+
kwargs[field] = attr_value
|
68
|
+
return kwargs
|
69
|
+
|
70
|
+
@classmethod
|
71
|
+
def from_func(
|
72
|
+
cls,
|
73
|
+
func: Callable,
|
74
|
+
afunc: Callable[..., Awaitable[str]] = None,
|
75
|
+
auth: Optional[ToolAuth] = None
|
76
|
+
) -> "FunctionTool":
|
77
|
+
model = function_to_model(func)
|
78
|
+
argument_json_schema = flatten_json_schema(model.model_json_schema())
|
79
|
+
|
80
|
+
return cls(
|
81
|
+
func=func,
|
82
|
+
afunc=afunc,
|
83
|
+
name=func.__name__,
|
84
|
+
description=model.__doc__,
|
85
|
+
argument_json_schema=argument_json_schema,
|
86
|
+
auth=auth
|
87
|
+
)
|
File without changes
|