versionhq 1.1.9.2__tar.gz → 1.1.9.4__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.
Files changed (78) hide show
  1. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/.gitignore +3 -1
  2. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/PKG-INFO +5 -2
  3. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/README.md +1 -1
  4. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/pyproject.toml +4 -1
  5. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/requirements.txt +70 -5
  6. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/__init__.py +3 -3
  7. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/clients/workflow/model.py +1 -1
  8. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/tool/__init__.py +7 -4
  9. versionhq-1.1.9.4/src/versionhq/tool/composio_tool.py +191 -0
  10. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq.egg-info/PKG-INFO +5 -2
  11. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq.egg-info/SOURCES.txt +1 -1
  12. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq.egg-info/requires.txt +3 -0
  13. versionhq-1.1.9.4/tests/tool/composio_test.py +37 -0
  14. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/uv.lock +355 -29
  15. versionhq-1.1.9.2/src/versionhq/tool/composio.py +0 -161
  16. versionhq-1.1.9.2/tests/tool/composio_test.py +0 -19
  17. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/.github/workflows/publish.yml +0 -0
  18. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/.github/workflows/publish_testpypi.yml +0 -0
  19. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/.github/workflows/run_tests.yml +0 -0
  20. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/.github/workflows/security_check.yml +0 -0
  21. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/.pre-commit-config.yaml +0 -0
  22. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/.python-version +0 -0
  23. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/LICENSE +0 -0
  24. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/SECURITY.md +0 -0
  25. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/db/preprocess.py +0 -0
  26. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/requirements-dev.txt +0 -0
  27. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/runtime.txt +0 -0
  28. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/setup.cfg +0 -0
  29. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/_utils/__init__.py +0 -0
  30. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/_utils/cache_handler.py +0 -0
  31. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/_utils/i18n.py +0 -0
  32. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/_utils/logger.py +0 -0
  33. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/_utils/process_config.py +0 -0
  34. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/_utils/rpm_controller.py +0 -0
  35. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/_utils/usage_metrics.py +0 -0
  36. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/agent/TEMPLATES/Backstory.py +0 -0
  37. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/agent/TEMPLATES/__init__.py +0 -0
  38. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/agent/__init__.py +0 -0
  39. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/agent/model.py +0 -0
  40. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/agent/parser.py +0 -0
  41. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/cli/__init__.py +0 -0
  42. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/clients/__init__.py +0 -0
  43. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/clients/customer/__init__.py +0 -0
  44. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/clients/customer/model.py +0 -0
  45. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/clients/product/__init__.py +0 -0
  46. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/clients/product/model.py +0 -0
  47. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/clients/workflow/__init__.py +0 -0
  48. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/llm/__init__.py +0 -0
  49. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/llm/llm_vars.py +0 -0
  50. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/llm/model.py +0 -0
  51. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/storage/__init__.py +0 -0
  52. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/storage/task_output_storage.py +0 -0
  53. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/task/__init__.py +0 -0
  54. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/task/formatter.py +0 -0
  55. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/task/log_handler.py +0 -0
  56. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/task/model.py +0 -0
  57. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/team/__init__.py +0 -0
  58. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/team/model.py +0 -0
  59. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/team/team_planner.py +0 -0
  60. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/tool/decorator.py +0 -0
  61. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/tool/model.py +0 -0
  62. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq/tool/tool_handler.py +0 -0
  63. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq.egg-info/dependency_links.txt +0 -0
  64. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/src/versionhq.egg-info/top_level.txt +0 -0
  65. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/__init__.py +0 -0
  66. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/agent/__init__.py +0 -0
  67. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/agent/agent_test.py +0 -0
  68. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/cli/__init__.py +0 -0
  69. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/clients/product_test.py +0 -0
  70. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/clients/workflow_test.py +0 -0
  71. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/conftest.py +0 -0
  72. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/task/__init__.py +0 -0
  73. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/task/task_test.py +0 -0
  74. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/team/Prompts/Demo_test.py +0 -0
  75. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/team/__init__.py +0 -0
  76. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/team/team_test.py +0 -0
  77. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/tool/__init__.py +0 -0
  78. {versionhq-1.1.9.2 → versionhq-1.1.9.4}/tests/tool/tool_test.py +0 -0
