alita-sdk 0.3.257__py3-none-any.whl → 0.3.562__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 +1073 -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 +72 -12
- alita_sdk/community/inventory/__init__.py +236 -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/toolkit_utils.py +176 -0
- alita_sdk/community/inventory/visualize.py +1370 -0
- alita_sdk/configurations/__init__.py +11 -0
- alita_sdk/configurations/ado.py +148 -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 +130 -1
- alita_sdk/configurations/delta_lake.py +1 -1
- alita_sdk/configurations/figma.py +76 -5
- alita_sdk/configurations/github.py +65 -1
- alita_sdk/configurations/gitlab.py +81 -0
- alita_sdk/configurations/google_places.py +17 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/openapi.py +111 -0
- alita_sdk/configurations/postman.py +1 -1
- alita_sdk/configurations/qtest.py +72 -3
- alita_sdk/configurations/report_portal.py +115 -0
- alita_sdk/configurations/salesforce.py +19 -0
- alita_sdk/configurations/service_now.py +1 -12
- alita_sdk/configurations/sharepoint.py +167 -0
- alita_sdk/configurations/sonar.py +18 -0
- alita_sdk/configurations/sql.py +20 -0
- alita_sdk/configurations/testio.py +101 -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 +21 -4
- alita_sdk/runtime/clients/client.py +458 -67
- 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 +352 -0
- alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
- alita_sdk/runtime/langchain/assistant.py +183 -43
- alita_sdk/runtime/langchain/constants.py +647 -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/AlitaJSONLinesLoader.py +77 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
- 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 +189 -41
- alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
- alita_sdk/runtime/langchain/langraph_agent.py +407 -92
- alita_sdk/runtime/langchain/utils.py +102 -8
- alita_sdk/runtime/llms/preloaded.py +2 -6
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/skills/__init__.py +91 -0
- alita_sdk/runtime/skills/callbacks.py +498 -0
- alita_sdk/runtime/skills/discovery.py +540 -0
- alita_sdk/runtime/skills/executor.py +610 -0
- alita_sdk/runtime/skills/input_builder.py +371 -0
- alita_sdk/runtime/skills/models.py +330 -0
- alita_sdk/runtime/skills/registry.py +355 -0
- alita_sdk/runtime/skills/skill_runner.py +330 -0
- alita_sdk/runtime/toolkits/__init__.py +28 -0
- alita_sdk/runtime/toolkits/application.py +14 -4
- alita_sdk/runtime/toolkits/artifact.py +24 -9
- 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/skill_router.py +238 -0
- alita_sdk/runtime/toolkits/subgraph.py +11 -6
- alita_sdk/runtime/toolkits/tools.py +314 -70
- alita_sdk/runtime/toolkits/vectorstore.py +11 -5
- alita_sdk/runtime/tools/__init__.py +24 -0
- alita_sdk/runtime/tools/application.py +16 -4
- alita_sdk/runtime/tools/artifact.py +367 -33
- alita_sdk/runtime/tools/data_analysis.py +183 -0
- alita_sdk/runtime/tools/function.py +100 -4
- alita_sdk/runtime/tools/graph.py +81 -0
- alita_sdk/runtime/tools/image_generation.py +218 -0
- alita_sdk/runtime/tools/llm.py +1013 -177
- alita_sdk/runtime/tools/loop.py +3 -1
- alita_sdk/runtime/tools/loop_output.py +3 -1
- 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/skill_router.py +776 -0
- alita_sdk/runtime/tools/tool.py +3 -1
- alita_sdk/runtime/tools/vectorstore.py +69 -65
- alita_sdk/runtime/tools/vectorstore_base.py +163 -90
- alita_sdk/runtime/utils/AlitaCallback.py +137 -21
- alita_sdk/runtime/utils/mcp_client.py +492 -0
- alita_sdk/runtime/utils/mcp_oauth.py +361 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +434 -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 +48 -0
- alita_sdk/tools/__init__.py +135 -37
- alita_sdk/tools/ado/__init__.py +2 -2
- alita_sdk/tools/ado/repos/__init__.py +15 -19
- alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
- alita_sdk/tools/ado/test_plan/__init__.py +26 -8
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
- alita_sdk/tools/ado/wiki/__init__.py +27 -12
- alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
- alita_sdk/tools/ado/work_item/__init__.py +27 -12
- alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
- alita_sdk/tools/advanced_jira_mining/__init__.py +12 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +14 -11
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +13 -8
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +454 -110
- alita_sdk/tools/bitbucket/__init__.py +27 -19
- alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
- alita_sdk/tools/browser/__init__.py +41 -16
- alita_sdk/tools/browser/crawler.py +3 -1
- alita_sdk/tools/browser/utils.py +15 -6
- alita_sdk/tools/carrier/__init__.py +18 -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 +2 -1
- 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 +11 -7
- alita_sdk/tools/cloud/azure/__init__.py +11 -7
- alita_sdk/tools/cloud/gcp/__init__.py +11 -7
- alita_sdk/tools/cloud/k8s/__init__.py +11 -7
- alita_sdk/tools/code/linter/__init__.py +9 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +20 -13
- alita_sdk/tools/code_indexer_toolkit.py +199 -0
- alita_sdk/tools/confluence/__init__.py +21 -14
- alita_sdk/tools/confluence/api_wrapper.py +197 -58
- alita_sdk/tools/confluence/loader.py +14 -2
- alita_sdk/tools/custom_open_api/__init__.py +11 -5
- alita_sdk/tools/elastic/__init__.py +10 -8
- alita_sdk/tools/elitea_base.py +546 -64
- alita_sdk/tools/figma/__init__.py +11 -8
- alita_sdk/tools/figma/api_wrapper.py +352 -153
- alita_sdk/tools/github/__init__.py +17 -17
- alita_sdk/tools/github/api_wrapper.py +9 -26
- alita_sdk/tools/github/github_client.py +81 -12
- alita_sdk/tools/github/schemas.py +2 -1
- alita_sdk/tools/github/tool.py +5 -1
- alita_sdk/tools/gitlab/__init__.py +18 -13
- alita_sdk/tools/gitlab/api_wrapper.py +224 -80
- alita_sdk/tools/gitlab_org/__init__.py +13 -10
- alita_sdk/tools/google/bigquery/__init__.py +13 -13
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +20 -11
- alita_sdk/tools/jira/__init__.py +21 -11
- alita_sdk/tools/jira/api_wrapper.py +315 -168
- alita_sdk/tools/keycloak/__init__.py +10 -8
- alita_sdk/tools/localgit/__init__.py +8 -3
- alita_sdk/tools/localgit/local_git.py +62 -54
- alita_sdk/tools/localgit/tool.py +5 -1
- alita_sdk/tools/memory/__init__.py +38 -14
- alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
- alita_sdk/tools/ocr/__init__.py +10 -8
- alita_sdk/tools/openapi/__init__.py +281 -108
- alita_sdk/tools/openapi/api_wrapper.py +883 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +18 -11
- alita_sdk/tools/pandas/api_wrapper.py +40 -45
- alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
- alita_sdk/tools/postman/__init__.py +10 -11
- alita_sdk/tools/postman/api_wrapper.py +19 -8
- alita_sdk/tools/postman/postman_analysis.py +8 -1
- alita_sdk/tools/pptx/__init__.py +10 -10
- alita_sdk/tools/qtest/__init__.py +21 -14
- alita_sdk/tools/qtest/api_wrapper.py +1784 -88
- alita_sdk/tools/rally/__init__.py +12 -10
- alita_sdk/tools/report_portal/__init__.py +22 -16
- alita_sdk/tools/salesforce/__init__.py +21 -16
- alita_sdk/tools/servicenow/__init__.py +20 -16
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +16 -14
- 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 +11 -7
- alita_sdk/tools/sql/__init__.py +21 -19
- alita_sdk/tools/sql/api_wrapper.py +71 -23
- alita_sdk/tools/testio/__init__.py +20 -13
- alita_sdk/tools/testrail/__init__.py +12 -11
- alita_sdk/tools/testrail/api_wrapper.py +214 -46
- alita_sdk/tools/utils/__init__.py +28 -4
- alita_sdk/tools/utils/content_parser.py +182 -62
- alita_sdk/tools/utils/text_operations.py +254 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
- alita_sdk/tools/xray/__init__.py +17 -14
- alita_sdk/tools/xray/api_wrapper.py +58 -113
- alita_sdk/tools/yagmail/__init__.py +8 -3
- alita_sdk/tools/zephyr/__init__.py +11 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +15 -9
- alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
- alita_sdk/tools/zephyr_essential/__init__.py +15 -10
- alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
- alita_sdk/tools/zephyr_essential/client.py +6 -4
- alita_sdk/tools/zephyr_scale/__init__.py +12 -8
- alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
- alita_sdk/tools/zephyr_squad/__init__.py +11 -7
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/METADATA +184 -37
- alita_sdk-0.3.562.dist-info/RECORD +450 -0
- alita_sdk-0.3.562.dist-info/entry_points.txt +2 -0
- alita_sdk/tools/bitbucket/tools.py +0 -304
- alita_sdk-0.3.257.dist-info/RECORD +0 -343
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.562.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
# api_wrapper.py
|
|
2
|
-
from typing import Any, Dict, List, Optional
|
|
3
2
|
import fnmatch
|
|
4
|
-
from
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
|
+
|
|
5
|
+
from gitlab import GitlabGetError
|
|
6
|
+
from langchain_core.tools import ToolException
|
|
5
7
|
from pydantic import create_model, Field, model_validator, SecretStr, PrivateAttr
|
|
6
8
|
|
|
9
|
+
from ..code_indexer_toolkit import CodeIndexerToolkit
|
|
10
|
+
from ..utils.available_tools_decorator import extend_with_parent_available_tools
|
|
11
|
+
from ..elitea_base import extend_with_file_operations, BaseCodeToolApiWrapper
|
|
12
|
+
from ..utils.content_parser import parse_file_content
|
|
13
|
+
from .utils import get_position
|
|
14
|
+
|
|
7
15
|
AppendFileModel = create_model(
|
|
8
16
|
"AppendFileModel",
|
|
9
17
|
file_path=(str, Field(description="The path of the file")),
|
|
@@ -29,7 +37,7 @@ ReadFileModel = create_model(
|
|
|
29
37
|
)
|
|
30
38
|
UpdateFileModel = create_model(
|
|
31
39
|
"UpdateFileModel",
|
|
32
|
-
file_query=(str, Field(description="The file query string")),
|
|
40
|
+
file_query=(str, Field(description="The file query string containing file path on first line, followed by OLD/NEW markers. Format: file_path\\nOLD <<<< old content >>>> OLD\\nNEW <<<< new content >>>> NEW")),
|
|
33
41
|
branch=(str, Field(description="The branch to update the file in")),
|
|
34
42
|
)
|
|
35
43
|
CommentOnIssueModel = create_model(
|
|
@@ -84,9 +92,9 @@ GetPRChangesModel = create_model(
|
|
|
84
92
|
CreatePRChangeCommentModel = create_model(
|
|
85
93
|
"CreatePRChangeCommentModel",
|
|
86
94
|
pr_number=(int, Field(description="GitLab Merge Request (Pull Request) number")),
|
|
87
|
-
file_path=(str, Field(description="File path of the changed file")),
|
|
88
|
-
line_number=(int, Field(description="Line
|
|
89
|
-
comment=(str, Field(description="Comment content")),
|
|
95
|
+
file_path=(str, Field(description="File path of the changed file as shown in the diff")),
|
|
96
|
+
line_number=(int, Field(description="Line index (0-based) from the diff output. Use get_pr_changes first to see the diff and identify the correct line index to comment on.")),
|
|
97
|
+
comment=(str, Field(description="Comment content to add to the specific line")),
|
|
90
98
|
)
|
|
91
99
|
GetCommitsModel = create_model(
|
|
92
100
|
"GetCommitsModel",
|
|
@@ -97,53 +105,67 @@ GetCommitsModel = create_model(
|
|
|
97
105
|
author=(Optional[str], Field(description="Author name", default=None)),
|
|
98
106
|
)
|
|
99
107
|
|
|
100
|
-
class GitLabAPIWrapper(
|
|
108
|
+
class GitLabAPIWrapper(CodeIndexerToolkit):
|
|
101
109
|
url: str
|
|
102
110
|
repository: str
|
|
103
111
|
private_token: SecretStr
|
|
104
112
|
branch: Optional[str] = 'main'
|
|
105
113
|
_git: Any = PrivateAttr()
|
|
106
|
-
_repo_instance: Any = PrivateAttr()
|
|
107
114
|
_active_branch: Any = PrivateAttr()
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
vectorstore_type: Optional[str] = "PGVector"
|
|
115
|
+
|
|
116
|
+
# Import file operation methods from BaseCodeToolApiWrapper
|
|
117
|
+
read_file_chunk = BaseCodeToolApiWrapper.read_file_chunk
|
|
118
|
+
read_multiple_files = BaseCodeToolApiWrapper.read_multiple_files
|
|
119
|
+
search_file = BaseCodeToolApiWrapper.search_file
|
|
120
|
+
edit_file = BaseCodeToolApiWrapper.edit_file
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _sanitize_url(url: str) -> str:
|
|
124
|
+
"""Remove trailing slash from URL if present."""
|
|
125
|
+
return url.rstrip('/') if url else url
|
|
120
126
|
|
|
121
127
|
@model_validator(mode='before')
|
|
122
128
|
@classmethod
|
|
123
|
-
def
|
|
129
|
+
def validate_toolkit_before(cls, values: Dict) -> Dict:
|
|
130
|
+
return super().validate_toolkit(values)
|
|
131
|
+
|
|
132
|
+
@model_validator(mode='after')
|
|
133
|
+
def validate_toolkit(self):
|
|
124
134
|
try:
|
|
125
|
-
|
|
135
|
+
import gitlab
|
|
126
136
|
except ImportError:
|
|
127
137
|
raise ImportError(
|
|
128
138
|
"python-gitlab is not installed. "
|
|
129
139
|
"Please install it with `pip install python-gitlab`"
|
|
130
140
|
)
|
|
131
|
-
|
|
141
|
+
self.repository = self._sanitize_url(self.repository)
|
|
132
142
|
g = gitlab.Gitlab(
|
|
133
|
-
url=
|
|
134
|
-
private_token=
|
|
143
|
+
url=self._sanitize_url(self.url),
|
|
144
|
+
private_token=self.private_token.get_secret_value(),
|
|
135
145
|
keep_base_url=True,
|
|
136
146
|
)
|
|
137
147
|
|
|
138
148
|
g.auth()
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
149
|
+
self._git = g
|
|
150
|
+
self._active_branch = self.branch
|
|
151
|
+
return self
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def repo_instance(self):
|
|
155
|
+
if not hasattr(self, "_repo_instance") or self._repo_instance is None:
|
|
156
|
+
try:
|
|
157
|
+
if self._git and self.repository:
|
|
158
|
+
self._repo_instance = self._git.projects.get(self.repository)
|
|
159
|
+
else:
|
|
160
|
+
self._repo_instance = None
|
|
161
|
+
except Exception as e:
|
|
162
|
+
# Only raise when accessed, not during initialization
|
|
163
|
+
raise ToolException(e)
|
|
164
|
+
return self._repo_instance
|
|
143
165
|
|
|
144
166
|
def set_active_branch(self, branch_name: str) -> str:
|
|
145
167
|
self._active_branch = branch_name
|
|
146
|
-
self.
|
|
168
|
+
self.repo_instance.default_branch = branch_name
|
|
147
169
|
return f"Active branch set to {branch_name}"
|
|
148
170
|
|
|
149
171
|
def list_branches_in_repo(self, limit: Optional[int] = 20, branch_wildcard: Optional[str] = None) -> List[str]:
|
|
@@ -158,7 +180,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
158
180
|
List[str]: List containing names of branches
|
|
159
181
|
"""
|
|
160
182
|
try:
|
|
161
|
-
branches = self.
|
|
183
|
+
branches = self.repo_instance.branches.list(get_all=True)
|
|
162
184
|
|
|
163
185
|
if branch_wildcard:
|
|
164
186
|
branches = [branch for branch in branches if fnmatch.fnmatch(branch.name, branch_wildcard)]
|
|
@@ -185,7 +207,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
185
207
|
|
|
186
208
|
def _get_all_files(self, path: str = None, recursive: bool = True, branch: str = None):
|
|
187
209
|
branch = branch if branch else self._active_branch
|
|
188
|
-
return self.
|
|
210
|
+
return self.repo_instance.repository_tree(path=path, ref=branch, recursive=recursive, all=True)
|
|
189
211
|
|
|
190
212
|
# overrided for indexer
|
|
191
213
|
def _get_files(self, path: str = None, recursive: bool = True, branch: str = None):
|
|
@@ -197,17 +219,31 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
197
219
|
Get the commit hash of a file in a specific branch.
|
|
198
220
|
"""
|
|
199
221
|
try:
|
|
200
|
-
file = self.
|
|
222
|
+
file = self.repo_instance.files.get(file_path, branch)
|
|
201
223
|
return file.commit_id
|
|
202
224
|
except Exception as e:
|
|
203
225
|
return f"Unable to get commit hash for {file_path} due to error:\n{e}"
|
|
204
226
|
|
|
205
|
-
def _read_file(self, file_path: str, branch: str):
|
|
227
|
+
def _read_file(self, file_path: str, branch: str, **kwargs):
|
|
228
|
+
"""
|
|
229
|
+
Read a file from specified branch with optional partial read support.
|
|
230
|
+
|
|
231
|
+
Parameters:
|
|
232
|
+
file_path: the file path
|
|
233
|
+
branch: the branch to read the file from
|
|
234
|
+
**kwargs: Additional parameters (offset, limit, head, tail) - currently ignored,
|
|
235
|
+
partial read handled client-side by base class methods
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
File content as string
|
|
239
|
+
"""
|
|
240
|
+
# Default to active branch if branch is None, consistent with other methods
|
|
241
|
+
branch = branch if branch else self._active_branch
|
|
206
242
|
return self.read_file(file_path, branch)
|
|
207
243
|
|
|
208
244
|
def create_branch(self, branch_name: str) -> str:
|
|
209
245
|
try:
|
|
210
|
-
self.
|
|
246
|
+
self.repo_instance.branches.create(
|
|
211
247
|
{
|
|
212
248
|
'branch': branch_name,
|
|
213
249
|
'ref': self._active_branch,
|
|
@@ -230,7 +266,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
230
266
|
return parsed
|
|
231
267
|
|
|
232
268
|
def get_issues(self) -> str:
|
|
233
|
-
issues = self.
|
|
269
|
+
issues = self.repo_instance.issues.list(state="opened")
|
|
234
270
|
if len(issues) > 0:
|
|
235
271
|
parsed_issues = self.parse_issues(issues)
|
|
236
272
|
parsed_issues_str = (
|
|
@@ -241,7 +277,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
241
277
|
return "No open issues available"
|
|
242
278
|
|
|
243
279
|
def get_issue(self, issue_number: int) -> Dict[str, Any]:
|
|
244
|
-
issue = self.
|
|
280
|
+
issue = self.repo_instance.issues.get(issue_number)
|
|
245
281
|
page = 0
|
|
246
282
|
comments: List[dict] = []
|
|
247
283
|
while len(comments) <= 10:
|
|
@@ -267,7 +303,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
267
303
|
commits are already in the {self.branch} branch"""
|
|
268
304
|
else:
|
|
269
305
|
try:
|
|
270
|
-
pr = self.
|
|
306
|
+
pr = self.repo_instance.mergerequests.create(
|
|
271
307
|
{
|
|
272
308
|
"source_branch": branch,
|
|
273
309
|
"target_branch": self.branch,
|
|
@@ -284,16 +320,18 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
284
320
|
issue_number = int(comment_query.split("\n\n")[0])
|
|
285
321
|
comment = comment_query[len(str(issue_number)) + 2 :]
|
|
286
322
|
try:
|
|
287
|
-
issue = self.
|
|
323
|
+
issue = self.repo_instance.issues.get(issue_number)
|
|
288
324
|
issue.notes.create({"body": comment})
|
|
289
325
|
return "Commented on issue " + str(issue_number)
|
|
290
326
|
except Exception as e:
|
|
291
327
|
return "Unable to make comment due to error:\n" + str(e)
|
|
292
328
|
|
|
293
329
|
def create_file(self, file_path: str, file_contents: str, branch: str) -> str:
|
|
330
|
+
# Default to active branch if branch is None
|
|
331
|
+
branch = branch if branch else self._active_branch
|
|
294
332
|
try:
|
|
295
333
|
self.set_active_branch(branch)
|
|
296
|
-
self.
|
|
334
|
+
self.repo_instance.files.get(file_path, branch)
|
|
297
335
|
return f"File already exists at {file_path}. Use update_file instead"
|
|
298
336
|
except Exception:
|
|
299
337
|
data = {
|
|
@@ -302,53 +340,126 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
302
340
|
"file_path": file_path,
|
|
303
341
|
"content": file_contents,
|
|
304
342
|
}
|
|
305
|
-
self.
|
|
343
|
+
self.repo_instance.files.create(data)
|
|
306
344
|
|
|
307
345
|
return "Created file " + file_path
|
|
308
346
|
|
|
309
347
|
def read_file(self, file_path: str, branch: str) -> str:
|
|
348
|
+
# Default to active branch if branch is None
|
|
349
|
+
branch = branch if branch else self._active_branch
|
|
310
350
|
self.set_active_branch(branch)
|
|
311
|
-
file = self.
|
|
312
|
-
return
|
|
351
|
+
file = self.repo_instance.files.get(file_path, branch)
|
|
352
|
+
return parse_file_content(file_name=file_path,
|
|
353
|
+
file_content=file.decode(),
|
|
354
|
+
llm=self.llm)
|
|
355
|
+
|
|
356
|
+
def _write_file(
|
|
357
|
+
self,
|
|
358
|
+
file_path: str,
|
|
359
|
+
content: str,
|
|
360
|
+
branch: str = None,
|
|
361
|
+
commit_message: str = None
|
|
362
|
+
) -> str:
|
|
363
|
+
"""
|
|
364
|
+
Write content to a file (create or update).
|
|
365
|
+
|
|
366
|
+
Parameters:
|
|
367
|
+
file_path: Path to the file
|
|
368
|
+
content: New file content
|
|
369
|
+
branch: Branch name (uses active branch if None)
|
|
370
|
+
commit_message: Commit message
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
Success message
|
|
374
|
+
"""
|
|
375
|
+
try:
|
|
376
|
+
branch = branch or self._active_branch
|
|
377
|
+
|
|
378
|
+
if branch == self.branch:
|
|
379
|
+
raise ToolException(
|
|
380
|
+
f"Cannot commit directly to the {self.branch} branch. "
|
|
381
|
+
"Please create a new branch and try again."
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
self.set_active_branch(branch)
|
|
385
|
+
|
|
386
|
+
# Check if file exists
|
|
387
|
+
try:
|
|
388
|
+
self.repo_instance.files.get(file_path, branch)
|
|
389
|
+
# File exists, update it
|
|
390
|
+
commit = {
|
|
391
|
+
"branch": branch,
|
|
392
|
+
"commit_message": commit_message or f"Update {file_path}",
|
|
393
|
+
"actions": [
|
|
394
|
+
{
|
|
395
|
+
"action": "update",
|
|
396
|
+
"file_path": file_path,
|
|
397
|
+
"content": content,
|
|
398
|
+
}
|
|
399
|
+
],
|
|
400
|
+
}
|
|
401
|
+
self.repo_instance.commits.create(commit)
|
|
402
|
+
return f"Updated file {file_path}"
|
|
403
|
+
except:
|
|
404
|
+
# File doesn't exist, create it
|
|
405
|
+
data = {
|
|
406
|
+
"branch": branch,
|
|
407
|
+
"commit_message": commit_message or f"Create {file_path}",
|
|
408
|
+
"file_path": file_path,
|
|
409
|
+
"content": content,
|
|
410
|
+
}
|
|
411
|
+
self.repo_instance.files.create(data)
|
|
412
|
+
return f"Created file {file_path}"
|
|
413
|
+
except Exception as e:
|
|
414
|
+
raise ToolException(f"Unable to write file {file_path}: {str(e)}")
|
|
313
415
|
|
|
314
416
|
def update_file(self, file_query: str, branch: str) -> str:
|
|
417
|
+
"""
|
|
418
|
+
Update file using edit_file functionality.
|
|
419
|
+
|
|
420
|
+
This method now delegates to edit_file which uses OLD/NEW markers.
|
|
421
|
+
For backwards compatibility, it extracts the file_path from the query.
|
|
422
|
+
|
|
423
|
+
Expected format:
|
|
424
|
+
file_path
|
|
425
|
+
OLD <<<<
|
|
426
|
+
old content
|
|
427
|
+
>>>> OLD
|
|
428
|
+
NEW <<<<
|
|
429
|
+
new content
|
|
430
|
+
>>>> NEW
|
|
431
|
+
|
|
432
|
+
Args:
|
|
433
|
+
file_query: File path on first line, followed by OLD/NEW markers
|
|
434
|
+
branch: Branch to update the file in
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
Success or error message
|
|
438
|
+
"""
|
|
315
439
|
if branch == self.branch:
|
|
316
440
|
return (
|
|
317
|
-
"You're attempting to commit
|
|
441
|
+
"You're attempting to commit directly "
|
|
318
442
|
f"to the {self.branch} branch, which is protected. "
|
|
319
443
|
"Please create a new branch and try again."
|
|
320
444
|
)
|
|
321
445
|
try:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
updated_file_content = file_content
|
|
326
|
-
for old, new in self.extract_old_new_pairs(file_query):
|
|
327
|
-
if not old.strip():
|
|
328
|
-
continue
|
|
329
|
-
updated_file_content = updated_file_content.replace(old, new)
|
|
330
|
-
|
|
331
|
-
if file_content == updated_file_content:
|
|
446
|
+
# Extract file path from first line
|
|
447
|
+
lines = file_query.split("\n", 1)
|
|
448
|
+
if len(lines) < 2:
|
|
332
449
|
return (
|
|
333
|
-
"
|
|
334
|
-
"
|
|
335
|
-
"
|
|
450
|
+
"Invalid file_query format. Expected:\n"
|
|
451
|
+
"file_path\n"
|
|
452
|
+
"OLD <<<< old content >>>> OLD\n"
|
|
453
|
+
"NEW <<<< new content >>>> NEW"
|
|
336
454
|
)
|
|
337
455
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
"file_path": file_path,
|
|
345
|
-
"content": updated_file_content,
|
|
346
|
-
}
|
|
347
|
-
],
|
|
348
|
-
}
|
|
456
|
+
file_path = lines[0].strip()
|
|
457
|
+
edit_content = lines[1] if len(lines) > 1 else ""
|
|
458
|
+
|
|
459
|
+
# Delegate to edit_file method with appropriate commit message
|
|
460
|
+
commit_message = f"Update {file_path}"
|
|
461
|
+
return self.edit_file(file_path, edit_content, branch, commit_message)
|
|
349
462
|
|
|
350
|
-
self._repo_instance.commits.create(commit)
|
|
351
|
-
return "Updated file " + file_path
|
|
352
463
|
except Exception as e:
|
|
353
464
|
return "Unable to update file due to error:\n" + str(e)
|
|
354
465
|
|
|
@@ -377,7 +488,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
377
488
|
],
|
|
378
489
|
}
|
|
379
490
|
|
|
380
|
-
self.
|
|
491
|
+
self.repo_instance.commits.create(commit)
|
|
381
492
|
return "Updated file " + file_path
|
|
382
493
|
except Exception as e:
|
|
383
494
|
return "Unable to update file due to error:\n" + str(e)
|
|
@@ -387,23 +498,54 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
387
498
|
self.set_active_branch(branch)
|
|
388
499
|
if not commit_message:
|
|
389
500
|
commit_message = f"Delete {file_path}"
|
|
390
|
-
self.
|
|
501
|
+
self.repo_instance.files.delete(file_path, branch, commit_message)
|
|
391
502
|
return f"Deleted file {file_path}"
|
|
392
503
|
except Exception as e:
|
|
393
504
|
return f"Unable to delete file due to error:\n{e}"
|
|
394
505
|
|
|
395
506
|
def get_pr_changes(self, pr_number: int) -> str:
|
|
396
|
-
mr = self.
|
|
507
|
+
mr = self.repo_instance.mergerequests.get(pr_number)
|
|
397
508
|
res = f"title: {mr.title}\ndescription: {mr.description}\n\n"
|
|
398
509
|
for change in mr.changes()["changes"]:
|
|
399
510
|
res += f"diff --git a/{change['old_path']} b/{change['new_path']}\n{change['diff']}\n"
|
|
400
511
|
return res
|
|
401
512
|
|
|
402
513
|
def create_pr_change_comment(self, pr_number: int, file_path: str, line_number: int, comment: str) -> str:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
514
|
+
"""
|
|
515
|
+
Create a comment on a specific line in a pull request (merge request) change in GitLab.
|
|
516
|
+
|
|
517
|
+
This method adds an inline comment to a specific line in the diff of a merge request.
|
|
518
|
+
The line_number parameter refers to the line index in the diff output (0-based),
|
|
519
|
+
not the line number in the original file.
|
|
520
|
+
|
|
521
|
+
**Important**: Use get_pr_changes first to see the diff and identify the correct
|
|
522
|
+
line index for commenting.
|
|
523
|
+
|
|
524
|
+
Parameters:
|
|
525
|
+
pr_number: GitLab Merge Request number
|
|
526
|
+
file_path: Path to the file being commented on (as shown in the diff)
|
|
527
|
+
line_number: Line index from the diff (0-based index)
|
|
528
|
+
comment: Comment text to add
|
|
529
|
+
|
|
530
|
+
Returns:
|
|
531
|
+
Success message or error description
|
|
532
|
+
"""
|
|
533
|
+
try:
|
|
534
|
+
mr = self.repo_instance.mergerequests.get(pr_number)
|
|
535
|
+
except GitlabGetError as e:
|
|
536
|
+
if e.response_code == 404:
|
|
537
|
+
raise ToolException(f"Merge request number {pr_number} wasn't found: {e}")
|
|
538
|
+
raise ToolException(f"Error retrieving merge request {pr_number}: {e}")
|
|
539
|
+
|
|
540
|
+
try:
|
|
541
|
+
# Calculate proper position with SHA references and line mappings
|
|
542
|
+
position = get_position(file_path=file_path, line_number=line_number, mr=mr)
|
|
543
|
+
|
|
544
|
+
# Create discussion with the comment
|
|
545
|
+
mr.discussions.create({"body": comment, "position": position})
|
|
546
|
+
return f"Comment added successfully to line {line_number} in {file_path} on MR #{pr_number}"
|
|
547
|
+
except Exception as e:
|
|
548
|
+
raise ToolException(f"Failed to create comment on MR #{pr_number}: {e}")
|
|
407
549
|
|
|
408
550
|
def get_commits(self, sha: Optional[str] = None, path: Optional[str] = None, since: Optional[str] = None, until: Optional[str] = None, author: Optional[str] = None):
|
|
409
551
|
params = {}
|
|
@@ -417,7 +559,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
417
559
|
params["until"] = until
|
|
418
560
|
if author:
|
|
419
561
|
params["author"] = author
|
|
420
|
-
commits = self.
|
|
562
|
+
commits = self.repo_instance.commits.list(**params)
|
|
421
563
|
return [
|
|
422
564
|
{
|
|
423
565
|
"sha": commit.id,
|
|
@@ -429,6 +571,8 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
429
571
|
for commit in commits
|
|
430
572
|
]
|
|
431
573
|
|
|
574
|
+
@extend_with_parent_available_tools
|
|
575
|
+
@extend_with_file_operations
|
|
432
576
|
def get_available_tools(self):
|
|
433
577
|
return [
|
|
434
578
|
{
|
|
@@ -524,7 +668,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
524
668
|
{
|
|
525
669
|
"name": "create_pr_change_comment",
|
|
526
670
|
"ref": self.create_pr_change_comment,
|
|
527
|
-
"description": "Create
|
|
671
|
+
"description": self.create_pr_change_comment.__doc__ or "Create an inline comment on a specific line in a pull request change. Use get_pr_changes first to see the diff and identify the line index for commenting. The line_number is a 0-based index from the diff output, not the file line number.",
|
|
528
672
|
"args_schema": CreatePRChangeCommentModel,
|
|
529
673
|
},
|
|
530
674
|
{
|
|
@@ -533,4 +677,4 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
533
677
|
"description": "Retrieve a list of commits from the repository.",
|
|
534
678
|
"args_schema": GetCommitsModel,
|
|
535
679
|
}
|
|
536
|
-
]
|
|
680
|
+
]
|
|
@@ -4,7 +4,9 @@ from langchain_core.tools import BaseToolkit
|
|
|
4
4
|
from langchain_core.tools import BaseTool
|
|
5
5
|
from ..base.tool import BaseAction
|
|
6
6
|
from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
from ..elitea_base import filter_missconfigured_index_tools
|
|
9
|
+
from ..utils import clean_string, get_max_toolkit_length
|
|
8
10
|
from ...configurations.gitlab import GitlabConfiguration
|
|
9
11
|
|
|
10
12
|
name = "gitlab_org"
|
|
@@ -20,17 +22,13 @@ def get_tools(tool):
|
|
|
20
22
|
|
|
21
23
|
class AlitaGitlabSpaceToolkit(BaseToolkit):
|
|
22
24
|
tools: List[BaseTool] = []
|
|
23
|
-
toolkit_max_length: int = 0
|
|
24
25
|
|
|
25
26
|
@staticmethod
|
|
26
27
|
def toolkit_config_schema() -> BaseModel:
|
|
27
28
|
selected_tools = {x['name']: x['args_schema'].schema() for x in GitLabWorkspaceAPIWrapper.model_construct().get_available_tools()}
|
|
28
|
-
AlitaGitlabSpaceToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
|
|
29
29
|
return create_model(
|
|
30
30
|
name,
|
|
31
|
-
|
|
32
|
-
'max_toolkit_length': AlitaGitlabSpaceToolkit.toolkit_max_length})),
|
|
33
|
-
gitlab_configuration=(Optional[GitlabConfiguration], Field(description="GitLab configuration",
|
|
31
|
+
gitlab_configuration=(GitlabConfiguration, Field(description="GitLab configuration",
|
|
34
32
|
json_schema_extra={
|
|
35
33
|
'configuration_types': ['gitlab']})),
|
|
36
34
|
repositories=(str, Field(
|
|
@@ -51,6 +49,7 @@ class AlitaGitlabSpaceToolkit(BaseToolkit):
|
|
|
51
49
|
)
|
|
52
50
|
|
|
53
51
|
@classmethod
|
|
52
|
+
@filter_missconfigured_index_tools
|
|
54
53
|
def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
|
|
55
54
|
if selected_tools is None:
|
|
56
55
|
selected_tools = []
|
|
@@ -60,18 +59,22 @@ class AlitaGitlabSpaceToolkit(BaseToolkit):
|
|
|
60
59
|
**kwargs['gitlab_configuration'],
|
|
61
60
|
}
|
|
62
61
|
gitlab_wrapper = GitLabWorkspaceAPIWrapper(**wrapper_payload)
|
|
63
|
-
prefix = clean_string(toolkit_name, AlitaGitlabSpaceToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
|
|
64
62
|
available_tools = gitlab_wrapper.get_available_tools()
|
|
65
63
|
tools = []
|
|
66
64
|
for tool in available_tools:
|
|
67
65
|
if selected_tools:
|
|
68
66
|
if tool["name"] not in selected_tools:
|
|
69
67
|
continue
|
|
68
|
+
description = tool["description"]
|
|
69
|
+
if toolkit_name:
|
|
70
|
+
description = f"Toolkit: {toolkit_name}\n{description}"
|
|
71
|
+
description = description[:1000]
|
|
70
72
|
tools.append(BaseAction(
|
|
71
73
|
api_wrapper=gitlab_wrapper,
|
|
72
|
-
name=
|
|
73
|
-
description=
|
|
74
|
-
args_schema=tool["args_schema"]
|
|
74
|
+
name=tool['name'],
|
|
75
|
+
description=description,
|
|
76
|
+
args_schema=tool["args_schema"],
|
|
77
|
+
metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
|
|
75
78
|
))
|
|
76
79
|
return cls(tools=tools)
|
|
77
80
|
|
|
@@ -5,7 +5,7 @@ from langchain_core.tools import BaseTool, BaseToolkit
|
|
|
5
5
|
from pydantic import BaseModel, Field, computed_field, field_validator
|
|
6
6
|
|
|
7
7
|
from ....configurations.bigquery import BigQueryConfiguration
|
|
8
|
-
from ...utils import
|
|
8
|
+
from ...utils import clean_string, get_max_toolkit_length
|
|
9
9
|
from .api_wrapper import BigQueryApiWrapper
|
|
10
10
|
from .tool import BigQueryAction
|
|
11
11
|
|
|
@@ -22,11 +22,6 @@ def get_available_tools() -> dict[str, dict]:
|
|
|
22
22
|
return available_tools
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
toolkit_max_length = lru_cache(maxsize=1)(
|
|
26
|
-
lambda: get_max_toolkit_length(get_available_tools())
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
|
|
30
25
|
class BigQueryToolkitConfig(BaseModel):
|
|
31
26
|
class Config:
|
|
32
27
|
title = name
|
|
@@ -46,7 +41,7 @@ class BigQueryToolkitConfig(BaseModel):
|
|
|
46
41
|
}
|
|
47
42
|
}
|
|
48
43
|
|
|
49
|
-
bigquery_configuration:
|
|
44
|
+
bigquery_configuration: BigQueryConfiguration = Field(
|
|
50
45
|
description="BigQuery configuration", json_schema_extra={"configuration_types": ["bigquery"]}
|
|
51
46
|
)
|
|
52
47
|
selected_tools: List[str] = Field(
|
|
@@ -86,9 +81,10 @@ class BigQueryToolkit(BaseToolkit):
|
|
|
86
81
|
|
|
87
82
|
@computed_field
|
|
88
83
|
@property
|
|
89
|
-
def
|
|
84
|
+
def toolkit_context(self) -> str:
|
|
85
|
+
"""Returns toolkit context for descriptions (max 1000 chars)."""
|
|
90
86
|
return (
|
|
91
|
-
clean_string(self.toolkit_name,
|
|
87
|
+
f" [Toolkit: {clean_string(self.toolkit_name, 0)}]"
|
|
92
88
|
if self.toolkit_name
|
|
93
89
|
else ""
|
|
94
90
|
)
|
|
@@ -122,14 +118,18 @@ class BigQueryToolkit(BaseToolkit):
|
|
|
122
118
|
selected_tools = set(selected_tools)
|
|
123
119
|
for t in instance.available_tools:
|
|
124
120
|
if t["name"] in selected_tools:
|
|
121
|
+
description = t["description"]
|
|
122
|
+
if toolkit_name:
|
|
123
|
+
description = f"Toolkit: {toolkit_name}\n{description}"
|
|
124
|
+
description = f"Project: {getattr(instance.api_wrapper, 'project', '')}\n{description}"
|
|
125
|
+
description = description[:1000]
|
|
125
126
|
instance.tools.append(
|
|
126
127
|
BigQueryAction(
|
|
127
128
|
api_wrapper=instance.api_wrapper,
|
|
128
|
-
name=
|
|
129
|
-
|
|
130
|
-
description=f"Project: {getattr(instance.api_wrapper, 'project', '')}\n"
|
|
131
|
-
+ t["description"],
|
|
129
|
+
name=t["name"],
|
|
130
|
+
description=description,
|
|
132
131
|
args_schema=t["args_schema"],
|
|
132
|
+
metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
|
|
133
133
|
)
|
|
134
134
|
)
|
|
135
135
|
return instance
|
|
@@ -29,6 +29,10 @@ class BigQueryAction(BaseTool):
|
|
|
29
29
|
) -> str:
|
|
30
30
|
"""Use the GitHub API to run an operation."""
|
|
31
31
|
try:
|
|
32
|
-
|
|
32
|
+
# Strip numeric suffix added for deduplication (_2, _3, etc.)
|
|
33
|
+
# to get the original tool name that exists in the wrapper
|
|
34
|
+
import re
|
|
35
|
+
mode = re.sub(r'_\d+$', '', self.mode) if self.mode else self.mode
|
|
36
|
+
return self.api_wrapper.run(mode, *args, **kwargs)
|
|
33
37
|
except Exception as e:
|
|
34
38
|
return f"Error: {format_exc()}"
|