alita-sdk 0.3.263__py3-none-any.whl → 0.3.499__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.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- alita_sdk/cli/agent/__init__.py +5 -0
- alita_sdk/cli/agent/default.py +258 -0
- alita_sdk/cli/agent_executor.py +155 -0
- alita_sdk/cli/agent_loader.py +215 -0
- alita_sdk/cli/agent_ui.py +228 -0
- alita_sdk/cli/agents.py +3601 -0
- alita_sdk/cli/callbacks.py +647 -0
- alita_sdk/cli/cli.py +168 -0
- alita_sdk/cli/config.py +306 -0
- alita_sdk/cli/context/__init__.py +30 -0
- alita_sdk/cli/context/cleanup.py +198 -0
- alita_sdk/cli/context/manager.py +731 -0
- alita_sdk/cli/context/message.py +285 -0
- alita_sdk/cli/context/strategies.py +289 -0
- alita_sdk/cli/context/token_estimation.py +127 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/input_handler.py +419 -0
- alita_sdk/cli/inventory.py +1256 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/toolkit.py +327 -0
- alita_sdk/cli/toolkit_loader.py +85 -0
- alita_sdk/cli/tools/__init__.py +43 -0
- alita_sdk/cli/tools/approval.py +224 -0
- alita_sdk/cli/tools/filesystem.py +1751 -0
- alita_sdk/cli/tools/planning.py +389 -0
- alita_sdk/cli/tools/terminal.py +414 -0
- alita_sdk/community/__init__.py +64 -8
- alita_sdk/community/inventory/__init__.py +224 -0
- alita_sdk/community/inventory/config.py +257 -0
- alita_sdk/community/inventory/enrichment.py +2137 -0
- alita_sdk/community/inventory/extractors.py +1469 -0
- alita_sdk/community/inventory/ingestion.py +3172 -0
- alita_sdk/community/inventory/knowledge_graph.py +1457 -0
- alita_sdk/community/inventory/parsers/__init__.py +218 -0
- alita_sdk/community/inventory/parsers/base.py +295 -0
- alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
- alita_sdk/community/inventory/parsers/go_parser.py +851 -0
- alita_sdk/community/inventory/parsers/html_parser.py +389 -0
- alita_sdk/community/inventory/parsers/java_parser.py +593 -0
- alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
- alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
- alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
- alita_sdk/community/inventory/parsers/python_parser.py +604 -0
- alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
- alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
- alita_sdk/community/inventory/parsers/text_parser.py +322 -0
- alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
- alita_sdk/community/inventory/patterns/__init__.py +61 -0
- alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
- alita_sdk/community/inventory/patterns/loader.py +348 -0
- alita_sdk/community/inventory/patterns/registry.py +198 -0
- alita_sdk/community/inventory/presets.py +535 -0
- alita_sdk/community/inventory/retrieval.py +1403 -0
- alita_sdk/community/inventory/toolkit.py +173 -0
- alita_sdk/community/inventory/visualize.py +1370 -0
- alita_sdk/configurations/__init__.py +10 -0
- alita_sdk/configurations/ado.py +4 -2
- alita_sdk/configurations/azure_search.py +1 -1
- alita_sdk/configurations/bigquery.py +1 -1
- alita_sdk/configurations/bitbucket.py +94 -2
- alita_sdk/configurations/browser.py +18 -0
- alita_sdk/configurations/carrier.py +19 -0
- alita_sdk/configurations/confluence.py +96 -1
- alita_sdk/configurations/delta_lake.py +1 -1
- alita_sdk/configurations/figma.py +0 -5
- alita_sdk/configurations/github.py +65 -1
- alita_sdk/configurations/gitlab.py +79 -0
- alita_sdk/configurations/google_places.py +17 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/postman.py +1 -1
- alita_sdk/configurations/qtest.py +1 -3
- alita_sdk/configurations/report_portal.py +19 -0
- alita_sdk/configurations/salesforce.py +19 -0
- alita_sdk/configurations/service_now.py +1 -12
- alita_sdk/configurations/sharepoint.py +19 -0
- alita_sdk/configurations/sonar.py +18 -0
- alita_sdk/configurations/sql.py +20 -0
- alita_sdk/configurations/testio.py +18 -0
- alita_sdk/configurations/testrail.py +88 -0
- alita_sdk/configurations/xray.py +94 -1
- alita_sdk/configurations/zephyr_enterprise.py +94 -1
- alita_sdk/configurations/zephyr_essential.py +95 -0
- alita_sdk/runtime/clients/artifact.py +12 -2
- alita_sdk/runtime/clients/client.py +235 -66
- 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 +123 -17
- alita_sdk/runtime/langchain/constants.py +8 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
- alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +8 -2
- alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
- alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
- alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
- alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
- alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
- alita_sdk/runtime/langchain/document_loaders/constants.py +187 -40
- alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
- alita_sdk/runtime/langchain/langraph_agent.py +406 -91
- alita_sdk/runtime/langchain/utils.py +51 -8
- alita_sdk/runtime/llms/preloaded.py +2 -6
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/toolkits/__init__.py +26 -0
- alita_sdk/runtime/toolkits/application.py +9 -2
- alita_sdk/runtime/toolkits/artifact.py +19 -7
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +780 -0
- alita_sdk/runtime/toolkits/planning.py +178 -0
- alita_sdk/runtime/toolkits/subgraph.py +11 -6
- alita_sdk/runtime/toolkits/tools.py +214 -60
- alita_sdk/runtime/toolkits/vectorstore.py +9 -4
- alita_sdk/runtime/tools/__init__.py +22 -0
- alita_sdk/runtime/tools/application.py +16 -4
- alita_sdk/runtime/tools/artifact.py +312 -19
- alita_sdk/runtime/tools/function.py +100 -4
- alita_sdk/runtime/tools/graph.py +81 -0
- alita_sdk/runtime/tools/image_generation.py +212 -0
- alita_sdk/runtime/tools/llm.py +539 -180
- alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
- alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
- alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
- alita_sdk/runtime/tools/planning/__init__.py +36 -0
- alita_sdk/runtime/tools/planning/models.py +246 -0
- alita_sdk/runtime/tools/planning/wrapper.py +607 -0
- alita_sdk/runtime/tools/router.py +2 -1
- alita_sdk/runtime/tools/sandbox.py +375 -0
- alita_sdk/runtime/tools/vectorstore.py +62 -63
- alita_sdk/runtime/tools/vectorstore_base.py +156 -85
- alita_sdk/runtime/utils/AlitaCallback.py +106 -20
- alita_sdk/runtime/utils/mcp_client.py +465 -0
- alita_sdk/runtime/utils/mcp_oauth.py +244 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +405 -0
- alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
- alita_sdk/runtime/utils/streamlit.py +41 -14
- alita_sdk/runtime/utils/toolkit_utils.py +28 -9
- alita_sdk/runtime/utils/utils.py +14 -0
- alita_sdk/tools/__init__.py +78 -35
- alita_sdk/tools/ado/__init__.py +0 -1
- alita_sdk/tools/ado/repos/__init__.py +10 -6
- alita_sdk/tools/ado/repos/repos_wrapper.py +12 -11
- alita_sdk/tools/ado/test_plan/__init__.py +10 -7
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -23
- alita_sdk/tools/ado/wiki/__init__.py +10 -11
- alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -28
- alita_sdk/tools/ado/work_item/__init__.py +10 -11
- alita_sdk/tools/ado/work_item/ado_wrapper.py +63 -10
- alita_sdk/tools/advanced_jira_mining/__init__.py +10 -7
- alita_sdk/tools/aws/delta_lake/__init__.py +13 -11
- alita_sdk/tools/azure_ai/search/__init__.py +11 -7
- alita_sdk/tools/base_indexer_toolkit.py +392 -86
- alita_sdk/tools/bitbucket/__init__.py +18 -11
- alita_sdk/tools/bitbucket/api_wrapper.py +52 -9
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
- alita_sdk/tools/browser/__init__.py +40 -16
- alita_sdk/tools/browser/crawler.py +3 -1
- alita_sdk/tools/browser/utils.py +15 -6
- alita_sdk/tools/carrier/__init__.py +17 -17
- alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
- alita_sdk/tools/carrier/excel_reporter.py +8 -4
- alita_sdk/tools/chunkers/__init__.py +3 -1
- alita_sdk/tools/chunkers/code/codeparser.py +1 -1
- alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
- alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
- alita_sdk/tools/chunkers/universal_chunker.py +270 -0
- alita_sdk/tools/cloud/aws/__init__.py +9 -6
- alita_sdk/tools/cloud/azure/__init__.py +9 -6
- alita_sdk/tools/cloud/gcp/__init__.py +9 -6
- alita_sdk/tools/cloud/k8s/__init__.py +9 -6
- alita_sdk/tools/code/linter/__init__.py +7 -7
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +18 -12
- alita_sdk/tools/code_indexer_toolkit.py +199 -0
- alita_sdk/tools/confluence/__init__.py +14 -11
- alita_sdk/tools/confluence/api_wrapper.py +198 -58
- alita_sdk/tools/confluence/loader.py +10 -0
- alita_sdk/tools/custom_open_api/__init__.py +9 -4
- alita_sdk/tools/elastic/__init__.py +8 -7
- alita_sdk/tools/elitea_base.py +543 -64
- alita_sdk/tools/figma/__init__.py +10 -8
- alita_sdk/tools/figma/api_wrapper.py +352 -153
- alita_sdk/tools/github/__init__.py +13 -11
- alita_sdk/tools/github/api_wrapper.py +9 -26
- alita_sdk/tools/github/github_client.py +75 -12
- alita_sdk/tools/github/schemas.py +2 -1
- alita_sdk/tools/gitlab/__init__.py +11 -10
- alita_sdk/tools/gitlab/api_wrapper.py +135 -45
- alita_sdk/tools/gitlab_org/__init__.py +11 -9
- alita_sdk/tools/google/bigquery/__init__.py +12 -13
- alita_sdk/tools/google_places/__init__.py +18 -10
- alita_sdk/tools/jira/__init__.py +14 -8
- alita_sdk/tools/jira/api_wrapper.py +315 -168
- alita_sdk/tools/keycloak/__init__.py +8 -7
- alita_sdk/tools/localgit/local_git.py +56 -54
- alita_sdk/tools/memory/__init__.py +27 -11
- alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
- alita_sdk/tools/ocr/__init__.py +8 -7
- alita_sdk/tools/openapi/__init__.py +10 -1
- alita_sdk/tools/pandas/__init__.py +8 -7
- alita_sdk/tools/pandas/api_wrapper.py +7 -25
- alita_sdk/tools/postman/__init__.py +8 -10
- alita_sdk/tools/postman/api_wrapper.py +19 -8
- alita_sdk/tools/postman/postman_analysis.py +8 -1
- alita_sdk/tools/pptx/__init__.py +8 -9
- alita_sdk/tools/qtest/__init__.py +19 -13
- alita_sdk/tools/qtest/api_wrapper.py +1784 -88
- alita_sdk/tools/rally/__init__.py +10 -9
- alita_sdk/tools/report_portal/__init__.py +20 -15
- alita_sdk/tools/salesforce/__init__.py +19 -15
- alita_sdk/tools/servicenow/__init__.py +14 -11
- alita_sdk/tools/sharepoint/__init__.py +14 -13
- alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
- alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
- alita_sdk/tools/sharepoint/utils.py +8 -2
- alita_sdk/tools/slack/__init__.py +10 -7
- alita_sdk/tools/sql/__init__.py +19 -18
- alita_sdk/tools/sql/api_wrapper.py +71 -23
- alita_sdk/tools/testio/__init__.py +18 -12
- alita_sdk/tools/testrail/__init__.py +10 -10
- alita_sdk/tools/testrail/api_wrapper.py +213 -45
- alita_sdk/tools/utils/__init__.py +28 -4
- alita_sdk/tools/utils/content_parser.py +181 -61
- alita_sdk/tools/utils/text_operations.py +254 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
- alita_sdk/tools/xray/__init__.py +12 -7
- alita_sdk/tools/xray/api_wrapper.py +58 -113
- alita_sdk/tools/zephyr/__init__.py +9 -6
- alita_sdk/tools/zephyr_enterprise/__init__.py +13 -8
- alita_sdk/tools/zephyr_enterprise/api_wrapper.py +17 -7
- alita_sdk/tools/zephyr_essential/__init__.py +13 -9
- alita_sdk/tools/zephyr_essential/api_wrapper.py +289 -47
- alita_sdk/tools/zephyr_essential/client.py +6 -4
- alita_sdk/tools/zephyr_scale/__init__.py +10 -7
- alita_sdk/tools/zephyr_scale/api_wrapper.py +6 -2
- alita_sdk/tools/zephyr_squad/__init__.py +9 -6
- {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/METADATA +180 -33
- alita_sdk-0.3.499.dist-info/RECORD +433 -0
- alita_sdk-0.3.499.dist-info/entry_points.txt +2 -0
- alita_sdk-0.3.263.dist-info/RECORD +0 -342
- {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/top_level.txt +0 -0
|
@@ -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,13 +26,13 @@ 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,
|
|
33
|
-
"""Clean the vectorstore collection by deleting all indexed data."""
|
|
34
|
+
def clean_collection(self, vectorstore_wrapper, index_name: str = '', including_index_meta: bool = False):
|
|
35
|
+
"""Clean the vectorstore collection by deleting all indexed data. If including_index_meta is True, skip the index_meta records."""
|
|
34
36
|
pass
|
|
35
37
|
|
|
36
38
|
@abstractmethod
|
|
@@ -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,15 +50,26 @@ 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."""
|
|
54
61
|
|
|
55
62
|
def get_vectorstore_params(self, collection_name: str, connection_string: Optional[str] = None) -> Dict[str, Any]:
|
|
63
|
+
try:
|
|
64
|
+
from tools import this # pylint: disable=E0401,C0415
|
|
65
|
+
worker_config = this.for_module("indexer_worker").descriptor.config
|
|
66
|
+
except: # pylint: disable=W0702
|
|
67
|
+
worker_config = {}
|
|
68
|
+
#
|
|
56
69
|
return {
|
|
57
70
|
"use_jsonb": True,
|
|
58
71
|
"collection_name": collection_name,
|
|
59
|
-
"create_extension": True,
|
|
72
|
+
"create_extension": worker_config.get("pgvector_create_extension", True),
|
|
60
73
|
"alita_sdk_options": {
|
|
61
74
|
"target_schema": collection_name,
|
|
62
75
|
},
|
|
@@ -93,20 +106,25 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
93
106
|
session.commit()
|
|
94
107
|
logger.info(f"Schema '{schema_name}' has been dropped.")
|
|
95
108
|
|
|
96
|
-
def get_indexed_ids(self, vectorstore_wrapper,
|
|
109
|
+
def get_indexed_ids(self, vectorstore_wrapper, index_name: Optional[str] = '') -> List[str]:
|
|
97
110
|
"""Get all indexed document IDs from PGVector"""
|
|
98
111
|
from sqlalchemy.orm import Session
|
|
99
|
-
from sqlalchemy import func
|
|
112
|
+
from sqlalchemy import func, or_
|
|
100
113
|
|
|
101
114
|
store = vectorstore_wrapper.vectorstore
|
|
102
115
|
try:
|
|
103
116
|
with Session(store.session_maker.bind) as session:
|
|
104
117
|
# Start building the query
|
|
105
118
|
query = session.query(store.EmbeddingStore.id)
|
|
106
|
-
# Apply filter only if
|
|
107
|
-
if
|
|
119
|
+
# Apply filter only if index_name is provided
|
|
120
|
+
if index_name:
|
|
108
121
|
query = query.filter(
|
|
109
|
-
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
|
+
)
|
|
110
128
|
)
|
|
111
129
|
ids = query.all()
|
|
112
130
|
return [str(id_tuple[0]) for id_tuple in ids]
|
|
@@ -114,25 +132,37 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
114
132
|
logger.error(f"Failed to get indexed IDs from PGVector: {str(e)}")
|
|
115
133
|
return []
|
|
116
134
|
|
|
117
|
-
def clean_collection(self, vectorstore_wrapper,
|
|
118
|
-
"""Clean the vectorstore collection by deleting all indexed data."""
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
vectorstore_wrapper.vectorstore
|
|
135
|
+
def clean_collection(self, vectorstore_wrapper, index_name: str = '', including_index_meta: bool = False):
|
|
136
|
+
"""Clean the vectorstore collection by deleting all indexed data. If including_index_meta is True, skip the index_meta records."""
|
|
137
|
+
from sqlalchemy.orm import Session
|
|
138
|
+
from sqlalchemy import func, or_
|
|
139
|
+
store = vectorstore_wrapper.vectorstore
|
|
140
|
+
with Session(store.session_maker.bind) as session:
|
|
141
|
+
if including_index_meta:
|
|
142
|
+
session.query(store.EmbeddingStore).filter(
|
|
143
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name
|
|
144
|
+
).delete(synchronize_session=False)
|
|
145
|
+
else:
|
|
146
|
+
session.query(store.EmbeddingStore).filter(
|
|
147
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name,
|
|
148
|
+
or_(func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'type').is_(None),
|
|
149
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'type') != IndexerKeywords.INDEX_META_TYPE.value)
|
|
150
|
+
).delete(synchronize_session=False)
|
|
151
|
+
session.commit()
|
|
122
152
|
|
|
123
153
|
def is_vectorstore_type(self, vectorstore) -> bool:
|
|
124
154
|
"""Check if the vectorstore is a PGVector store."""
|
|
125
155
|
return hasattr(vectorstore, 'session_maker') and hasattr(vectorstore, 'EmbeddingStore')
|
|
126
156
|
|
|
127
|
-
def get_indexed_data(self, vectorstore_wrapper,
|
|
128
|
-
"""Get all indexed data from PGVector for non-code content per
|
|
157
|
+
def get_indexed_data(self, vectorstore_wrapper, index_name: str)-> Dict[str, Dict[str, Any]]:
|
|
158
|
+
"""Get all indexed data from PGVector for non-code content per index_name."""
|
|
129
159
|
from sqlalchemy.orm import Session
|
|
130
160
|
from sqlalchemy import func
|
|
131
161
|
from ...runtime.utils.utils import IndexerKeywords
|
|
132
162
|
|
|
133
163
|
result = {}
|
|
134
164
|
try:
|
|
135
|
-
vectorstore_wrapper.
|
|
165
|
+
vectorstore_wrapper._log_tool_event("Retrieving already indexed data from PGVector vectorstore",
|
|
136
166
|
tool_name="get_indexed_data")
|
|
137
167
|
store = vectorstore_wrapper.vectorstore
|
|
138
168
|
with Session(store.session_maker.bind) as session:
|
|
@@ -141,7 +171,7 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
141
171
|
store.EmbeddingStore.document,
|
|
142
172
|
store.EmbeddingStore.cmetadata
|
|
143
173
|
).filter(
|
|
144
|
-
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') ==
|
|
174
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name
|
|
145
175
|
).all()
|
|
146
176
|
|
|
147
177
|
# Process the retrieved data
|
|
@@ -174,14 +204,14 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
174
204
|
|
|
175
205
|
return result
|
|
176
206
|
|
|
177
|
-
def get_code_indexed_data(self, vectorstore_wrapper,
|
|
207
|
+
def get_code_indexed_data(self, vectorstore_wrapper, index_name: str) -> Dict[str, Dict[str, Any]]:
|
|
178
208
|
"""Get all indexed code data from PGVector per collection suffix."""
|
|
179
209
|
from sqlalchemy.orm import Session
|
|
180
210
|
from sqlalchemy import func
|
|
181
211
|
|
|
182
212
|
result = {}
|
|
183
213
|
try:
|
|
184
|
-
vectorstore_wrapper.
|
|
214
|
+
vectorstore_wrapper._log_tool_event(message="Retrieving already indexed code data from PGVector vectorstore",
|
|
185
215
|
tool_name="index_code_data")
|
|
186
216
|
store = vectorstore_wrapper.vectorstore
|
|
187
217
|
with (Session(store.session_maker.bind) as session):
|
|
@@ -189,7 +219,7 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
189
219
|
store.EmbeddingStore.id,
|
|
190
220
|
store.EmbeddingStore.cmetadata
|
|
191
221
|
).filter(
|
|
192
|
-
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') ==
|
|
222
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name
|
|
193
223
|
).all()
|
|
194
224
|
|
|
195
225
|
for db_id, meta in docs:
|
|
@@ -259,6 +289,29 @@ class PGVectorAdapter(VectorStoreAdapter):
|
|
|
259
289
|
except Exception as e:
|
|
260
290
|
logger.error(f"Failed to update collection for entry ID {entry_id}: {str(e)}")
|
|
261
291
|
|
|
292
|
+
def get_index_meta(self, vectorstore_wrapper, index_name: str) -> List[Dict[str, Any]]:
|
|
293
|
+
from sqlalchemy.orm import Session
|
|
294
|
+
from sqlalchemy import func
|
|
295
|
+
|
|
296
|
+
store = vectorstore_wrapper.vectorstore
|
|
297
|
+
try:
|
|
298
|
+
with Session(store.session_maker.bind) as session:
|
|
299
|
+
meta = session.query(
|
|
300
|
+
store.EmbeddingStore.id,
|
|
301
|
+
store.EmbeddingStore.document,
|
|
302
|
+
store.EmbeddingStore.cmetadata
|
|
303
|
+
).filter(
|
|
304
|
+
store.EmbeddingStore.cmetadata['type'].astext == IndexerKeywords.INDEX_META_TYPE.value,
|
|
305
|
+
func.jsonb_extract_path_text(store.EmbeddingStore.cmetadata, 'collection') == index_name
|
|
306
|
+
).all()
|
|
307
|
+
result = []
|
|
308
|
+
for id, document, cmetadata in meta:
|
|
309
|
+
result.append({"id": id, "content": document, "metadata": cmetadata})
|
|
310
|
+
return result
|
|
311
|
+
except Exception as e:
|
|
312
|
+
logger.error(f"Failed to get index_meta from PGVector: {str(e)}")
|
|
313
|
+
raise e
|
|
314
|
+
|
|
262
315
|
|
|
263
316
|
class ChromaAdapter(VectorStoreAdapter):
|
|
264
317
|
"""Adapter for Chroma database operations."""
|
|
@@ -276,7 +329,7 @@ class ChromaAdapter(VectorStoreAdapter):
|
|
|
276
329
|
def remove_collection(self, vectorstore_wrapper, collection_name: str):
|
|
277
330
|
vectorstore_wrapper.vectorstore.delete_collection()
|
|
278
331
|
|
|
279
|
-
def get_indexed_ids(self, vectorstore_wrapper,
|
|
332
|
+
def get_indexed_ids(self, vectorstore_wrapper, index_name: Optional[str] = '') -> List[str]:
|
|
280
333
|
"""Get all indexed document IDs from Chroma"""
|
|
281
334
|
try:
|
|
282
335
|
data = vectorstore_wrapper.vectorstore.get(include=[]) # Only get IDs, no metadata
|
|
@@ -285,9 +338,9 @@ class ChromaAdapter(VectorStoreAdapter):
|
|
|
285
338
|
logger.error(f"Failed to get indexed IDs from Chroma: {str(e)}")
|
|
286
339
|
return []
|
|
287
340
|
|
|
288
|
-
def clean_collection(self, vectorstore_wrapper,
|
|
289
|
-
"""Clean the vectorstore collection by deleting all indexed data."""
|
|
290
|
-
vectorstore_wrapper.vectorstore.delete(ids=self.get_indexed_ids(vectorstore_wrapper,
|
|
341
|
+
def clean_collection(self, vectorstore_wrapper, index_name: str = '', including_index_meta: bool = False):
|
|
342
|
+
"""Clean the vectorstore collection by deleting all indexed data. including_index_meta is ignored."""
|
|
343
|
+
vectorstore_wrapper.vectorstore.delete(ids=self.get_indexed_ids(vectorstore_wrapper, index_name))
|
|
291
344
|
|
|
292
345
|
def get_indexed_data(self, vectorstore_wrapper):
|
|
293
346
|
"""Get all indexed data from Chroma for non-code content"""
|
|
@@ -325,7 +378,7 @@ class ChromaAdapter(VectorStoreAdapter):
|
|
|
325
378
|
|
|
326
379
|
return result
|
|
327
380
|
|
|
328
|
-
def get_code_indexed_data(self, vectorstore_wrapper,
|
|
381
|
+
def get_code_indexed_data(self, vectorstore_wrapper, index_name) -> Dict[str, Dict[str, Any]]:
|
|
329
382
|
"""Get all indexed code data from Chroma."""
|
|
330
383
|
result = {}
|
|
331
384
|
try:
|
|
@@ -355,6 +408,9 @@ class ChromaAdapter(VectorStoreAdapter):
|
|
|
355
408
|
# This is a simplified implementation - in practice, you might need more complex logic
|
|
356
409
|
logger.warning("add_to_collection for Chroma is not fully implemented yet")
|
|
357
410
|
|
|
411
|
+
def get_index_meta(self, vectorstore_wrapper, index_name: str) -> List[Dict[str, Any]]:
|
|
412
|
+
logger.warning("get_index_meta for Chroma is not implemented yet")
|
|
413
|
+
|
|
358
414
|
|
|
359
415
|
class VectorStoreAdapterFactory:
|
|
360
416
|
"""Factory for creating vector store adapters."""
|
alita_sdk/tools/xray/__init__.py
CHANGED
|
@@ -7,7 +7,8 @@ from pydantic import create_model, BaseModel, Field
|
|
|
7
7
|
|
|
8
8
|
from .api_wrapper import XrayApiWrapper
|
|
9
9
|
from ..base.tool import BaseAction
|
|
10
|
-
from ..
|
|
10
|
+
from ..elitea_base import filter_missconfigured_index_tools
|
|
11
|
+
from ..utils import clean_string, get_max_toolkit_length
|
|
11
12
|
from ...configurations.pgvector import PgVectorConfiguration
|
|
12
13
|
from ...configurations.xray import XrayConfiguration
|
|
13
14
|
|
|
@@ -21,6 +22,7 @@ def get_tools(tool):
|
|
|
21
22
|
limit=tool['settings'].get('limit', 20),
|
|
22
23
|
verify_ssl=tool['settings'].get('verify_ssl', True),
|
|
23
24
|
toolkit_name=tool.get('toolkit_name'),
|
|
25
|
+
llm=tool['settings'].get('llm', None),
|
|
24
26
|
alita=tool['settings'].get('alita', None),
|
|
25
27
|
|
|
26
28
|
# indexer settings
|
|
@@ -32,12 +34,10 @@ def get_tools(tool):
|
|
|
32
34
|
|
|
33
35
|
class XrayToolkit(BaseToolkit):
|
|
34
36
|
tools: List[BaseTool] = []
|
|
35
|
-
toolkit_max_length: int = 0
|
|
36
37
|
|
|
37
38
|
@staticmethod
|
|
38
39
|
def toolkit_config_schema() -> BaseModel:
|
|
39
40
|
selected_tools = {x['name']: x['args_schema'].schema() for x in XrayApiWrapper.model_construct().get_available_tools()}
|
|
40
|
-
XrayToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
|
|
41
41
|
return create_model(
|
|
42
42
|
name,
|
|
43
43
|
limit=(Optional[int], Field(description="Limit", default=100)),
|
|
@@ -54,7 +54,7 @@ class XrayToolkit(BaseToolkit):
|
|
|
54
54
|
{
|
|
55
55
|
'metadata': {
|
|
56
56
|
"label": "XRAY cloud", "icon_url": "xray.svg",
|
|
57
|
-
|
|
57
|
+
"categories": ["test management"],
|
|
58
58
|
"extra_categories": ["test automation", "test case management", "test planning"]
|
|
59
59
|
}
|
|
60
60
|
}
|
|
@@ -62,6 +62,7 @@ class XrayToolkit(BaseToolkit):
|
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
@classmethod
|
|
65
|
+
@filter_missconfigured_index_tools
|
|
65
66
|
def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
|
|
66
67
|
if selected_tools is None:
|
|
67
68
|
selected_tools = []
|
|
@@ -72,17 +73,21 @@ class XrayToolkit(BaseToolkit):
|
|
|
72
73
|
**(kwargs.get('pgvector_configuration') or {}),
|
|
73
74
|
}
|
|
74
75
|
xray_api_wrapper = XrayApiWrapper(**wrapper_payload)
|
|
75
|
-
prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
|
|
76
76
|
available_tools = xray_api_wrapper.get_available_tools()
|
|
77
77
|
tools = []
|
|
78
78
|
for tool in available_tools:
|
|
79
79
|
if selected_tools:
|
|
80
80
|
if tool["name"] not in selected_tools:
|
|
81
81
|
continue
|
|
82
|
+
description = tool["description"]
|
|
83
|
+
if toolkit_name:
|
|
84
|
+
description = f"Toolkit: {toolkit_name}\n{description}"
|
|
85
|
+
description = description + "\nXray instance: " + xray_api_wrapper.base_url
|
|
86
|
+
description = description[:1000]
|
|
82
87
|
tools.append(BaseAction(
|
|
83
88
|
api_wrapper=xray_api_wrapper,
|
|
84
|
-
name=
|
|
85
|
-
description=
|
|
89
|
+
name=tool["name"],
|
|
90
|
+
description=description,
|
|
86
91
|
args_schema=tool["args_schema"]
|
|
87
92
|
))
|
|
88
93
|
return cls(tools=tools)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import logging
|
|
3
3
|
import hashlib
|
|
4
|
-
from typing import Any, Dict, Generator, List, Optional
|
|
4
|
+
from typing import Any, Dict, Generator, List, Optional, Literal
|
|
5
5
|
|
|
6
6
|
import requests
|
|
7
7
|
from langchain_core.documents import Document
|
|
@@ -9,12 +9,9 @@ from langchain_core.tools import ToolException
|
|
|
9
9
|
from pydantic import PrivateAttr, SecretStr, create_model, model_validator, Field
|
|
10
10
|
from python_graphql_client import GraphqlClient
|
|
11
11
|
|
|
12
|
-
from ..
|
|
13
|
-
|
|
14
|
-
extend_with_vector_tools,
|
|
15
|
-
)
|
|
12
|
+
from ..non_code_indexer_toolkit import NonCodeIndexerToolkit
|
|
13
|
+
from ..utils.available_tools_decorator import extend_with_parent_available_tools
|
|
16
14
|
from ...runtime.utils.utils import IndexerKeywords
|
|
17
|
-
from ..utils.content_parser import parse_file_content, load_content_from_bytes
|
|
18
15
|
|
|
19
16
|
try:
|
|
20
17
|
from alita_sdk.runtime.langchain.interfaces.llm_processor import get_embeddings
|
|
@@ -31,7 +28,7 @@ _get_tests_query = """query GetTests($jql: String!, $limit:Int!, $start: Int)
|
|
|
31
28
|
limit
|
|
32
29
|
results {
|
|
33
30
|
issueId
|
|
34
|
-
jira(fields: ["key", "summary", "created", "updated", "assignee.displayName", "reporter.displayName"])
|
|
31
|
+
jira(fields: ["key", "summary", "description", "created", "updated", "assignee.displayName", "reporter.displayName"])
|
|
35
32
|
projectId
|
|
36
33
|
testType {
|
|
37
34
|
name
|
|
@@ -107,7 +104,7 @@ XrayCreateTest = create_model(
|
|
|
107
104
|
|
|
108
105
|
XrayCreateTests = create_model(
|
|
109
106
|
"XrayCreateTests",
|
|
110
|
-
graphql_mutations=(
|
|
107
|
+
graphql_mutations=(List[str], Field(description="list of GraphQL mutations:\n" + _graphql_mutation_description))
|
|
111
108
|
)
|
|
112
109
|
|
|
113
110
|
def _parse_tests(test_results) -> List[Any]:
|
|
@@ -120,7 +117,7 @@ def _parse_tests(test_results) -> List[Any]:
|
|
|
120
117
|
return test_results
|
|
121
118
|
|
|
122
119
|
|
|
123
|
-
class XrayApiWrapper(
|
|
120
|
+
class XrayApiWrapper(NonCodeIndexerToolkit):
|
|
124
121
|
_default_base_url: str = 'https://xray.cloud.getxray.app'
|
|
125
122
|
base_url: str = ""
|
|
126
123
|
client_id: str = None
|
|
@@ -147,7 +144,7 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
147
144
|
client_id = values['client_id']
|
|
148
145
|
client_secret = values['client_secret']
|
|
149
146
|
# Authenticate to get the token
|
|
150
|
-
values['base_url'] = values.get('base_url', '') or cls._default_base_url
|
|
147
|
+
values['base_url'] = values.get('base_url', '') or cls._default_base_url.default
|
|
151
148
|
auth_url = f"{values['base_url']}/api/v1/authenticate"
|
|
152
149
|
auth_data = {
|
|
153
150
|
"client_id": client_id,
|
|
@@ -168,7 +165,7 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
168
165
|
return ToolException(f"Please, check you credentials ({values['client_id']} / {masked_secret}). Unable")
|
|
169
166
|
else:
|
|
170
167
|
return ToolException(f"Authentication failed: {str(e)}")
|
|
171
|
-
return values
|
|
168
|
+
return super().validate_toolkit(values)
|
|
172
169
|
|
|
173
170
|
def __init__(self, **data):
|
|
174
171
|
super().__init__(**data)
|
|
@@ -333,6 +330,7 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
333
330
|
|
|
334
331
|
for test in tests_data:
|
|
335
332
|
page_content = ""
|
|
333
|
+
content_structure = {}
|
|
336
334
|
test_type_name = test.get("testType", {}).get("name", "").lower()
|
|
337
335
|
|
|
338
336
|
attachment_ids = []
|
|
@@ -359,19 +357,16 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
359
357
|
content_structure = {"steps": steps_content}
|
|
360
358
|
if attachment_ids:
|
|
361
359
|
content_structure["attachment_ids"] = sorted(attachment_ids)
|
|
362
|
-
page_content = json.dumps(content_structure, indent=2)
|
|
363
360
|
|
|
364
361
|
elif test_type_name == "cucumber" and test.get("gherkin"):
|
|
365
362
|
content_structure = {"gherkin": test["gherkin"]}
|
|
366
363
|
if attachment_ids:
|
|
367
364
|
content_structure["attachment_ids"] = sorted(attachment_ids)
|
|
368
|
-
page_content = json.dumps(content_structure, indent=2)
|
|
369
365
|
|
|
370
366
|
elif test.get("unstructured"):
|
|
371
367
|
content_structure = {"unstructured": test["unstructured"]}
|
|
372
368
|
if attachment_ids:
|
|
373
369
|
content_structure["attachment_ids"] = sorted(attachment_ids)
|
|
374
|
-
page_content = json.dumps(content_structure, indent=2)
|
|
375
370
|
|
|
376
371
|
metadata = {"doctype": self.doctype}
|
|
377
372
|
|
|
@@ -382,7 +377,12 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
382
377
|
|
|
383
378
|
if "created" in jira_data:
|
|
384
379
|
metadata["created_on"] = jira_data["created"]
|
|
385
|
-
|
|
380
|
+
|
|
381
|
+
if jira_data.get("description"):
|
|
382
|
+
content_structure["description"] = jira_data.get("description")
|
|
383
|
+
|
|
384
|
+
page_content = json.dumps(content_structure if content_structure.items() else "", indent=2)
|
|
385
|
+
|
|
386
386
|
content_hash = hashlib.sha256(page_content.encode('utf-8')).hexdigest()[:16]
|
|
387
387
|
metadata["updated_on"] = content_hash
|
|
388
388
|
|
|
@@ -407,11 +407,13 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
407
407
|
if "attachments" in step and step["attachments"]:
|
|
408
408
|
for attachment in step["attachments"]:
|
|
409
409
|
if attachment and "id" in attachment and "filename" in attachment:
|
|
410
|
+
attachment['step_id'] = step['id']
|
|
410
411
|
attachments_data.append(attachment)
|
|
411
412
|
if attachments_data:
|
|
412
413
|
metadata["_attachments_data"] = attachments_data
|
|
413
414
|
|
|
414
|
-
|
|
415
|
+
metadata[IndexerKeywords.CONTENT_IN_BYTES.value] = page_content.encode('utf-8')
|
|
416
|
+
yield Document(page_content='', metadata=metadata)
|
|
415
417
|
|
|
416
418
|
except Exception as e:
|
|
417
419
|
logger.error(f"Error processing test data: {e}")
|
|
@@ -430,14 +432,7 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
430
432
|
Generator[Document, None, None]: A generator yielding processed Document objects with metadata.
|
|
431
433
|
"""
|
|
432
434
|
try:
|
|
433
|
-
if not getattr(self, '_include_attachments', False):
|
|
434
|
-
yield document
|
|
435
|
-
return
|
|
436
|
-
|
|
437
435
|
attachments_data = document.metadata.get("_attachments_data", [])
|
|
438
|
-
if not attachments_data:
|
|
439
|
-
yield document
|
|
440
|
-
return
|
|
441
436
|
|
|
442
437
|
issue_id = document.metadata.get("id")
|
|
443
438
|
|
|
@@ -458,44 +453,33 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
458
453
|
).append(attachment_id)
|
|
459
454
|
|
|
460
455
|
try:
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
456
|
+
attachment_metadata = {
|
|
457
|
+
'id': str(attachment_id),
|
|
458
|
+
'issue_key': document.metadata.get('key', ''),
|
|
459
|
+
'issueId': str(issue_id),
|
|
460
|
+
'projectId': document.metadata.get('projectId', ''),
|
|
461
|
+
'source': f"xray_test_{issue_id}",
|
|
462
|
+
'filename': filename,
|
|
463
|
+
'download_link': attachment.get('downloadLink', ''),
|
|
464
|
+
'entity_type': 'test_case_attachment',
|
|
465
|
+
'step_id': attachment.get('step_id', ''),
|
|
466
|
+
'key': document.metadata.get('key', ''),
|
|
467
|
+
IndexerKeywords.PARENT.value: document.metadata.get('id', str(issue_id)),
|
|
468
|
+
'type': 'attachment',
|
|
469
|
+
'doctype': self.doctype,
|
|
470
|
+
}
|
|
471
|
+
yield from self._process_attachment(attachment, attachment_metadata)
|
|
465
472
|
except Exception as e:
|
|
466
473
|
logger.error(f"Failed to process attachment {filename}: {str(e)}")
|
|
467
474
|
continue
|
|
468
|
-
|
|
469
|
-
attachment_metadata = {
|
|
470
|
-
'id': str(attachment_id),
|
|
471
|
-
'issue_key': document.metadata.get('key', ''),
|
|
472
|
-
'issueId': str(issue_id),
|
|
473
|
-
'projectId': document.metadata.get('projectId', ''),
|
|
474
|
-
'source': f"xray_test_{issue_id}",
|
|
475
|
-
'filename': filename,
|
|
476
|
-
'download_link': attachment.get('downloadLink', ''),
|
|
477
|
-
'entity_type': 'test_case_attachment',
|
|
478
|
-
'key': document.metadata.get('key', ''),
|
|
479
|
-
IndexerKeywords.PARENT.value: document.metadata.get('id', str(issue_id)),
|
|
480
|
-
'type': 'attachment',
|
|
481
|
-
'doctype': self.doctype,
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
yield Document(
|
|
485
|
-
page_content=content,
|
|
486
|
-
metadata=attachment_metadata
|
|
487
|
-
)
|
|
488
475
|
|
|
489
476
|
if "_attachments_data" in document.metadata:
|
|
490
477
|
del document.metadata["_attachments_data"]
|
|
491
478
|
|
|
492
|
-
yield document
|
|
493
|
-
|
|
494
479
|
except Exception as e:
|
|
495
480
|
logger.error(f"Error processing document for attachments: {e}")
|
|
496
|
-
yield document
|
|
497
481
|
|
|
498
|
-
def _process_attachment(self, attachment: Dict[str, Any]) ->
|
|
482
|
+
def _process_attachment(self, attachment: Dict[str, Any], attachment_metadata) -> Generator[Document, None, None]:
|
|
499
483
|
"""
|
|
500
484
|
Processes an attachment to extract its content.
|
|
501
485
|
|
|
@@ -508,38 +492,17 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
508
492
|
try:
|
|
509
493
|
download_link = attachment.get('downloadLink')
|
|
510
494
|
filename = attachment.get('filename', '')
|
|
511
|
-
|
|
512
|
-
if not download_link:
|
|
513
|
-
return f"Attachment: {filename} (no download link available)"
|
|
514
495
|
|
|
515
496
|
try:
|
|
516
497
|
auth_token = self._ensure_auth_token()
|
|
517
498
|
headers = {'Authorization': f'Bearer {auth_token}'}
|
|
518
499
|
response = requests.get(download_link, headers=headers, timeout=30)
|
|
519
500
|
response.raise_for_status()
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
file_content=response.content,
|
|
526
|
-
file_name=filename,
|
|
527
|
-
llm=self.llm,
|
|
528
|
-
is_capture_image=True
|
|
529
|
-
)
|
|
530
|
-
else:
|
|
531
|
-
content = load_content_from_bytes(
|
|
532
|
-
response.content,
|
|
533
|
-
ext,
|
|
534
|
-
llm=self.llm
|
|
535
|
-
)
|
|
536
|
-
|
|
537
|
-
if content:
|
|
538
|
-
return f"filename: {filename}\ncontent: {content}"
|
|
539
|
-
else:
|
|
540
|
-
logger.warning(f"No content extracted from attachment {filename}")
|
|
541
|
-
return f"filename: {filename}\ncontent: [No extractable content]"
|
|
542
|
-
|
|
501
|
+
|
|
502
|
+
yield from self._load_attachment(content=response.content,
|
|
503
|
+
file_name=filename,
|
|
504
|
+
attachment_metadata=attachment_metadata)
|
|
505
|
+
|
|
543
506
|
except requests.RequestException as req_e:
|
|
544
507
|
logger.error(f"Unable to download attachment {filename} with existing token: {req_e}")
|
|
545
508
|
|
|
@@ -560,23 +523,13 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
560
523
|
fresh_headers = {'Authorization': f'Bearer {fresh_token}'}
|
|
561
524
|
response = requests.get(download_link, headers=fresh_headers, timeout=60)
|
|
562
525
|
response.raise_for_status()
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
file_name=filename,
|
|
568
|
-
llm=self.llm,
|
|
569
|
-
is_capture_image=True
|
|
570
|
-
) if ext == '.pdf' else load_content_from_bytes(response.content, ext, llm=self.llm)
|
|
571
|
-
|
|
572
|
-
if content:
|
|
573
|
-
return f"filename: {filename}\ncontent: {content}"
|
|
574
|
-
else:
|
|
575
|
-
return f"filename: {filename}\ncontent: [Content extraction failed after re-auth]"
|
|
526
|
+
|
|
527
|
+
yield from self._load_attachment(content=response.content,
|
|
528
|
+
file_name=filename,
|
|
529
|
+
attachment_metadata=attachment_metadata)
|
|
576
530
|
|
|
577
531
|
except Exception as reauth_e:
|
|
578
532
|
logger.error(f"Re-authentication and retry failed for {filename}: {reauth_e}")
|
|
579
|
-
return f"Attachment: {filename} (download failed: {str(req_e)}, re-auth failed: {str(reauth_e)})"
|
|
580
533
|
else:
|
|
581
534
|
try:
|
|
582
535
|
auth_token = self._ensure_auth_token()
|
|
@@ -587,34 +540,29 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
587
540
|
}
|
|
588
541
|
response = requests.get(download_link, headers=fallback_headers, timeout=60)
|
|
589
542
|
response.raise_for_status()
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
file_name=filename,
|
|
595
|
-
llm=self.llm,
|
|
596
|
-
is_capture_image=True
|
|
597
|
-
) if ext == '.pdf' else load_content_from_bytes(response.content, ext, llm=self.llm)
|
|
598
|
-
|
|
599
|
-
if content:
|
|
600
|
-
return f"filename: {filename}\ncontent: {content}"
|
|
601
|
-
else:
|
|
602
|
-
return f"filename: {filename}\ncontent: [Content extraction failed after fallback]"
|
|
543
|
+
|
|
544
|
+
yield from self._load_attachment(content=response.content,
|
|
545
|
+
file_name=filename,
|
|
546
|
+
attachment_metadata=attachment_metadata)
|
|
603
547
|
|
|
604
548
|
except Exception as fallback_e:
|
|
605
549
|
logger.error(f"Fallback download also failed for {filename}: {fallback_e}")
|
|
606
|
-
return f"Attachment: {filename} (download failed: {str(req_e)}, fallback failed: {str(fallback_e)})"
|
|
607
550
|
|
|
608
551
|
except Exception as parse_e:
|
|
609
552
|
logger.error(f"Unable to parse attachment {filename}: {parse_e}")
|
|
610
|
-
return f"Attachment: {filename} (parsing failed: {str(parse_e)})"
|
|
611
553
|
|
|
612
554
|
except Exception as e:
|
|
613
555
|
logger.error(f"Error processing attachment: {e}")
|
|
614
|
-
|
|
556
|
+
|
|
557
|
+
def _load_attachment(self, content, file_name, attachment_metadata) -> Generator[Document, None, None]:
|
|
558
|
+
attachment_metadata[IndexerKeywords.CONTENT_IN_BYTES.value] = content
|
|
559
|
+
attachment_metadata[IndexerKeywords.CONTENT_FILE_NAME.value] = file_name
|
|
560
|
+
yield Document(page_content='', metadata=attachment_metadata)
|
|
615
561
|
|
|
616
562
|
def _index_tool_params(self, **kwargs) -> dict[str, tuple[type, Field]]:
|
|
617
563
|
return {
|
|
564
|
+
'chunking_tool': (Literal['json', ''],
|
|
565
|
+
Field(description="Name of chunking tool for base document", default='json')),
|
|
618
566
|
'jql': (Optional[str], Field(description="""JQL query for searching test cases in Xray.
|
|
619
567
|
|
|
620
568
|
Standard JQL query syntax for filtering Xray test cases. Examples:
|
|
@@ -684,9 +632,9 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
684
632
|
except Exception as e:
|
|
685
633
|
raise ToolException(f"Unable to execute GraphQL due to error: {str(e)}")
|
|
686
634
|
|
|
687
|
-
@
|
|
635
|
+
@extend_with_parent_available_tools
|
|
688
636
|
def get_available_tools(self):
|
|
689
|
-
|
|
637
|
+
return [
|
|
690
638
|
{
|
|
691
639
|
"name": "get_tests",
|
|
692
640
|
"description": self.get_tests.__doc__,
|
|
@@ -711,7 +659,4 @@ class XrayApiWrapper(BaseVectorStoreToolApiWrapper):
|
|
|
711
659
|
"args_schema": XrayGrapql,
|
|
712
660
|
"ref": self.execute_graphql,
|
|
713
661
|
}
|
|
714
|
-
]
|
|
715
|
-
|
|
716
|
-
tools.extend(self._get_vector_search_tools())
|
|
717
|
-
return tools
|
|
662
|
+
]
|