@@ -1,7 +1,9 @@
1
+ deploy.py
2
+ destinations.py
3
+
1
4
  knowledge/
2
5
  memory/
3
6
 
4
- # composio.py
5
7
  memo.txt
6
8
 
7
9
  dist/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: versionhq
3
- Version: 1.1.9.2
3
+ Version: 1.1.9.4
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
  ![MIT license](https://img.shields.io/badge/License-MIT-green)
59
62
  [![Publisher](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml/badge.svg)](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
60
- ![PyPI](https://img.shields.io/badge/PyPI-v1.1.7.9-blue)
63
+ ![PyPI](https://img.shields.io/badge/PyPI-v1.1.9.3-blue)
61
64
  ![python ver](https://img.shields.io/badge/Python-3.12/3.13-purple)
62
65
  ![pyenv ver](https://img.shields.io/badge/pyenv-2.5.0-orange)
63
66
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ![MIT license](https://img.shields.io/badge/License-MIT-green)
4
4
  [![Publisher](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml/badge.svg)](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
5
- ![PyPI](https://img.shields.io/badge/PyPI-v1.1.7.9-blue)
5
+ ![PyPI](https://img.shields.io/badge/PyPI-v1.1.9.3-blue)
6
6
  ![python ver](https://img.shields.io/badge/Python-3.12/3.13-purple)
7
7
  ![pyenv ver](https://img.shields.io/badge/pyenv-2.5.0-orange)
8
8
 
@@ -15,7 +15,7 @@ exclude = ["test*", "__pycache__"]
15
15
 
16
16
  [project]
17
17
  name = "versionhq"
18
- version = "1.1.9.2"
18
+ version = "1.1.9.4"
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.9
47
- # via composio-openai
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 huggingface-hub
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 huggingface-hub
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 litellm
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.composio import Composio
18
+ from versionhq.tool.composio_tool import ComposioHandler
19
19
 
20
20
 
21
- __version__ = "1.1.9.2"
21
+ __version__ = "1.1.9.4"
22
22
  __all__ = [
23
23
  "Agent",
24
24
  "Customer",
@@ -34,5 +34,5 @@ __all__ = [
34
34
  "Team",
35
35
  "TeamOutput",
36
36
  "Tool",
37
- "Composio"
37
+ "ComposioHandler"
38
38
  ]
@@ -59,7 +59,7 @@ class MessagingComponent(ABC, BaseModel):
59
59
  layer_id: int = Field(default=0, description="add id of the layer: 0, 1, 2")
60
60
  message: str = Field(default=None, max_length=1024, description="text message content to be sent")
61
61
  score: InstanceOf[Score] = Field(default=None)
62
- condition: str = Field(default=None, max_length=128, description="condition to execute the next component")
62
+ condition: str = Field(default=None, description="condition to execute the next component")
63
63
  interval: Optional[str] = Field(default=None, description="ideal interval to set to assess the condition")
64
64
 
65
65
 
@@ -40,10 +40,11 @@ composio_app_set = [
40
40
  (ComposioAppName.LINKEDIN, ComposioAuthScheme.OAUTH2),
41
41
  ]
42
42
 
43
- class COMPOSIO_STATUS(str, Enum):
44
- INITIATED = "initiated"
45
- ACTIVE = "active"
46
- FAILED = "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.2
3
+ Version: 1.1.9.4
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
  ![MIT license](https://img.shields.io/badge/License-MIT-green)
59
62
  [![Publisher](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml/badge.svg)](https://github.com/versionHQ/multi-agent-system/actions/workflows/publish.yml)
60
- ![PyPI](https://img.shields.io/badge/PyPI-v1.1.7.9-blue)
63
+ ![PyPI](https://img.shields.io/badge/PyPI-v1.1.9.3-blue)
61
64
  ![python ver](https://img.shields.io/badge/Python-3.12/3.13-purple)
62
65
  ![pyenv ver](https://img.shields.io/badge/pyenv-2.5.0-orange)
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/composio.py
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
@@ -12,3 +12,6 @@ setuptools>=75.6.0
12
12
  wheel>=0.45.1
13
13
  python-dotenv>=1.0.0
14
14
  appdirs>=1.4.4
15
+ langchain>=0.3.14
16
+ langchain-openai>=0.2.14
17
+ composio-langchain>=0.6.12
@@ -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)