versionhq 1.1.9.2__tar.gz → 1.1.9.3__tar.gz
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.
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/.gitignore +3 -1
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/PKG-INFO +5 -2
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/README.md +1 -1
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/pyproject.toml +4 -1
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/requirements.txt +70 -5
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/__init__.py +3 -3
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/tool/__init__.py +7 -4
- versionhq-1.1.9.3/src/versionhq/tool/composio_tool.py +191 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq.egg-info/PKG-INFO +5 -2
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq.egg-info/SOURCES.txt +1 -1
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq.egg-info/requires.txt +3 -0
- versionhq-1.1.9.3/tests/tool/composio_test.py +37 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/uv.lock +355 -29
- versionhq-1.1.9.2/src/versionhq/tool/composio.py +0 -161
- versionhq-1.1.9.2/tests/tool/composio_test.py +0 -19
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/.github/workflows/publish.yml +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/.github/workflows/publish_testpypi.yml +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/.github/workflows/run_tests.yml +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/.github/workflows/security_check.yml +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/.pre-commit-config.yaml +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/.python-version +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/LICENSE +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/SECURITY.md +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/db/preprocess.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/requirements-dev.txt +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/runtime.txt +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/setup.cfg +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/_utils/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/_utils/cache_handler.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/_utils/i18n.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/_utils/logger.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/_utils/process_config.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/_utils/rpm_controller.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/_utils/usage_metrics.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/agent/TEMPLATES/Backstory.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/agent/TEMPLATES/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/agent/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/agent/model.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/agent/parser.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/cli/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/clients/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/clients/customer/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/clients/customer/model.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/clients/product/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/clients/product/model.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/clients/workflow/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/clients/workflow/model.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/llm/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/llm/llm_vars.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/llm/model.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/storage/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/storage/task_output_storage.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/task/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/task/formatter.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/task/log_handler.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/task/model.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/team/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/team/model.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/team/team_planner.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/tool/decorator.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/tool/model.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq/tool/tool_handler.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq.egg-info/dependency_links.txt +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/src/versionhq.egg-info/top_level.txt +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/agent/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/agent/agent_test.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/cli/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/clients/product_test.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/clients/workflow_test.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/conftest.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/task/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/task/task_test.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/team/Prompts/Demo_test.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/team/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/team/team_test.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/tool/__init__.py +0 -0
- {versionhq-1.1.9.2 → versionhq-1.1.9.3}/tests/tool/tool_test.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: versionhq
|
3
|
-
Version: 1.1.9.
|
3
|
+
Version: 1.1.9.3
|
4
4
|
Summary: LLM orchestration frameworks for model-agnostic AI agents that handle complex outbound workflows
|
5
5
|
Author-email: Kuriko Iwai <kuriko@versi0n.io>
|
6
6
|
License: MIT License
|
@@ -52,12 +52,15 @@ Requires-Dist: setuptools>=75.6.0
|
|
52
52
|
Requires-Dist: wheel>=0.45.1
|
53
53
|
Requires-Dist: python-dotenv>=1.0.0
|
54
54
|
Requires-Dist: appdirs>=1.4.4
|
55
|
+
Requires-Dist: langchain>=0.3.14
|
56
|
+
Requires-Dist: langchain-openai>=0.2.14
|
57
|
+
Requires-Dist: composio-langchain>=0.6.12
|
55
58
|
|
56
59
|
# Overview
|
57
60
|
|
58
61
|

