alita-sdk 0.3.365__py3-none-any.whl → 0.3.462__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of alita-sdk might be problematic. Click here for more details.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- alita_sdk/cli/agent_executor.py +144 -0
- alita_sdk/cli/agent_loader.py +197 -0
- alita_sdk/cli/agent_ui.py +166 -0
- alita_sdk/cli/agents.py +1069 -0
- alita_sdk/cli/callbacks.py +576 -0
- alita_sdk/cli/cli.py +159 -0
- alita_sdk/cli/config.py +153 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/toolkit.py +330 -0
- alita_sdk/cli/toolkit_loader.py +55 -0
- alita_sdk/cli/tools/__init__.py +9 -0
- alita_sdk/cli/tools/filesystem.py +905 -0
- alita_sdk/configurations/bitbucket.py +95 -0
- alita_sdk/configurations/confluence.py +96 -1
- alita_sdk/configurations/gitlab.py +79 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/testrail.py +88 -0
- alita_sdk/configurations/xray.py +93 -0
- alita_sdk/configurations/zephyr_enterprise.py +93 -0
- alita_sdk/configurations/zephyr_essential.py +75 -0
- alita_sdk/runtime/clients/artifact.py +1 -1
- alita_sdk/runtime/clients/client.py +47 -10
- alita_sdk/runtime/clients/mcp_discovery.py +342 -0
- alita_sdk/runtime/clients/mcp_manager.py +262 -0
- alita_sdk/runtime/clients/sandbox_client.py +373 -0
- alita_sdk/runtime/langchain/assistant.py +70 -41
- alita_sdk/runtime/langchain/constants.py +6 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +4 -1
- alita_sdk/runtime/langchain/document_loaders/constants.py +73 -100
- alita_sdk/runtime/langchain/langraph_agent.py +164 -38
- alita_sdk/runtime/langchain/utils.py +43 -7
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/toolkits/__init__.py +24 -0
- alita_sdk/runtime/toolkits/application.py +8 -1
- alita_sdk/runtime/toolkits/artifact.py +5 -6
- alita_sdk/runtime/toolkits/mcp.py +895 -0
- alita_sdk/runtime/toolkits/tools.py +140 -50
- alita_sdk/runtime/tools/__init__.py +7 -2
- alita_sdk/runtime/tools/application.py +7 -0
- alita_sdk/runtime/tools/function.py +94 -5
- alita_sdk/runtime/tools/graph.py +10 -4
- alita_sdk/runtime/tools/image_generation.py +104 -8
- alita_sdk/runtime/tools/llm.py +204 -114
- alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
- alita_sdk/runtime/tools/mcp_remote_tool.py +166 -0
- alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
- alita_sdk/runtime/tools/sandbox.py +180 -79
- alita_sdk/runtime/tools/vectorstore.py +22 -21
- alita_sdk/runtime/tools/vectorstore_base.py +79 -26
- alita_sdk/runtime/utils/mcp_oauth.py +164 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +405 -0
- alita_sdk/runtime/utils/streamlit.py +34 -3
- alita_sdk/runtime/utils/toolkit_utils.py +14 -4
- alita_sdk/runtime/utils/utils.py +1 -0
- alita_sdk/tools/__init__.py +48 -31
- alita_sdk/tools/ado/repos/__init__.py +1 -0
- alita_sdk/tools/ado/test_plan/__init__.py +1 -1
- alita_sdk/tools/ado/wiki/__init__.py +1 -5
- alita_sdk/tools/ado/work_item/__init__.py +1 -5
- alita_sdk/tools/ado/work_item/ado_wrapper.py +17 -8
- alita_sdk/tools/base_indexer_toolkit.py +194 -112
- alita_sdk/tools/bitbucket/__init__.py +1 -0
- alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
- alita_sdk/tools/code/sonar/__init__.py +1 -1
- alita_sdk/tools/code_indexer_toolkit.py +15 -5
- alita_sdk/tools/confluence/__init__.py +2 -2
- alita_sdk/tools/confluence/api_wrapper.py +110 -63
- alita_sdk/tools/confluence/loader.py +10 -0
- alita_sdk/tools/elitea_base.py +22 -22
- alita_sdk/tools/github/__init__.py +2 -2
- alita_sdk/tools/gitlab/__init__.py +2 -1
- alita_sdk/tools/gitlab/api_wrapper.py +11 -7
- alita_sdk/tools/gitlab_org/__init__.py +1 -2
- alita_sdk/tools/google_places/__init__.py +2 -1
- alita_sdk/tools/jira/__init__.py +1 -0
- alita_sdk/tools/jira/api_wrapper.py +1 -1
- alita_sdk/tools/memory/__init__.py +1 -1
- alita_sdk/tools/non_code_indexer_toolkit.py +2 -2
- alita_sdk/tools/openapi/__init__.py +10 -1
- alita_sdk/tools/pandas/__init__.py +1 -1
- alita_sdk/tools/postman/__init__.py +2 -1
- alita_sdk/tools/postman/api_wrapper.py +18 -8
- alita_sdk/tools/postman/postman_analysis.py +8 -1
- alita_sdk/tools/pptx/__init__.py +2 -2
- alita_sdk/tools/qtest/__init__.py +3 -3
- alita_sdk/tools/qtest/api_wrapper.py +1708 -76
- alita_sdk/tools/rally/__init__.py +1 -2
- alita_sdk/tools/report_portal/__init__.py +1 -0
- alita_sdk/tools/salesforce/__init__.py +1 -0
- alita_sdk/tools/servicenow/__init__.py +2 -3
- alita_sdk/tools/sharepoint/__init__.py +1 -0
- alita_sdk/tools/sharepoint/api_wrapper.py +125 -34
- alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
- alita_sdk/tools/sharepoint/utils.py +8 -2
- alita_sdk/tools/slack/__init__.py +1 -0
- alita_sdk/tools/sql/__init__.py +2 -1
- alita_sdk/tools/sql/api_wrapper.py +71 -23
- alita_sdk/tools/testio/__init__.py +1 -0
- alita_sdk/tools/testrail/__init__.py +1 -3
- alita_sdk/tools/utils/__init__.py +17 -0
- alita_sdk/tools/utils/content_parser.py +35 -24
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +67 -21
- alita_sdk/tools/xray/__init__.py +2 -1
- alita_sdk/tools/zephyr/__init__.py +2 -1
- alita_sdk/tools/zephyr_enterprise/__init__.py +1 -0
- alita_sdk/tools/zephyr_essential/__init__.py +1 -0
- alita_sdk/tools/zephyr_scale/__init__.py +1 -0
- alita_sdk/tools/zephyr_squad/__init__.py +1 -0
- {alita_sdk-0.3.365.dist-info → alita_sdk-0.3.462.dist-info}/METADATA +8 -2
- {alita_sdk-0.3.365.dist-info → alita_sdk-0.3.462.dist-info}/RECORD +118 -93
- alita_sdk-0.3.462.dist-info/entry_points.txt +2 -0
- {alita_sdk-0.3.365.dist-info → alita_sdk-0.3.462.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.365.dist-info → alita_sdk-0.3.462.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.365.dist-info → alita_sdk-0.3.462.dist-info}/top_level.txt +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional
|
|
3
3
|
|
|
4
|
-
from
|
|
5
|
-
from pydantic
|
|
4
|
+
from langchain_core.tools import ToolException
|
|
5
|
+
from pydantic import create_model, SecretStr, model_validator
|
|
6
|
+
from pydantic.fields import PrivateAttr, Field
|
|
6
7
|
from sqlalchemy import create_engine, text, inspect, Engine
|
|
7
8
|
from sqlalchemy.orm import sessionmaker
|
|
8
9
|
|
|
@@ -27,7 +28,7 @@ class SQLApiWrapper(BaseToolApiWrapper):
|
|
|
27
28
|
username: str
|
|
28
29
|
password: SecretStr
|
|
29
30
|
database_name: str
|
|
30
|
-
_client: Optional[Engine] = PrivateAttr()
|
|
31
|
+
_client: Optional[Engine] = PrivateAttr(default=None)
|
|
31
32
|
|
|
32
33
|
@model_validator(mode='before')
|
|
33
34
|
@classmethod
|
|
@@ -35,27 +36,73 @@ class SQLApiWrapper(BaseToolApiWrapper):
|
|
|
35
36
|
for field in SQLConfig.model_fields:
|
|
36
37
|
if field not in values or not values[field]:
|
|
37
38
|
raise ValueError(f"{field} is a required field and must be provided.")
|
|
38
|
-
|
|
39
|
-
dialect = values['dialect']
|
|
40
|
-
host = values['host']
|
|
41
|
-
username = values['username']
|
|
42
|
-
password = values['password']
|
|
43
|
-
database_name = values['database_name']
|
|
44
|
-
port = values['port']
|
|
45
|
-
|
|
46
|
-
if dialect == SQLDialect.POSTGRES:
|
|
47
|
-
connection_string = f'postgresql+psycopg2://{username}:{password}@{host}:{port}/{database_name}'
|
|
48
|
-
elif dialect == SQLDialect.MYSQL:
|
|
49
|
-
connection_string = f'mysql+pymysql://{username}:{password}@{host}:{port}/{database_name}'
|
|
50
|
-
else:
|
|
51
|
-
raise ValueError(f"Unsupported database type. Supported types are: {[e.value for e in SQLDialect]}")
|
|
52
|
-
|
|
53
|
-
cls._client = create_engine(connection_string)
|
|
54
39
|
return values
|
|
55
40
|
|
|
41
|
+
def _mask_password_in_error(self, error_message: str) -> str:
|
|
42
|
+
"""Mask password in error messages, showing only last 4 characters."""
|
|
43
|
+
password_str = self.password.get_secret_value()
|
|
44
|
+
if len(password_str) <= 4:
|
|
45
|
+
masked_password = "****"
|
|
46
|
+
else:
|
|
47
|
+
masked_password = "****" + password_str[-4:]
|
|
48
|
+
|
|
49
|
+
# Replace all occurrences of the password, and any substring of the password that may appear in the error message
|
|
50
|
+
for part in [password_str, password_str.replace('@', ''), password_str.split('@')[-1]]:
|
|
51
|
+
if part and part in error_message:
|
|
52
|
+
error_message = error_message.replace(part, masked_password)
|
|
53
|
+
return error_message
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def client(self) -> Engine:
|
|
57
|
+
"""Lazy property to create and return database engine with error handling."""
|
|
58
|
+
if self._client is None:
|
|
59
|
+
try:
|
|
60
|
+
dialect = self.dialect
|
|
61
|
+
host = self.host
|
|
62
|
+
username = self.username
|
|
63
|
+
password = self.password.get_secret_value()
|
|
64
|
+
database_name = self.database_name
|
|
65
|
+
port = self.port
|
|
66
|
+
|
|
67
|
+
if dialect == SQLDialect.POSTGRES:
|
|
68
|
+
connection_string = f'postgresql+psycopg2://{username}:{password}@{host}:{port}/{database_name}'
|
|
69
|
+
elif dialect == SQLDialect.MYSQL:
|
|
70
|
+
connection_string = f'mysql+pymysql://{username}:{password}@{host}:{port}/{database_name}'
|
|
71
|
+
else:
|
|
72
|
+
raise ValueError(f"Unsupported database type. Supported types are: {[e.value for e in SQLDialect]}")
|
|
73
|
+
|
|
74
|
+
self._client = create_engine(connection_string)
|
|
75
|
+
|
|
76
|
+
# Test the connection
|
|
77
|
+
with self._client.connect() as conn:
|
|
78
|
+
conn.execute(text("SELECT 1"))
|
|
79
|
+
|
|
80
|
+
except Exception as e:
|
|
81
|
+
error_message = str(e)
|
|
82
|
+
masked_error = self._mask_password_in_error(error_message)
|
|
83
|
+
logger.error(f"Database connection failed: {masked_error}")
|
|
84
|
+
raise ValueError(f"Database connection failed: {masked_error}")
|
|
85
|
+
|
|
86
|
+
return self._client
|
|
87
|
+
|
|
88
|
+
def _handle_database_errors(func):
|
|
89
|
+
"""Decorator to catch exceptions and mask passwords in error messages."""
|
|
90
|
+
|
|
91
|
+
def wrapper(self, *args, **kwargs):
|
|
92
|
+
try:
|
|
93
|
+
return func(self, *args, **kwargs)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
error_message = str(e)
|
|
96
|
+
masked_error = self._mask_password_in_error(error_message)
|
|
97
|
+
logger.error(f"Database operation failed in {func.__name__}: {masked_error}")
|
|
98
|
+
raise ToolException(masked_error)
|
|
99
|
+
|
|
100
|
+
return wrapper
|
|
101
|
+
|
|
102
|
+
@_handle_database_errors
|
|
56
103
|
def execute_sql(self, sql_query: str):
|
|
57
104
|
"""Executes the provided SQL query on the configured database."""
|
|
58
|
-
engine = self.
|
|
105
|
+
engine = self.client
|
|
59
106
|
maker_session = sessionmaker(bind=engine)
|
|
60
107
|
session = maker_session()
|
|
61
108
|
try:
|
|
@@ -76,9 +123,10 @@ class SQLApiWrapper(BaseToolApiWrapper):
|
|
|
76
123
|
finally:
|
|
77
124
|
session.close()
|
|
78
125
|
|
|
126
|
+
@_handle_database_errors
|
|
79
127
|
def list_tables_and_columns(self):
|
|
80
128
|
"""Lists all tables and their columns in the configured database."""
|
|
81
|
-
inspector = inspect(self.
|
|
129
|
+
inspector = inspect(self.client)
|
|
82
130
|
data = {}
|
|
83
131
|
tables = inspector.get_table_names()
|
|
84
132
|
for table in tables:
|
|
@@ -109,4 +157,4 @@ class SQLApiWrapper(BaseToolApiWrapper):
|
|
|
109
157
|
"description": self.list_tables_and_columns.__doc__,
|
|
110
158
|
"args_schema": SQLNoInput,
|
|
111
159
|
}
|
|
112
|
-
]
|
|
160
|
+
]
|
|
@@ -33,6 +33,7 @@ class TestIOToolkit(BaseToolkit):
|
|
|
33
33
|
testio_configuration=(TestIOConfiguration, Field(description="TestIO Configuration", json_schema_extra={'configuration_types': ['testio']})),
|
|
34
34
|
selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
|
|
35
35
|
__config__=ConfigDict(json_schema_extra={'metadata': {"label": "TestIO", "icon_url": "testio-icon.svg",
|
|
36
|
+
"max_length": TOOLKIT_MAX_LENGTH,
|
|
36
37
|
"categories": ["testing"],
|
|
37
38
|
"extra_categories": ["test automation", "test case management", "test planning"]}})
|
|
38
39
|
)
|
|
@@ -39,9 +39,6 @@ class TestrailToolkit(BaseToolkit):
|
|
|
39
39
|
TestrailToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
|
|
40
40
|
m = create_model(
|
|
41
41
|
name,
|
|
42
|
-
name=(str, Field(description="Toolkit name", json_schema_extra={
|
|
43
|
-
'toolkit_name': True,
|
|
44
|
-
"max_length": TestrailToolkit.toolkit_max_length})),
|
|
45
42
|
testrail_configuration=(Optional[TestRailConfiguration], Field(description="TestRail Configuration", json_schema_extra={'configuration_types': ['testrail']})),
|
|
46
43
|
pgvector_configuration=(Optional[PgVectorConfiguration], Field(default = None,
|
|
47
44
|
description="PgVector Configuration", json_schema_extra={'configuration_types': ['pgvector']})),
|
|
@@ -50,6 +47,7 @@ class TestrailToolkit(BaseToolkit):
|
|
|
50
47
|
selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
|
|
51
48
|
__config__=ConfigDict(json_schema_extra={'metadata':
|
|
52
49
|
{"label": "Testrail", "icon_url": "testrail-icon.svg",
|
|
50
|
+
"max_length": TestrailToolkit.toolkit_max_length,
|
|
53
51
|
"categories": ["test management"],
|
|
54
52
|
"extra_categories": ["quality assurance", "test case management", "test planning"]
|
|
55
53
|
}})
|
|
@@ -97,3 +97,20 @@ def check_connection_response(check_fun):
|
|
|
97
97
|
else:
|
|
98
98
|
return f"Service Unreachable: return code {response.status_code}"
|
|
99
99
|
return _wrapper
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def make_json_serializable(obj):
|
|
103
|
+
if isinstance(obj, BaseModel):
|
|
104
|
+
return obj.model_dump()
|
|
105
|
+
if isinstance(obj, dict):
|
|
106
|
+
return {k: make_json_serializable(v) for k, v in obj.items()}
|
|
107
|
+
if isinstance(obj, list):
|
|
108
|
+
return [make_json_serializable(i) for i in obj]
|
|
109
|
+
if isinstance(obj, bool):
|
|
110
|
+
return bool(obj)
|
|
111
|
+
if isinstance(obj, (str, int, float)) or obj is None:
|
|
112
|
+
return obj
|
|
113
|
+
# Fallback: handle objects that look like booleans but were not caught above
|
|
114
|
+
if str(obj) in ("True", "False"):
|
|
115
|
+
return str(obj) == "True"
|
|
116
|
+
return str(obj)
|
|
@@ -92,21 +92,24 @@ def parse_file_content(file_name=None, file_content=None, is_capture_image: bool
|
|
|
92
92
|
return ToolException(
|
|
93
93
|
"Not supported type of files entered. Supported types are TXT, DOCX, PDF, PPTX, XLSX and XLS only.")
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
extension = Path(file_path if file_path else file_name).suffix
|
|
99
|
-
loader_kwargs = get_loader_kwargs(loaders_map.get(extension), file_name, file_content, is_capture_image, page_number, sheet_name, llm, file_path, excel_by_sheets)
|
|
100
|
-
if file_content:
|
|
101
|
-
return load_content_from_bytes(file_content=file_content,
|
|
102
|
-
extension=extension,
|
|
103
|
-
loader_extra_config=loader_kwargs,
|
|
104
|
-
llm=llm)
|
|
95
|
+
try:
|
|
96
|
+
if hasattr(loader, 'get_content'):
|
|
97
|
+
return loader.get_content()
|
|
105
98
|
else:
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
99
|
+
extension = Path(file_path if file_path else file_name).suffix
|
|
100
|
+
loader_kwargs = get_loader_kwargs(loaders_map.get(extension), file_name, file_content, is_capture_image, page_number, sheet_name, llm, file_path, excel_by_sheets)
|
|
101
|
+
if file_content:
|
|
102
|
+
return load_content_from_bytes(file_content=file_content,
|
|
103
|
+
extension=extension,
|
|
104
|
+
loader_extra_config=loader_kwargs,
|
|
105
|
+
llm=llm)
|
|
106
|
+
else:
|
|
107
|
+
return load_content(file_path=file_path,
|
|
108
|
+
extension=extension,
|
|
109
|
+
loader_extra_config=loader_kwargs,
|
|
110
|
+
llm=llm)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
return ToolException(f"Error reading file ({file_name or file_path}) content. Make sure these types are supported: {str(e)}")
|
|
110
113
|
|
|
111
114
|
def load_file_docs(file_name=None, file_content=None, is_capture_image: bool = False, page_number: int = None,
|
|
112
115
|
sheet_name: str = None, llm=None, file_path: str = None, excel_by_sheets: bool = False) -> List[Document] | ToolException:
|
|
@@ -153,7 +156,7 @@ def prepare_loader(file_name=None, file_content=None, is_capture_image: bool = F
|
|
|
153
156
|
|
|
154
157
|
loader_object = loaders_map.get(extension)
|
|
155
158
|
if not loader_object:
|
|
156
|
-
|
|
159
|
+
loader_object = loaders_map.get('.txt') # Default to text loader if no specific loader found
|
|
157
160
|
loader_kwargs = get_loader_kwargs(loader_object, file_name, file_content, is_capture_image, page_number, sheet_name, llm, file_path, excel_by_sheets, prompt)
|
|
158
161
|
loader = loader_object['class'](**loader_kwargs)
|
|
159
162
|
return loader
|
|
@@ -222,10 +225,18 @@ def process_document_by_type(content, extension_source: str, document: Document
|
|
|
222
225
|
metadata={**document.metadata, 'chunk_id': 1}
|
|
223
226
|
)
|
|
224
227
|
return
|
|
228
|
+
#
|
|
229
|
+
chunks_counter = 0
|
|
225
230
|
for chunk in chunks:
|
|
231
|
+
chunks_counter += 1
|
|
232
|
+
metadata = {**document.metadata, **chunk.metadata}
|
|
233
|
+
#
|
|
234
|
+
# ensure each chunk has a unique chunk_id
|
|
235
|
+
metadata['chunk_id'] = chunks_counter
|
|
236
|
+
#
|
|
226
237
|
yield Document(
|
|
227
238
|
page_content=sanitize_for_postgres(chunk.page_content),
|
|
228
|
-
metadata=
|
|
239
|
+
metadata=metadata
|
|
229
240
|
)
|
|
230
241
|
|
|
231
242
|
|
|
@@ -262,18 +273,18 @@ def process_content_by_type(content, filename: str, llm=None, chunking_config=No
|
|
|
262
273
|
loader_kwargs = loader_config['kwargs']
|
|
263
274
|
# Determine which loader configuration keys are allowed to be overridden by user input.
|
|
264
275
|
# If 'allowed_to_override' is specified in the loader configuration, use it; otherwise, allow all keys in loader_kwargs.
|
|
265
|
-
allowed_to_override = loader_config.get('allowed_to_override',
|
|
276
|
+
allowed_to_override = loader_config.get('allowed_to_override', loader_kwargs)
|
|
266
277
|
# If a chunking_config is provided and contains custom configuration for the current file extension,
|
|
267
|
-
# update loader_kwargs with user-supplied values, but only for keys explicitly permitted in allowed_to_override.
|
|
278
|
+
# update loader_kwargs with user-supplied values, but only for keys explicitly permitted in allowed_to_override and if value differs from default.
|
|
268
279
|
# This ensures that only safe and intended parameters can be customized, preventing accidental or unauthorized changes
|
|
269
280
|
# to critical loader settings.
|
|
270
281
|
if chunking_config and (users_config_for_extension := chunking_config.get(extension, {})):
|
|
271
|
-
for key in set(users_config_for_extension.keys()) & set(allowed_to_override):
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
282
|
+
for key in set(users_config_for_extension.keys()) & set(allowed_to_override.keys()):
|
|
283
|
+
if users_config_for_extension[key] != allowed_to_override[key]:
|
|
284
|
+
loader_kwargs[key] = users_config_for_extension[key]
|
|
285
|
+
if LoaderProperties.LLM.value in loader_kwargs and loader_kwargs.pop(LoaderProperties.LLM.value):
|
|
286
|
+
loader_kwargs['llm'] = llm
|
|
287
|
+
if LoaderProperties.PROMPT_DEFAULT.value in loader_kwargs and loader_kwargs.pop(LoaderProperties.PROMPT_DEFAULT.value):
|
|
277
288
|
loader_kwargs[LoaderProperties.PROMPT.value] = image_processing_prompt
|
|
278
289
|
loader = loader_cls(file_path=temp_file_path, **loader_kwargs)
|
|
279
290
|
yield from loader.load()
|
|
@@ -2,6 +2,8 @@ from abc import ABC, abstractmethod
|
|
|
2
2
|
from typing import Any, Dict, Optional, List
|
|
3
3
|
from logging import getLogger
|
|
4
4
|
|
|
5
|
+
from ...runtime.utils.utils import IndexerKeywords
|
|
6
|
+
|
|
5
7
|
logger = getLogger(__name__)
|
|
6
8
|
|
|
7
9
|
|
|
@@ -24,12 +26,12 @@ class VectorStoreAdapter(ABC):
|
|
|
24
26
|
pass
|
|
25
27
|
|
|
26
28
|
@abstractmethod
|
|
27
|
-
def get_indexed_ids(self, vectorstore_wrapper,
|
|
29
|
+
def get_indexed_ids(self, vectorstore_wrapper, index_name: Optional[str] = '') -> List[str]:
|
|
28
30
|
"""Get all indexed document IDs from vectorstore"""
|
|
29
31
|
pass
|
|
30
32
|
|
|
31
33
|
@abstractmethod
|
|
32
|
-
def clean_collection(self, vectorstore_wrapper,
|
|
34
|
+
def clean_collection(self, vectorstore_wrapper, index_name: str = ''):
|
|
33
35
|
"""Clean the vectorstore collection by deleting all indexed data."""
|
|
34
36
|
pass
|
|
35
37
|
|
|
@@ -39,7 +41,7 @@ class VectorStoreAdapter(ABC):
|
|
|
39
41
|
pass
|
|
40
42
|
|
|
41
43
|
@abstractmethod
|
|
42
|
-
def get_code_indexed_data(self, vectorstore_wrapper,
|
|
44
|
+
def get_code_indexed_data(self, vectorstore_wrapper, index_name) -> Dict[str, Dict[str, Any]]:
|
|
43
45
|
"""Get all indexed data from vectorstore for code content"""
|
|
44
46
|
pass
|
|
45
47
|
|
|
@@ -48,6 +50,11 @@ class VectorStoreAdapter(ABC):
|
|
|
48
50
|
"""Add a new collection name to the metadata"""
|
|
49
51
|
pass
|
|
50
52
|
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def get_index_meta(self, vectorstore_wrapper, index_name: str) -> List[Dict[str, Any]]:
|
|
55
|
+
"""Get all index_meta entries from the vector store."""
|
|
56
|
+
pass
|
|
57
|
+
|
|
51
58
|
|
|
52
59
|
class PGVectorAdapter(VectorStoreAdapter):
|
|
53
60
|
"""Adapter for PGVector database operations."""
|
|
@@ -99,20 +106,25 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
99
106
|
session.commit()
|
|
100
107
|
logger.info(f"Schema '{schema_name}' has been dropped.")
|
|
101
108
|
|
|
102
|
-
def get_indexed_ids(self, vectorstore_wrapper,
|
|
109
|
+
def get_indexed_ids(self, vectorstore_wrapper, index_name: Optional[str] = '') -> List[str]:
|
|
103
110
|
"""Get all indexed document IDs from PGVector"""
|
|
104
111
|
from sqlalchemy.orm import Session
|
|
105
|
-
from sqlalchemy import func
|
|
112
|
+
from sqlalchemy import func, or_
|
|
106
113
|
|
|
107
114
|
store = vectorstore_wrapper.vectorstore
|
|
108
115
|
try:
|
|
109
116
|
with Session(store.session_maker.bind) as session:
|
|
110
117
|
# Start building the query
|
|
111
118
|
query = session.query(store.EmbeddingStore.id)
|
|
112
|
-
# Apply filter only if
|
|
113
|
-
if
|
|
119
|
+
# Apply filter only if index_name is provided
|
|
120
|
+
if index_name:
|
|
114
121
|
query = query.filter(
|
|
115
|
-
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') ==
|
|
122
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name,
|
|
123
|
+
or_(
|
|
124
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'type').is_(None),
|
|
125
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata,
|
|
126
|
+
'type') != IndexerKeywords.INDEX_META_TYPE.value
|
|
127
|
+
)
|
|
116
128
|
)
|
|
117
129
|
ids = query.all()
|
|
118
130
|
return [str(id_tuple[0]) for id_tuple in ids]
|
|
@@ -120,25 +132,33 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
120
132
|
logger.error(f"Failed to get indexed IDs from PGVector: {str(e)}")
|
|
121
133
|
return []
|
|
122
134
|
|
|
123
|
-
def clean_collection(self, vectorstore_wrapper,
|
|
135
|
+
def clean_collection(self, vectorstore_wrapper, index_name: str = ''):
|
|
124
136
|
"""Clean the vectorstore collection by deleting all indexed data."""
|
|
125
137
|
# This logic deletes all data from the vectorstore collection without removal of collection.
|
|
126
138
|
# Collection itself remains available for future indexing.
|
|
127
|
-
|
|
139
|
+
from sqlalchemy.orm import Session
|
|
140
|
+
from sqlalchemy import func
|
|
141
|
+
|
|
142
|
+
store = vectorstore_wrapper.vectorstore
|
|
143
|
+
with Session(store.session_maker.bind) as session:
|
|
144
|
+
session.query(store.EmbeddingStore).filter(
|
|
145
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name
|
|
146
|
+
).delete(synchronize_session=False)
|
|
147
|
+
session.commit()
|
|
128
148
|
|
|
129
149
|
def is_vectorstore_type(self, vectorstore) -> bool:
|
|
130
150
|
"""Check if the vectorstore is a PGVector store."""
|
|
131
151
|
return hasattr(vectorstore, 'session_maker') and hasattr(vectorstore, 'EmbeddingStore')
|
|
132
152
|
|
|
133
|
-
def get_indexed_data(self, vectorstore_wrapper,
|
|
134
|
-
"""Get all indexed data from PGVector for non-code content per
|
|
153
|
+
def get_indexed_data(self, vectorstore_wrapper, index_name: str)-> Dict[str, Dict[str, Any]]:
|
|
154
|
+
"""Get all indexed data from PGVector for non-code content per index_name."""
|
|
135
155
|
from sqlalchemy.orm import Session
|
|
136
156
|
from sqlalchemy import func
|
|
137
157
|
from ...runtime.utils.utils import IndexerKeywords
|
|
138
158
|
|
|
139
159
|
result = {}
|
|
140
160
|
try:
|
|
141
|
-
vectorstore_wrapper.
|
|
161
|
+
vectorstore_wrapper._log_tool_event("Retrieving already indexed data from PGVector vectorstore",
|
|
142
162
|
tool_name="get_indexed_data")
|
|
143
163
|
store = vectorstore_wrapper.vectorstore
|
|
144
164
|
with Session(store.session_maker.bind) as session:
|
|
@@ -147,7 +167,7 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
147
167
|
store.EmbeddingStore.document,
|
|
148
168
|
store.EmbeddingStore.cmetadata
|
|
149
169
|
).filter(
|
|
150
|
-
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') ==
|
|
170
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name
|
|
151
171
|
).all()
|
|
152
172
|
|
|
153
173
|
# Process the retrieved data
|
|
@@ -180,14 +200,14 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
180
200
|
|
|
181
201
|
return result
|
|
182
202
|
|
|
183
|
-
def get_code_indexed_data(self, vectorstore_wrapper,
|
|
203
|
+
def get_code_indexed_data(self, vectorstore_wrapper, index_name: str) -> Dict[str, Dict[str, Any]]:
|
|
184
204
|
"""Get all indexed code data from PGVector per collection suffix."""
|
|
185
205
|
from sqlalchemy.orm import Session
|
|
186
206
|
from sqlalchemy import func
|
|
187
207
|
|
|
188
208
|
result = {}
|
|
189
209
|
try:
|
|
190
|
-
vectorstore_wrapper.
|
|
210
|
+
vectorstore_wrapper._log_tool_event(message="Retrieving already indexed code data from PGVector vectorstore",
|
|
191
211
|
tool_name="index_code_data")
|
|
192
212
|
store = vectorstore_wrapper.vectorstore
|
|
193
213
|
with (Session(store.session_maker.bind) as session):
|
|
@@ -195,7 +215,7 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
195
215
|
store.EmbeddingStore.id,
|
|
196
216
|
store.EmbeddingStore.cmetadata
|
|
197
217
|
).filter(
|
|
198
|
-
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') ==
|
|
218
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name
|
|
199
219
|
).all()
|
|
200
220
|
|
|
201
221
|
for db_id, meta in docs:
|
|
@@ -265,6 +285,29 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
265
285
|
except Exception as e:
|
|
266
286
|
logger.error(f"Failed to update collection for entry ID {entry_id}: {str(e)}")
|
|
267
287
|
|
|
288
|
+
def get_index_meta(self, vectorstore_wrapper, index_name: str) -> List[Dict[str, Any]]:
|
|
289
|
+
from sqlalchemy.orm import Session
|
|
290
|
+
from sqlalchemy import func
|
|
291
|
+
|
|
292
|
+
store = vectorstore_wrapper.vectorstore
|
|
293
|
+
try:
|
|
294
|
+
with Session(store.session_maker.bind) as session:
|
|
295
|
+
meta = session.query(
|
|
296
|
+
store.EmbeddingStore.id,
|
|
297
|
+
store.EmbeddingStore.document,
|
|
298
|
+
store.EmbeddingStore.cmetadata
|
|
299
|
+
).filter(
|
|
300
|
+
store.EmbeddingStore.cmetadata['type'].astext == IndexerKeywords.INDEX_META_TYPE.value,
|
|
301
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name
|
|
302
|
+
).all()
|
|
303
|
+
result = []
|
|
304
|
+
for id, document, cmetadata in meta:
|
|
305
|
+
result.append({"id": id, "content": document, "metadata": cmetadata})
|
|
306
|
+
return result
|
|
307
|
+
except Exception as e:
|
|
308
|
+
logger.error(f"Failed to get index_meta from PGVector: {str(e)}")
|
|
309
|
+
raise e
|
|
310
|
+
|
|
268
311
|
|
|
269
312
|
class ChromaAdapter(VectorStoreAdapter):
|
|
270
313
|
"""Adapter for Chroma database operations."""
|
|
@@ -282,7 +325,7 @@ class ChromaAdapter(VectorStoreAdapter):
|
|
|
282
325
|
def remove_collection(self, vectorstore_wrapper, collection_name: str):
|
|
283
326
|
vectorstore_wrapper.vectorstore.delete_collection()
|
|
284
327
|
|
|
285
|
-
def get_indexed_ids(self, vectorstore_wrapper,
|
|
328
|
+
def get_indexed_ids(self, vectorstore_wrapper, index_name: Optional[str] = '') -> List[str]:
|
|
286
329
|
"""Get all indexed document IDs from Chroma"""
|
|
287
330
|
try:
|
|
288
331
|
data = vectorstore_wrapper.vectorstore.get(include=[]) # Only get IDs, no metadata
|
|
@@ -291,9 +334,9 @@ class ChromaAdapter(VectorStoreAdapter):
|
|
|
291
334
|
logger.error(f"Failed to get indexed IDs from Chroma: {str(e)}")
|
|
292
335
|
return []
|
|
293
336
|
|
|
294
|
-
def clean_collection(self, vectorstore_wrapper,
|
|
337
|
+
def clean_collection(self, vectorstore_wrapper, index_name: str = ''):
|
|
295
338
|
"""Clean the vectorstore collection by deleting all indexed data."""
|
|
296
|
-
vectorstore_wrapper.vectorstore.delete(ids=self.get_indexed_ids(vectorstore_wrapper,
|
|
339
|
+
vectorstore_wrapper.vectorstore.delete(ids=self.get_indexed_ids(vectorstore_wrapper, index_name))
|
|
297
340
|
|
|
298
341
|
def get_indexed_data(self, vectorstore_wrapper):
|
|
299
342
|
"""Get all indexed data from Chroma for non-code content"""
|
|
@@ -331,7 +374,7 @@ class ChromaAdapter(VectorStoreAdapter):
|
|
|
331
374
|
|
|
332
375
|
return result
|
|
333
376
|
|
|
334
|
-
def get_code_indexed_data(self, vectorstore_wrapper,
|
|
377
|
+
def get_code_indexed_data(self, vectorstore_wrapper, index_name) -> Dict[str, Dict[str, Any]]:
|
|
335
378
|
"""Get all indexed code data from Chroma."""
|
|
336
379
|
result = {}
|
|
337
380
|
try:
|
|
@@ -361,6 +404,9 @@ class ChromaAdapter(VectorStoreAdapter):
|
|
|
361
404
|
# This is a simplified implementation - in practice, you might need more complex logic
|
|
362
405
|
logger.warning("add_to_collection for Chroma is not fully implemented yet")
|
|
363
406
|
|
|
407
|
+
def get_index_meta(self, vectorstore_wrapper, index_name: str) -> List[Dict[str, Any]]:
|
|
408
|
+
logger.warning("get_index_meta for Chroma is not implemented yet")
|
|
409
|
+
|
|
364
410
|
|
|
365
411
|
class VectorStoreAdapterFactory:
|
|
366
412
|
"""Factory for creating vector store adapters."""
|
alita_sdk/tools/xray/__init__.py
CHANGED
|
@@ -56,7 +56,8 @@ class XrayToolkit(BaseToolkit):
|
|
|
56
56
|
{
|
|
57
57
|
'metadata': {
|
|
58
58
|
"label": "XRAY cloud", "icon_url": "xray.svg",
|
|
59
|
-
|
|
59
|
+
"max_length": XrayToolkit.toolkit_max_length,
|
|
60
|
+
"categories": ["test management"],
|
|
60
61
|
"extra_categories": ["test automation", "test case management", "test planning"]
|
|
61
62
|
}
|
|
62
63
|
}
|
|
@@ -31,7 +31,7 @@ class ZephyrToolkit(BaseToolkit):
|
|
|
31
31
|
ZephyrToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
|
|
32
32
|
return create_model(
|
|
33
33
|
name,
|
|
34
|
-
base_url=(str, Field(description="Base URL"
|
|
34
|
+
base_url=(str, Field(description="Base URL")),
|
|
35
35
|
username=(str, Field(description="Username")),
|
|
36
36
|
password=(SecretStr, Field(description="Password", json_schema_extra={'secret': True})),
|
|
37
37
|
selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
|
|
@@ -40,6 +40,7 @@ class ZephyrToolkit(BaseToolkit):
|
|
|
40
40
|
{
|
|
41
41
|
'metadata': {
|
|
42
42
|
"label": "Zephyr", "icon_url": "zephyr.svg", "hidden": True,
|
|
43
|
+
"max_length": ZephyrToolkit.toolkit_max_length,
|
|
43
44
|
"categories": ["test management"],
|
|
44
45
|
"extra_categories": ["test automation", "test case management", "test planning"]
|
|
45
46
|
}}}
|
|
@@ -49,6 +49,7 @@ class ZephyrEnterpriseToolkit(BaseToolkit):
|
|
|
49
49
|
__config__=ConfigDict(json_schema_extra={
|
|
50
50
|
'metadata': {
|
|
51
51
|
"label": "Zephyr Enterprise", "icon_url": "zephyr.svg",
|
|
52
|
+
"max_length": ZephyrEnterpriseToolkit.toolkit_max_length,
|
|
52
53
|
"categories": ["test management"],
|
|
53
54
|
"extra_categories": ["test automation", "test case management", "test planning"]
|
|
54
55
|
}})
|
|
@@ -46,6 +46,7 @@ class ZephyrEssentialToolkit(BaseToolkit):
|
|
|
46
46
|
# embedder settings
|
|
47
47
|
embedding_model=(Optional[str], Field(default=None, description="Embedding configuration.", json_schema_extra={'configuration_model': 'embedding'})),
|
|
48
48
|
__config__={'json_schema_extra': {'metadata': {"label": "Zephyr Essential", "icon_url": "zephyr.svg",
|
|
49
|
+
"max_length": ZephyrEssentialToolkit.toolkit_max_length,
|
|
49
50
|
"categories": ["test management"],
|
|
50
51
|
"extra_categories": ["test automation", "test case management", "test planning"]
|
|
51
52
|
}}}
|
|
@@ -56,6 +56,7 @@ class ZephyrScaleToolkit(BaseToolkit):
|
|
|
56
56
|
'metadata': {
|
|
57
57
|
"label": "Zephyr Scale",
|
|
58
58
|
"icon_url": "zephyr.svg",
|
|
59
|
+
"max_length": ZephyrScaleToolkit.toolkit_max_length,
|
|
59
60
|
"categories": ["test management"],
|
|
60
61
|
"extra_categories": ["test automation", "test case management", "test planning"],
|
|
61
62
|
}
|
|
@@ -34,6 +34,7 @@ class ZephyrSquadToolkit(BaseToolkit):
|
|
|
34
34
|
secret_key=(SecretStr, Field(description="Generated secret key", json_schema_extra={'secret': True})),
|
|
35
35
|
selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
|
|
36
36
|
__config__={'json_schema_extra': {'metadata': {"label": "Zephyr Squad", "icon_url": "zephyr.svg",
|
|
37
|
+
"max_length": ZephyrSquadToolkit.toolkit_max_length,
|
|
37
38
|
"categories": ["test management"],
|
|
38
39
|
"extra_categories": ["test automation", "test case management", "test planning"]
|
|
39
40
|
}}}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alita_sdk
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.462
|
|
4
4
|
Summary: SDK for building langchain agents using resources from Alita
|
|
5
5
|
Author-email: Artem Rozumenko <artyom.rozumenko@gmail.com>, Mikalai Biazruchka <mikalai_biazruchka@epam.com>, Roman Mitusov <roman_mitusov@epam.com>, Ivan Krakhmaliuk <lifedj27@gmail.com>, Artem Dubrovskiy <ad13box@gmail.com>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -18,12 +18,13 @@ Requires-Dist: python-dotenv~=1.0.1
|
|
|
18
18
|
Requires-Dist: jinja2~=3.1.3
|
|
19
19
|
Requires-Dist: pillow~=11.1.0
|
|
20
20
|
Requires-Dist: requests~=2.3
|
|
21
|
-
Requires-Dist: pydantic~=2.
|
|
21
|
+
Requires-Dist: pydantic~=2.12.0
|
|
22
22
|
Requires-Dist: chardet==5.2.0
|
|
23
23
|
Requires-Dist: fastapi==0.115.9
|
|
24
24
|
Requires-Dist: httpcore==1.0.7
|
|
25
25
|
Requires-Dist: urllib3>=2
|
|
26
26
|
Requires-Dist: certifi==2024.8.30
|
|
27
|
+
Requires-Dist: aiohttp>=3.9.0
|
|
27
28
|
Provides-Extra: runtime
|
|
28
29
|
Requires-Dist: streamlit>=1.28.0; extra == "runtime"
|
|
29
30
|
Requires-Dist: langchain_core<0.4.0,>=0.3.76; extra == "runtime"
|
|
@@ -143,6 +144,11 @@ Requires-Dist: pytest-cov; extra == "dev"
|
|
|
143
144
|
Requires-Dist: black; extra == "dev"
|
|
144
145
|
Requires-Dist: flake8; extra == "dev"
|
|
145
146
|
Requires-Dist: mypy; extra == "dev"
|
|
147
|
+
Provides-Extra: cli
|
|
148
|
+
Requires-Dist: click>=8.1.0; extra == "cli"
|
|
149
|
+
Requires-Dist: rich>=13.0.0; extra == "cli"
|
|
150
|
+
Requires-Dist: pyyaml>=6.0; extra == "cli"
|
|
151
|
+
Requires-Dist: langchain-mcp-adapters; extra == "cli"
|
|
146
152
|
Dynamic: license-file
|
|
147
153
|
|
|
148
154
|
Alita SDK
|