|
59
62
|
[](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
|
60
|
-

|
61
64
|

|
62
65
|

|
63
66
|
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|

|
4
4
|
[](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
|
5
|
-

|
6
6
|

|
7
7
|

|
8
8
|
|
@@ -15,7 +15,7 @@ exclude = ["test*", "__pycache__"]
|
|
15
15
|
|
16
16
|
[project]
|
17
17
|
name = "versionhq"
|
18
|
-
version = "1.1.9.
|
18
|
+
version = "1.1.9.3"
|
19
19
|
authors = [{ name = "Kuriko Iwai", email = "kuriko@versi0n.io" }]
|
20
20
|
description = "LLM orchestration frameworks for model-agnostic AI agents that handle complex outbound workflows"
|
21
21
|
readme = "README.md"
|
@@ -37,6 +37,9 @@ dependencies = [
|
|
37
37
|
"wheel>=0.45.1",
|
38
38
|
"python-dotenv>=1.0.0",
|
39
39
|
"appdirs>=1.4.4",
|
40
|
+
"langchain>=0.3.14",
|
41
|
+
"langchain-openai>=0.2.14",
|
42
|
+
"composio-langchain>=0.6.12"
|
40
43
|
]
|
41
44
|
classifiers = [
|
42
45
|
"Programming Language :: Python",
|
@@ -5,6 +5,7 @@ aiohappyeyeballs==2.4.4
|
|
5
5
|
aiohttp==3.11.11
|
6
6
|
# via
|
7
7
|
# composio-core
|
8
|
+
# langchain
|
8
9
|
# litellm
|
9
10
|
aiosignal==1.3.2
|
10
11
|
# via aiohttp
|
@@ -43,8 +44,12 @@ click==8.1.8
|
|
43
44
|
# uvicorn
|
44
45
|
composio==0.1.0
|
45
46
|
# via versionhq (pyproject.toml)
|
46
|
-
composio-core==0.6.
|
47
|
-
# via
|
47
|
+
composio-core==0.6.11.post1
|
48
|
+
# via
|
49
|
+
# composio-langchain
|
50
|
+
# composio-openai
|
51
|
+
composio-langchain==0.6.12
|
52
|
+
# via versionhq (pyproject.toml)
|
48
53
|
composio-openai==0.6.9
|
49
54
|
# via versionhq (pyproject.toml)
|
50
55
|
cryptography==44.0.0
|
@@ -69,6 +74,7 @@ httpcore==1.0.7
|
|
69
74
|
# via httpx
|
70
75
|
httpx==0.27.2
|
71
76
|
# via
|
77
|
+
# langsmith
|
72
78
|
# litellm
|
73
79
|
# openai
|
74
80
|
huggingface-hub==0.27.0
|
@@ -91,6 +97,10 @@ jiter==0.8.2
|
|
91
97
|
# via openai
|
92
98
|
json-repair==0.35.0
|
93
99
|
# via versionhq (pyproject.toml)
|
100
|
+
jsonpatch==1.33
|
101
|
+
# via langchain-core
|
102
|
+
jsonpointer==3.0.0
|
103
|
+
# via jsonpatch
|
94
104
|
jsonref==1.1.0
|
95
105
|
# via composio-core
|
96
106
|
jsonschema==4.23.0
|
@@ -99,6 +109,27 @@ jsonschema==4.23.0
|
|
99
109
|
# litellm
|
100
110
|
jsonschema-specifications==2024.10.1
|
101
111
|
# via jsonschema
|
112
|
+
langchain==0.3.14
|
113
|
+
# via
|
114
|
+
# versionhq (pyproject.toml)
|
115
|
+
# composio-langchain
|
116
|
+
langchain-core==0.3.29
|
117
|
+
# via
|
118
|
+
# langchain
|
119
|
+
# langchain-openai
|
120
|
+
# langchain-text-splitters
|
121
|
+
langchain-openai==0.2.14
|
122
|
+
# via
|
123
|
+
# versionhq (pyproject.toml)
|
124
|
+
# composio-langchain
|
125
|
+
langchain-text-splitters==0.3.4
|
126
|
+
# via langchain
|
127
|
+
langchainhub==0.1.21
|
128
|
+
# via composio-langchain
|
129
|
+
langsmith==0.2.10
|
130
|
+
# via
|
131
|
+
# langchain
|
132
|
+
# langchain-core
|
102
133
|
litellm==1.56.5
|
103
134
|
# via versionhq (pyproject.toml)
|
104
135
|
markdown-it-py==3.0.0
|
@@ -113,13 +144,21 @@ multidict==6.1.0
|
|
113
144
|
# via
|
114
145
|
# aiohttp
|
115
146
|
# yarl
|
147
|
+
numpy==2.2.1
|
148
|
+
# via langchain
|
116
149
|
openai==1.58.1
|
117
150
|
# via
|
118
151
|
# versionhq (pyproject.toml)
|
119
152
|
# composio-openai
|
153
|
+
# langchain-openai
|
120
154
|
# litellm
|
155
|
+
orjson==3.10.13
|
156
|
+
# via langsmith
|
121
157
|
packaging==24.2
|
122
|
-
# via
|
158
|
+
# via
|
159
|
+
# huggingface-hub
|
160
|
+
# langchain-core
|
161
|
+
# langchainhub
|
123
162
|
paramiko==3.5.0
|
124
163
|
# via composio-core
|
125
164
|
propcache==0.2.1
|
@@ -132,7 +171,11 @@ pydantic==2.10.4
|
|
132
171
|
# via
|
133
172
|
# versionhq (pyproject.toml)
|
134
173
|
# composio-core
|
174
|
+
# composio-langchain
|
135
175
|
# fastapi
|
176
|
+
# langchain
|
177
|
+
# langchain-core
|
178
|
+
# langsmith
|
136
179
|
# litellm
|
137
180
|
# openai
|
138
181
|
pydantic-core==2.27.2
|
@@ -150,7 +193,10 @@ python-dotenv==1.0.1
|
|
150
193
|
# versionhq (pyproject.toml)
|
151
194
|
# litellm
|
152
195
|
pyyaml==6.0.2
|
153
|
-
# via
|
196
|
+
# via
|
197
|
+
# huggingface-hub
|
198
|
+
# langchain
|
199
|
+
# langchain-core
|
154
200
|
referencing==0.35.1
|
155
201
|
# via
|
156
202
|
# jsonschema
|
@@ -164,8 +210,14 @@ requests==2.32.3
|
|
164
210
|
# versionhq (pyproject.toml)
|
165
211
|
# composio-core
|
166
212
|
# huggingface-hub
|
213
|
+
# langchain
|
214
|
+
# langchainhub
|
215
|
+
# langsmith
|
167
216
|
# pysher
|
217
|
+
# requests-toolbelt
|
168
218
|
# tiktoken
|
219
|
+
requests-toolbelt==1.0.0
|
220
|
+
# via langsmith
|
169
221
|
rich==13.9.4
|
170
222
|
# via composio-core
|
171
223
|
rpds-py==0.22.3
|
@@ -183,29 +235,42 @@ sniffio==1.3.1
|
|
183
235
|
# anyio
|
184
236
|
# httpx
|
185
237
|
# openai
|
238
|
+
sqlalchemy==2.0.36
|
239
|
+
# via langchain
|
186
240
|
starlette==0.41.3
|
187
241
|
# via fastapi
|
242
|
+
tenacity==9.0.0
|
243
|
+
# via
|
244
|
+
# langchain
|
245
|
+
# langchain-core
|
188
246
|
tiktoken==0.8.0
|
189
|
-
# via
|
247
|
+
# via
|
248
|
+
# langchain-openai
|
249
|
+
# litellm
|
190
250
|
tokenizers==0.21.0
|
191
251
|
# via litellm
|
192
252
|
tqdm==4.67.1
|
193
253
|
# via
|
194
254
|
# huggingface-hub
|
195
255
|
# openai
|
256
|
+
types-requests==2.32.0.20241016
|
257
|
+
# via langchainhub
|
196
258
|
typing==3.10.0.0
|
197
259
|
# via versionhq (pyproject.toml)
|
198
260
|
typing-extensions==4.12.2
|
199
261
|
# via
|
200
262
|
# fastapi
|
201
263
|
# huggingface-hub
|
264
|
+
# langchain-core
|
202
265
|
# openai
|
203
266
|
# pydantic
|
204
267
|
# pydantic-core
|
268
|
+
# sqlalchemy
|
205
269
|
urllib3==2.3.0
|
206
270
|
# via
|
207
271
|
# requests
|
208
272
|
# sentry-sdk
|
273
|
+
# types-requests
|
209
274
|
uvicorn==0.34.0
|
210
275
|
# via composio-core
|
211
276
|
websocket-client==1.8.0
|
@@ -15,10 +15,10 @@ from versionhq.llm.model import LLM
|
|
15
15
|
from versionhq.task.model import Task, TaskOutput
|
16
16
|
from versionhq.team.model import Team, TeamOutput
|
17
17
|
from versionhq.tool.model import Tool
|
18
|
-
from versionhq.tool.
|
18
|
+
from versionhq.tool.composio_tool import ComposioHandler
|
19
19
|
|
20
20
|
|
21
|
-
__version__ = "1.1.9.
|
21
|
+
__version__ = "1.1.9.3"
|
22
22
|
__all__ = [
|
23
23
|
"Agent",
|
24
24
|
"Customer",
|
@@ -34,5 +34,5 @@ __all__ = [
|
|
34
34
|
"Team",
|
35
35
|
"TeamOutput",
|
36
36
|
"Tool",
|
37
|
-
"
|
37
|
+
"ComposioHandler"
|
38
38
|
]
|
@@ -40,10 +40,11 @@ composio_app_set = [
|
|
40
40
|
(ComposioAppName.LINKEDIN, ComposioAuthScheme.OAUTH2),
|
41
41
|
]
|
42
42
|
|
43
|
-
class
|
44
|
-
INITIATED = "
|
45
|
-
ACTIVE = "
|
46
|
-
FAILED = "
|
43
|
+
class ComposioStatus(str, Enum):
|
44
|
+
INITIATED = "INITIATED"
|
45
|
+
ACTIVE = "ACTIVE"
|
46
|
+
FAILED = "FAILED"
|
47
|
+
|
47
48
|
|
48
49
|
|
49
50
|
|
@@ -51,3 +52,5 @@ class ComposioAction(str, Enum):
|
|
51
52
|
"""
|
52
53
|
Enum to store composio's action that can be called via `Actions.xxx`
|
53
54
|
"""
|
55
|
+
# HUBSPOT_INITIATE_DATA_IMPORT_PROCESS = "hubspot_initate_date_import_process"
|
56
|
+
HUBSPOT_CREATE_PIPELINE_STAGE = "hubspot_create_pipeline_stage"
|
@@ -0,0 +1,191 @@
|
|
1
|
+
import os
|
2
|
+
import uuid
|
3
|
+
from abc import ABC
|
4
|
+
from dotenv import load_dotenv
|
5
|
+
from typing import Any, Callable, Type, get_args, get_origin, Optional, Tuple, Dict
|
6
|
+
from typing_extensions import Self
|
7
|
+
|
8
|
+
from pydantic import BaseModel, Field, model_validator, field_validator, UUID4, PrivateAttr
|
9
|
+
from pydantic_core import PydanticCustomError
|
10
|
+
|
11
|
+
from composio import ComposioToolSet
|
12
|
+
from composio_langchain import action
|
13
|
+
|
14
|
+
from versionhq.tool import ComposioAppName, ComposioAuthScheme, composio_app_set, ComposioStatus, ComposioAction
|
15
|
+
from versionhq._utils.logger import Logger
|
16
|
+
from versionhq._utils.cache_handler import CacheHandler
|
17
|
+
|
18
|
+
load_dotenv(override=True)
|
19
|
+
|
20
|
+
DEFAULT_REDIRECT_URL = os.environ.get("DEFAULT_REDIRECT_URL", None)
|
21
|
+
DEFAULT_USER_ID = os.environ.get("DEFAULT_USER_ID", None)
|
22
|
+
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", None)
|
23
|
+
|
24
|
+
|
25
|
+
class ComposioHandler(ABC, BaseModel):
|
26
|
+
"""
|
27
|
+
A class to handle connecting account with Composio and executing actions using Composio ecosystem.
|
28
|
+
`connected_account_id` is set up per `app_name` to call the actions on the given app. i.e., salesforce
|
29
|
+
"""
|
30
|
+
|
31
|
+
_logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=True))
|
32
|
+
_cache: CacheHandler = PrivateAttr(default_factory=lambda: CacheHandler())
|
33
|
+
|
34
|
+
id: UUID4 = Field(default_factory=uuid.uuid4, frozen=True)
|
35
|
+
app_name: str = Field(default=ComposioAppName.HUBSPOT, max_length=128, description="app name defined by composio")
|
36
|
+
user_id: str = Field(default=DEFAULT_USER_ID, description="composio entity id")
|
37
|
+
auth_scheme: str = Field(default=ComposioAuthScheme.OAUTH2)
|
38
|
+
redirect_url: str = Field(default=DEFAULT_REDIRECT_URL, description="redirect url after successful oauth2 connection")
|
39
|
+
connected_account_id: str = Field(
|
40
|
+
default=None,
|
41
|
+
description="store the client id generated by composio after auth validation. use the id to connect with a given app and execute composio actions"
|
42
|
+
)
|
43
|
+
tools: Any = Field(default=None, descritpion="retrieved composio tools")
|
44
|
+
|
45
|
+
@field_validator("id", mode="before")
|
46
|
+
@classmethod
|
47
|
+
def _deny_user_set_id(cls, v: Optional[UUID4]) -> None:
|
48
|
+
if v:
|
49
|
+
raise PydanticCustomError("may_not_set_field", "This field is not to be set by the user.", {})
|
50
|
+
|
51
|
+
|
52
|
+
@model_validator(mode="after")
|
53
|
+
def validate_app_name(self):
|
54
|
+
if self.app_name not in ComposioAppName:
|
55
|
+
raise PydanticCustomError("no_app_name", f"The given app name {self.app_name} is not valid.", {})
|
56
|
+
|
57
|
+
return self
|
58
|
+
|
59
|
+
|
60
|
+
@model_validator(mode="after")
|
61
|
+
def validate_auth_scheme(self):
|
62
|
+
"""
|
63
|
+
Raise error when the client uses auth scheme unavailable for the app.
|
64
|
+
"""
|
65
|
+
app_set = next(filter(lambda tup: self.app_name in tup, composio_app_set), None)
|
66
|
+
if not app_set:
|
67
|
+
raise PydanticCustomError("no_app_set", f"The app set of {self.app_name} is missing.", {})
|
68
|
+
|
69
|
+
else:
|
70
|
+
acceptable_auth_scheme = next(filter(lambda item: self.auth_scheme in item, app_set), None)
|
71
|
+
if acceptable_auth_scheme is None:
|
72
|
+
raise PydanticCustomError("invalid_auth_scheme", f"The app {self.app_name} must have different auth_scheme.", {})
|
73
|
+
|
74
|
+
return self
|
75
|
+
|
76
|
+
|
77
|
+
def _setup_langchain_toolset(self, metadata: Dict[str, Any] = dict()):
|
78
|
+
"""
|
79
|
+
Composio toolset on LangChain for action execution using LLM.
|
80
|
+
"""
|
81
|
+
from composio_langchain import ComposioToolSet
|
82
|
+
return ComposioToolSet(api_key=os.environ.get("COMPOSIO_API_KEY"), metadata={**metadata})
|
83
|
+
|
84
|
+
|
85
|
+
def _connect(
|
86
|
+
self, token: Optional[str] = None, api_key: Optional[str] = None, connected_account_id: str = None
|
87
|
+
) -> Tuple[Self | str | Any]:
|
88
|
+
"""
|
89
|
+
Send connection request to Composio, retrieve `connected_account_id`, and proceed with OAuth process of the given app to activate the connection.
|
90
|
+
"""
|
91
|
+
|
92
|
+
connection_request, connected_account = None, None
|
93
|
+
connected_account_id = connected_account_id or self.connected_account_id
|
94
|
+
if connected_account_id:
|
95
|
+
connected_account = self.toolset.get_connected_account(id=connected_account_id)
|
96
|
+
|
97
|
+
if connected_account and connected_account.status == ComposioStatus.ACTIVE.value:
|
98
|
+
return self, ComposioStatus.ACTIVE.value
|
99
|
+
|
100
|
+
if not self.user_id:
|
101
|
+
raise PydanticCustomError("entity_id_missing", "Need entity_id to connect with the tool", {})
|
102
|
+
|
103
|
+
if self.auth_scheme == ComposioAuthScheme.API_KEY:
|
104
|
+
collected_from_user = {}
|
105
|
+
collected_from_user["api_key"] = api_key
|
106
|
+
connection_request = self.toolset.initiate_connection(
|
107
|
+
connected_account_params = collected_from_user,
|
108
|
+
app=self.app_name,
|
109
|
+
entity_id=self.user_id,
|
110
|
+
auth_scheme=self.auth_scheme,
|
111
|
+
)
|
112
|
+
|
113
|
+
if self.auth_scheme == ComposioAuthScheme.BEARER_TOKEN:
|
114
|
+
collected_from_user = {}
|
115
|
+
collected_from_user["token"] = token
|
116
|
+
connection_request = self.toolset.initiate_connection(
|
117
|
+
connected_account_params = collected_from_user,
|
118
|
+
app=self.app_name,
|
119
|
+
entity_id=self.user_id,
|
120
|
+
auth_scheme=self.auth_scheme,
|
121
|
+
)
|
122
|
+
|
123
|
+
if self.auth_scheme == ComposioAuthScheme.OAUTH2:
|
124
|
+
connection_request = self.toolset.initiate_connection(
|
125
|
+
app=self.app_name,
|
126
|
+
redirect_url = self.redirect_url, # clients will be redirected to this url after successful auth.
|
127
|
+
entity_id=self.user_id,
|
128
|
+
auth_scheme=self.auth_scheme,
|
129
|
+
)
|
130
|
+
|
131
|
+
if connection_request.connectionStatus == ComposioStatus.FAILED.value:
|
132
|
+
self._logger.log(level="error", message="Connection to composio failed.", color="red")
|
133
|
+
raise PydanticCustomError("connection_failed", "Connection to composio has failed", {})
|
134
|
+
|
135
|
+
|
136
|
+
connected_account = self.toolset.get_connected_account(id=connection_request.connectedAccountId)
|
137
|
+
# Note: connected_account.id === connection_request.connectedAccountId === self.connected_account_id
|
138
|
+
|
139
|
+
if connected_account.status == ComposioStatus.ACTIVE.value:
|
140
|
+
setattr(self.toolset, "entity_id", self.user_id)
|
141
|
+
self.connected_account_id = connection_request.connectedAccountId
|
142
|
+
|
143
|
+
elif connected_account.status == ComposioStatus.INITIATED.value:
|
144
|
+
setattr(self.toolset, "entity_id", self.user_id)
|
145
|
+
self.connected_account_id = connection_request.connectedAccountId
|
146
|
+
|
147
|
+
if connection_request.redirectUrl:
|
148
|
+
import webbrowser
|
149
|
+
webbrowser.open(connection_request.redirectUrl)
|
150
|
+
|
151
|
+
else:
|
152
|
+
self._logger.log(level="error", message="The account is invalid.", color="red")
|
153
|
+
raise PydanticCustomError("connection_failed", "Connection to composio has failed", {})
|
154
|
+
|
155
|
+
return self, connected_account.status if connected_account else connection_request.connectionStatus
|
156
|
+
|
157
|
+
|
158
|
+
def execute_composio_action_with_langchain(self, action_name: str | ComposioAction, task_in_natural_language: str) -> Tuple[Self, str]:
|
159
|
+
"""
|
160
|
+
Execute Composio's Action using Langchain's agent ecosystem.
|
161
|
+
"""
|
162
|
+
from langchain import hub
|
163
|
+
from langchain_openai import ChatOpenAI
|
164
|
+
from langchain.agents import create_openai_functions_agent, AgentExecutor
|
165
|
+
from composio_langchain import Action
|
166
|
+
|
167
|
+
action_name = action_name.value if isinstance(action_name, ComposioAction) else action_name
|
168
|
+
action = Action(action_name)
|
169
|
+
metadata = { action: { "OPENAI_API_KEY": OPENAI_API_KEY } }
|
170
|
+
toolset = self._setup_langchain_toolset(metadata=metadata)
|
171
|
+
tools = toolset.get_tools(actions=[action_name,], entity_id=self.user_id)
|
172
|
+
if not tools:
|
173
|
+
self._logger.log(level="error", message=f"Tools related to {action_name} are not found on Langchain", color="red")
|
174
|
+
raise PydanticCustomError("tool_not_found", "Tools not found on Langchain", {})
|
175
|
+
|
176
|
+
self.tools = tools
|
177
|
+
llm = ChatOpenAI()
|
178
|
+
prompt = hub.pull("hwchase17/openai-functions-agent")
|
179
|
+
agent = create_openai_functions_agent(llm, tools, prompt)
|
180
|
+
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
|
181
|
+
result = agent_executor.invoke(dict(input=task_in_natural_language))
|
182
|
+
return self, result["output"]
|
183
|
+
|
184
|
+
|
185
|
+
@property
|
186
|
+
def toolset(self) -> ComposioToolSet:
|
187
|
+
return ComposioToolSet(api_key=os.environ.get("COMPOSIO_API_KEY"))
|
188
|
+
|
189
|
+
|
190
|
+
def __name__(self):
|
191
|
+
return self.app_name
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: versionhq
|
3
|
-
Version: 1.1.9.
|
3
|
+
Version: 1.1.9.3
|
4
4
|
Summary: LLM orchestration frameworks for model-agnostic AI agents that handle complex outbound workflows
|
5
5
|
Author-email: Kuriko Iwai <kuriko@versi0n.io>
|
6
6
|
License: MIT License
|
@@ -52,12 +52,15 @@ Requires-Dist: setuptools>=75.6.0
|
|
52
52
|
Requires-Dist: wheel>=0.45.1
|
53
53
|
Requires-Dist: python-dotenv>=1.0.0
|
54
54
|
Requires-Dist: appdirs>=1.4.4
|
55
|
+
Requires-Dist: langchain>=0.3.14
|
56
|
+
Requires-Dist: langchain-openai>=0.2.14
|
57
|
+
Requires-Dist: composio-langchain>=0.6.12
|
55
58
|
|
56
59
|
# Overview
|
57
60
|
|
58
61
|

|
59
62
|
[](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
|
60
|
-

|
61
64
|

|
62
65
|

|
63
66
|
|
@@ -53,7 +53,7 @@ src/versionhq/team/__init__.py
|
|
53
53
|
src/versionhq/team/model.py
|
54
54
|
src/versionhq/team/team_planner.py
|
55
55
|
src/versionhq/tool/__init__.py
|
56
|
-
src/versionhq/tool/
|
56
|
+
src/versionhq/tool/composio_tool.py
|
57
57
|
src/versionhq/tool/decorator.py
|
58
58
|
src/versionhq/tool/model.py
|
59
59
|
src/versionhq/tool/tool_handler.py
|
@@ -0,0 +1,37 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from versionhq.tool.composio_tool import ComposioHandler
|
4
|
+
from versionhq.tool import ComposioAppName, ComposioAuthScheme, ComposioStatus, ComposioAction
|
5
|
+
|
6
|
+
DEFAULT_MODEL_NAME = os.environ.get("LITELLM_MODEL_NAME", "gpt-3.5-turbo")
|
7
|
+
LITELLM_API_KEY = os.environ.get("LITELLM_API_KEY")
|
8
|
+
TEST_CONNECTED_ACCOUNT_ID = os.environ.get("TEST_CONNECTED_ACCOUNT_ID", "f79b1a07-db14-4aa5-959e-5713ccd18de2")
|
9
|
+
|
10
|
+
|
11
|
+
def _test_connect_with_composio():
|
12
|
+
composio = ComposioHandler(
|
13
|
+
app_name=ComposioAppName.HUBSPOT,
|
14
|
+
auth_scheme=ComposioAuthScheme.OAUTH2,
|
15
|
+
connected_account_id=TEST_CONNECTED_ACCOUNT_ID
|
16
|
+
)
|
17
|
+
composio, status = composio._connect()
|
18
|
+
assert composio.connected_account_id is not None
|
19
|
+
assert status is not ComposioStatus.FAILED
|
20
|
+
|
21
|
+
|
22
|
+
def _test_execute_action_on_langchain():
|
23
|
+
composio = ComposioHandler(
|
24
|
+
app_name=ComposioAppName.HUBSPOT,
|
25
|
+
auth_scheme=ComposioAuthScheme.OAUTH2,
|
26
|
+
connected_account_id=TEST_CONNECTED_ACCOUNT_ID
|
27
|
+
)
|
28
|
+
composio, status = composio._connect()
|
29
|
+
composio, res = composio.execute_composio_action_with_langchain(
|
30
|
+
action_name=ComposioAction.HUBSPOT_CREATE_PIPELINE_STAGE,
|
31
|
+
task_in_natural_language="create a demo pipeline"
|
32
|
+
)
|
33
|
+
assert status == ComposioStatus.ACTIVE.value
|
34
|
+
assert composio.connected_account_id == TEST_CONNECTED_ACCOUNT_ID
|
35
|
+
assert composio.tools is not None
|
36
|
+
assert res is not None
|
37
|
+
assert isinstance(res, str)
|