alita-sdk 0.3.257__py3-none-any.whl → 0.3.584__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/__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 +3794 -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 +323 -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 +493 -105
- alita_sdk/runtime/langchain/utils.py +118 -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 +25 -9
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +782 -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 +1032 -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/constants.py +5 -1
- 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 +16 -19
- alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
- alita_sdk/tools/ado/test_plan/__init__.py +27 -8
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
- alita_sdk/tools/ado/wiki/__init__.py +28 -12
- alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
- alita_sdk/tools/ado/work_item/__init__.py +28 -12
- alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
- alita_sdk/tools/advanced_jira_mining/__init__.py +13 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +15 -11
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +14 -8
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +454 -110
- alita_sdk/tools/bitbucket/__init__.py +28 -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 +12 -7
- alita_sdk/tools/cloud/azure/__init__.py +12 -7
- alita_sdk/tools/cloud/gcp/__init__.py +12 -7
- alita_sdk/tools/cloud/k8s/__init__.py +12 -7
- alita_sdk/tools/code/linter/__init__.py +10 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +21 -13
- alita_sdk/tools/code_indexer_toolkit.py +199 -0
- alita_sdk/tools/confluence/__init__.py +22 -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 +12 -5
- alita_sdk/tools/elastic/__init__.py +11 -8
- alita_sdk/tools/elitea_base.py +546 -64
- alita_sdk/tools/figma/__init__.py +60 -11
- alita_sdk/tools/figma/api_wrapper.py +1400 -167
- alita_sdk/tools/figma/figma_client.py +73 -0
- alita_sdk/tools/figma/toon_tools.py +2748 -0
- alita_sdk/tools/github/__init__.py +18 -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 +19 -13
- alita_sdk/tools/gitlab/api_wrapper.py +256 -80
- alita_sdk/tools/gitlab_org/__init__.py +14 -10
- alita_sdk/tools/google/bigquery/__init__.py +14 -13
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +21 -11
- alita_sdk/tools/jira/__init__.py +22 -11
- alita_sdk/tools/jira/api_wrapper.py +315 -168
- alita_sdk/tools/keycloak/__init__.py +11 -8
- alita_sdk/tools/localgit/__init__.py +9 -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 +11 -8
- alita_sdk/tools/openapi/__init__.py +491 -106
- alita_sdk/tools/openapi/api_wrapper.py +1357 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +20 -12
- 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 +11 -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 +11 -10
- alita_sdk/tools/qtest/__init__.py +22 -14
- alita_sdk/tools/qtest/api_wrapper.py +1784 -88
- alita_sdk/tools/rally/__init__.py +13 -10
- alita_sdk/tools/report_portal/__init__.py +23 -16
- alita_sdk/tools/salesforce/__init__.py +22 -16
- alita_sdk/tools/servicenow/__init__.py +21 -16
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +17 -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 +13 -8
- alita_sdk/tools/sql/__init__.py +22 -19
- alita_sdk/tools/sql/api_wrapper.py +71 -23
- alita_sdk/tools/testio/__init__.py +21 -13
- alita_sdk/tools/testrail/__init__.py +13 -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 +241 -55
- alita_sdk/tools/utils/text_operations.py +254 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
- alita_sdk/tools/xray/__init__.py +18 -14
- alita_sdk/tools/xray/api_wrapper.py +58 -113
- alita_sdk/tools/yagmail/__init__.py +9 -3
- alita_sdk/tools/zephyr/__init__.py +12 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +16 -9
- alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
- alita_sdk/tools/zephyr_essential/__init__.py +16 -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 +13 -8
- alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
- alita_sdk/tools/zephyr_squad/__init__.py +12 -7
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/METADATA +184 -37
- alita_sdk-0.3.584.dist-info/RECORD +452 -0
- alita_sdk-0.3.584.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.584.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.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(
|
|
@@ -46,6 +54,11 @@ CreatePullRequestModel = create_model(
|
|
|
46
54
|
pr_body=(str, Field(description="The body of the pull request")),
|
|
47
55
|
branch=(str, Field(description="The branch to create the pull request from")),
|
|
48
56
|
)
|
|
57
|
+
CommentOnPRModel = create_model(
|
|
58
|
+
"CommentOnPRModel",
|
|
59
|
+
pr_number=(int, Field(description="The number of the pull request/merge request")),
|
|
60
|
+
comment=(str, Field(description="The comment text to add")),
|
|
61
|
+
)
|
|
49
62
|
|
|
50
63
|
CreateBranchModel = create_model(
|
|
51
64
|
"CreateBranchModel",
|
|
@@ -84,9 +97,9 @@ GetPRChangesModel = create_model(
|
|
|
84
97
|
CreatePRChangeCommentModel = create_model(
|
|
85
98
|
"CreatePRChangeCommentModel",
|
|
86
99
|
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")),
|
|
100
|
+
file_path=(str, Field(description="File path of the changed file as shown in the diff")),
|
|
101
|
+
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.")),
|
|
102
|
+
comment=(str, Field(description="Comment content to add to the specific line")),
|
|
90
103
|
)
|
|
91
104
|
GetCommitsModel = create_model(
|
|
92
105
|
"GetCommitsModel",
|
|
@@ -97,53 +110,67 @@ GetCommitsModel = create_model(
|
|
|
97
110
|
author=(Optional[str], Field(description="Author name", default=None)),
|
|
98
111
|
)
|
|
99
112
|
|
|
100
|
-
class GitLabAPIWrapper(
|
|
113
|
+
class GitLabAPIWrapper(CodeIndexerToolkit):
|
|
101
114
|
url: str
|
|
102
115
|
repository: str
|
|
103
116
|
private_token: SecretStr
|
|
104
117
|
branch: Optional[str] = 'main'
|
|
105
118
|
_git: Any = PrivateAttr()
|
|
106
|
-
_repo_instance: Any = PrivateAttr()
|
|
107
119
|
_active_branch: Any = PrivateAttr()
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
vectorstore_type: Optional[str] = "PGVector"
|
|
120
|
+
|
|
121
|
+
# Import file operation methods from BaseCodeToolApiWrapper
|
|
122
|
+
read_file_chunk = BaseCodeToolApiWrapper.read_file_chunk
|
|
123
|
+
read_multiple_files = BaseCodeToolApiWrapper.read_multiple_files
|
|
124
|
+
search_file = BaseCodeToolApiWrapper.search_file
|
|
125
|
+
edit_file = BaseCodeToolApiWrapper.edit_file
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def _sanitize_url(url: str) -> str:
|
|
129
|
+
"""Remove trailing slash from URL if present."""
|
|
130
|
+
return url.rstrip('/') if url else url
|
|
120
131
|
|
|
121
132
|
@model_validator(mode='before')
|
|
122
133
|
@classmethod
|
|
123
|
-
def
|
|
134
|
+
def validate_toolkit_before(cls, values: Dict) -> Dict:
|
|
135
|
+
return super().validate_toolkit(values)
|
|
136
|
+
|
|
137
|
+
@model_validator(mode='after')
|
|
138
|
+
def validate_toolkit(self):
|
|
124
139
|
try:
|
|
125
|
-
|
|
140
|
+
import gitlab
|
|
126
141
|
except ImportError:
|
|
127
142
|
raise ImportError(
|
|
128
143
|
"python-gitlab is not installed. "
|
|
129
144
|
"Please install it with `pip install python-gitlab`"
|
|
130
145
|
)
|
|
131
|
-
|
|
146
|
+
self.repository = self._sanitize_url(self.repository)
|
|
132
147
|
g = gitlab.Gitlab(
|
|
133
|
-
url=
|
|
134
|
-
private_token=
|
|
148
|
+
url=self._sanitize_url(self.url),
|
|
149
|
+
private_token=self.private_token.get_secret_value(),
|
|
135
150
|
keep_base_url=True,
|
|
136
151
|
)
|
|
137
152
|
|
|
138
153
|
g.auth()
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
self._git = g
|
|
155
|
+
self._active_branch = self.branch
|
|
156
|
+
return self
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def repo_instance(self):
|
|
160
|
+
if not hasattr(self, "_repo_instance") or self._repo_instance is None:
|
|
161
|
+
try:
|
|
162
|
+
if self._git and self.repository:
|
|
163
|
+
self._repo_instance = self._git.projects.get(self.repository)
|
|
164
|
+
else:
|
|
165
|
+
self._repo_instance = None
|
|
166
|
+
except Exception as e:
|
|
167
|
+
# Only raise when accessed, not during initialization
|
|
168
|
+
raise ToolException(e)
|
|
169
|
+
return self._repo_instance
|
|
143
170
|
|
|
144
171
|
def set_active_branch(self, branch_name: str) -> str:
|
|
145
172
|
self._active_branch = branch_name
|
|
146
|
-
self.
|
|
173
|
+
self.repo_instance.default_branch = branch_name
|
|
147
174
|
return f"Active branch set to {branch_name}"
|
|
148
175
|
|
|
149
176
|
def list_branches_in_repo(self, limit: Optional[int] = 20, branch_wildcard: Optional[str] = None) -> List[str]:
|
|
@@ -158,7 +185,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
158
185
|
List[str]: List containing names of branches
|
|
159
186
|
"""
|
|
160
187
|
try:
|
|
161
|
-
branches = self.
|
|
188
|
+
branches = self.repo_instance.branches.list(get_all=True)
|
|
162
189
|
|
|
163
190
|
if branch_wildcard:
|
|
164
191
|
branches = [branch for branch in branches if fnmatch.fnmatch(branch.name, branch_wildcard)]
|
|
@@ -185,7 +212,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
185
212
|
|
|
186
213
|
def _get_all_files(self, path: str = None, recursive: bool = True, branch: str = None):
|
|
187
214
|
branch = branch if branch else self._active_branch
|
|
188
|
-
return self.
|
|
215
|
+
return self.repo_instance.repository_tree(path=path, ref=branch, recursive=recursive, all=True)
|
|
189
216
|
|
|
190
217
|
# overrided for indexer
|
|
191
218
|
def _get_files(self, path: str = None, recursive: bool = True, branch: str = None):
|
|
@@ -197,17 +224,31 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
197
224
|
Get the commit hash of a file in a specific branch.
|
|
198
225
|
"""
|
|
199
226
|
try:
|
|
200
|
-
file = self.
|
|
227
|
+
file = self.repo_instance.files.get(file_path, branch)
|
|
201
228
|
return file.commit_id
|
|
202
229
|
except Exception as e:
|
|
203
230
|
return f"Unable to get commit hash for {file_path} due to error:\n{e}"
|
|
204
231
|
|
|
205
|
-
def _read_file(self, file_path: str, branch: str):
|
|
232
|
+
def _read_file(self, file_path: str, branch: str, **kwargs):
|
|
233
|
+
"""
|
|
234
|
+
Read a file from specified branch with optional partial read support.
|
|
235
|
+
|
|
236
|
+
Parameters:
|
|
237
|
+
file_path: the file path
|
|
238
|
+
branch: the branch to read the file from
|
|
239
|
+
**kwargs: Additional parameters (offset, limit, head, tail) - currently ignored,
|
|
240
|
+
partial read handled client-side by base class methods
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
File content as string
|
|
244
|
+
"""
|
|
245
|
+
# Default to active branch if branch is None, consistent with other methods
|
|
246
|
+
branch = branch if branch else self._active_branch
|
|
206
247
|
return self.read_file(file_path, branch)
|
|
207
248
|
|
|
208
249
|
def create_branch(self, branch_name: str) -> str:
|
|
209
250
|
try:
|
|
210
|
-
self.
|
|
251
|
+
self.repo_instance.branches.create(
|
|
211
252
|
{
|
|
212
253
|
'branch': branch_name,
|
|
213
254
|
'ref': self._active_branch,
|
|
@@ -230,7 +271,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
230
271
|
return parsed
|
|
231
272
|
|
|
232
273
|
def get_issues(self) -> str:
|
|
233
|
-
issues = self.
|
|
274
|
+
issues = self.repo_instance.issues.list(state="opened")
|
|
234
275
|
if len(issues) > 0:
|
|
235
276
|
parsed_issues = self.parse_issues(issues)
|
|
236
277
|
parsed_issues_str = (
|
|
@@ -241,7 +282,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
241
282
|
return "No open issues available"
|
|
242
283
|
|
|
243
284
|
def get_issue(self, issue_number: int) -> Dict[str, Any]:
|
|
244
|
-
issue = self.
|
|
285
|
+
issue = self.repo_instance.issues.get(issue_number)
|
|
245
286
|
page = 0
|
|
246
287
|
comments: List[dict] = []
|
|
247
288
|
while len(comments) <= 10:
|
|
@@ -267,7 +308,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
267
308
|
commits are already in the {self.branch} branch"""
|
|
268
309
|
else:
|
|
269
310
|
try:
|
|
270
|
-
pr = self.
|
|
311
|
+
pr = self.repo_instance.mergerequests.create(
|
|
271
312
|
{
|
|
272
313
|
"source_branch": branch,
|
|
273
314
|
"target_branch": self.branch,
|
|
@@ -284,16 +325,39 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
284
325
|
issue_number = int(comment_query.split("\n\n")[0])
|
|
285
326
|
comment = comment_query[len(str(issue_number)) + 2 :]
|
|
286
327
|
try:
|
|
287
|
-
issue = self.
|
|
328
|
+
issue = self.repo_instance.issues.get(issue_number)
|
|
288
329
|
issue.notes.create({"body": comment})
|
|
289
330
|
return "Commented on issue " + str(issue_number)
|
|
290
331
|
except Exception as e:
|
|
291
332
|
return "Unable to make comment due to error:\n" + str(e)
|
|
292
333
|
|
|
334
|
+
def comment_on_pr(self, pr_number: int, comment: str) -> str:
|
|
335
|
+
"""
|
|
336
|
+
Add a comment to a pull request (merge request) in GitLab.
|
|
337
|
+
|
|
338
|
+
This method adds a general comment to the entire merge request,
|
|
339
|
+
not tied to specific code lines or file changes.
|
|
340
|
+
|
|
341
|
+
Parameters:
|
|
342
|
+
pr_number: GitLab Merge Request (Pull Request) number
|
|
343
|
+
comment: Comment text to add
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
Success message or error description
|
|
347
|
+
"""
|
|
348
|
+
try:
|
|
349
|
+
mr = self.repo_instance.mergerequests.get(pr_number)
|
|
350
|
+
mr.notes.create({"body": comment})
|
|
351
|
+
return "Commented on merge request " + str(pr_number)
|
|
352
|
+
except Exception as e:
|
|
353
|
+
return "Unable to make comment due to error:\n" + str(e)
|
|
354
|
+
|
|
293
355
|
def create_file(self, file_path: str, file_contents: str, branch: str) -> str:
|
|
356
|
+
# Default to active branch if branch is None
|
|
357
|
+
branch = branch if branch else self._active_branch
|
|
294
358
|
try:
|
|
295
359
|
self.set_active_branch(branch)
|
|
296
|
-
self.
|
|
360
|
+
self.repo_instance.files.get(file_path, branch)
|
|
297
361
|
return f"File already exists at {file_path}. Use update_file instead"
|
|
298
362
|
except Exception:
|
|
299
363
|
data = {
|
|
@@ -302,53 +366,126 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
302
366
|
"file_path": file_path,
|
|
303
367
|
"content": file_contents,
|
|
304
368
|
}
|
|
305
|
-
self.
|
|
369
|
+
self.repo_instance.files.create(data)
|
|
306
370
|
|
|
307
371
|
return "Created file " + file_path
|
|
308
372
|
|
|
309
373
|
def read_file(self, file_path: str, branch: str) -> str:
|
|
374
|
+
# Default to active branch if branch is None
|
|
375
|
+
branch = branch if branch else self._active_branch
|
|
310
376
|
self.set_active_branch(branch)
|
|
311
|
-
file = self.
|
|
312
|
-
return
|
|
377
|
+
file = self.repo_instance.files.get(file_path, branch)
|
|
378
|
+
return parse_file_content(file_name=file_path,
|
|
379
|
+
file_content=file.decode(),
|
|
380
|
+
llm=self.llm)
|
|
381
|
+
|
|
382
|
+
def _write_file(
|
|
383
|
+
self,
|
|
384
|
+
file_path: str,
|
|
385
|
+
content: str,
|
|
386
|
+
branch: str = None,
|
|
387
|
+
commit_message: str = None
|
|
388
|
+
) -> str:
|
|
389
|
+
"""
|
|
390
|
+
Write content to a file (create or update).
|
|
391
|
+
|
|
392
|
+
Parameters:
|
|
393
|
+
file_path: Path to the file
|
|
394
|
+
content: New file content
|
|
395
|
+
branch: Branch name (uses active branch if None)
|
|
396
|
+
commit_message: Commit message
|
|
397
|
+
|
|
398
|
+
Returns:
|
|
399
|
+
Success message
|
|
400
|
+
"""
|
|
401
|
+
try:
|
|
402
|
+
branch = branch or self._active_branch
|
|
403
|
+
|
|
404
|
+
if branch == self.branch:
|
|
405
|
+
raise ToolException(
|
|
406
|
+
f"Cannot commit directly to the {self.branch} branch. "
|
|
407
|
+
"Please create a new branch and try again."
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
self.set_active_branch(branch)
|
|
411
|
+
|
|
412
|
+
# Check if file exists
|
|
413
|
+
try:
|
|
414
|
+
self.repo_instance.files.get(file_path, branch)
|
|
415
|
+
# File exists, update it
|
|
416
|
+
commit = {
|
|
417
|
+
"branch": branch,
|
|
418
|
+
"commit_message": commit_message or f"Update {file_path}",
|
|
419
|
+
"actions": [
|
|
420
|
+
{
|
|
421
|
+
"action": "update",
|
|
422
|
+
"file_path": file_path,
|
|
423
|
+
"content": content,
|
|
424
|
+
}
|
|
425
|
+
],
|
|
426
|
+
}
|
|
427
|
+
self.repo_instance.commits.create(commit)
|
|
428
|
+
return f"Updated file {file_path}"
|
|
429
|
+
except:
|
|
430
|
+
# File doesn't exist, create it
|
|
431
|
+
data = {
|
|
432
|
+
"branch": branch,
|
|
433
|
+
"commit_message": commit_message or f"Create {file_path}",
|
|
434
|
+
"file_path": file_path,
|
|
435
|
+
"content": content,
|
|
436
|
+
}
|
|
437
|
+
self.repo_instance.files.create(data)
|
|
438
|
+
return f"Created file {file_path}"
|
|
439
|
+
except Exception as e:
|
|
440
|
+
raise ToolException(f"Unable to write file {file_path}: {str(e)}")
|
|
313
441
|
|
|
314
442
|
def update_file(self, file_query: str, branch: str) -> str:
|
|
443
|
+
"""
|
|
444
|
+
Update file using edit_file functionality.
|
|
445
|
+
|
|
446
|
+
This method now delegates to edit_file which uses OLD/NEW markers.
|
|
447
|
+
For backwards compatibility, it extracts the file_path from the query.
|
|
448
|
+
|
|
449
|
+
Expected format:
|
|
450
|
+
file_path
|
|
451
|
+
OLD <<<<
|
|
452
|
+
old content
|
|
453
|
+
>>>> OLD
|
|
454
|
+
NEW <<<<
|
|
455
|
+
new content
|
|
456
|
+
>>>> NEW
|
|
457
|
+
|
|
458
|
+
Args:
|
|
459
|
+
file_query: File path on first line, followed by OLD/NEW markers
|
|
460
|
+
branch: Branch to update the file in
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
Success or error message
|
|
464
|
+
"""
|
|
315
465
|
if branch == self.branch:
|
|
316
466
|
return (
|
|
317
|
-
"You're attempting to commit
|
|
467
|
+
"You're attempting to commit directly "
|
|
318
468
|
f"to the {self.branch} branch, which is protected. "
|
|
319
469
|
"Please create a new branch and try again."
|
|
320
470
|
)
|
|
321
471
|
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:
|
|
472
|
+
# Extract file path from first line
|
|
473
|
+
lines = file_query.split("\n", 1)
|
|
474
|
+
if len(lines) < 2:
|
|
332
475
|
return (
|
|
333
|
-
"
|
|
334
|
-
"
|
|
335
|
-
"
|
|
476
|
+
"Invalid file_query format. Expected:\n"
|
|
477
|
+
"file_path\n"
|
|
478
|
+
"OLD <<<< old content >>>> OLD\n"
|
|
479
|
+
"NEW <<<< new content >>>> NEW"
|
|
336
480
|
)
|
|
337
481
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
"file_path": file_path,
|
|
345
|
-
"content": updated_file_content,
|
|
346
|
-
}
|
|
347
|
-
],
|
|
348
|
-
}
|
|
482
|
+
file_path = lines[0].strip()
|
|
483
|
+
edit_content = lines[1] if len(lines) > 1 else ""
|
|
484
|
+
|
|
485
|
+
# Delegate to edit_file method with appropriate commit message
|
|
486
|
+
commit_message = f"Update {file_path}"
|
|
487
|
+
return self.edit_file(file_path, edit_content, branch, commit_message)
|
|
349
488
|
|
|
350
|
-
self._repo_instance.commits.create(commit)
|
|
351
|
-
return "Updated file " + file_path
|
|
352
489
|
except Exception as e:
|
|
353
490
|
return "Unable to update file due to error:\n" + str(e)
|
|
354
491
|
|
|
@@ -377,7 +514,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
377
514
|
],
|
|
378
515
|
}
|
|
379
516
|
|
|
380
|
-
self.
|
|
517
|
+
self.repo_instance.commits.create(commit)
|
|
381
518
|
return "Updated file " + file_path
|
|
382
519
|
except Exception as e:
|
|
383
520
|
return "Unable to update file due to error:\n" + str(e)
|
|
@@ -387,23 +524,54 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
387
524
|
self.set_active_branch(branch)
|
|
388
525
|
if not commit_message:
|
|
389
526
|
commit_message = f"Delete {file_path}"
|
|
390
|
-
self.
|
|
527
|
+
self.repo_instance.files.delete(file_path, branch, commit_message)
|
|
391
528
|
return f"Deleted file {file_path}"
|
|
392
529
|
except Exception as e:
|
|
393
530
|
return f"Unable to delete file due to error:\n{e}"
|
|
394
531
|
|
|
395
532
|
def get_pr_changes(self, pr_number: int) -> str:
|
|
396
|
-
mr = self.
|
|
533
|
+
mr = self.repo_instance.mergerequests.get(pr_number)
|
|
397
534
|
res = f"title: {mr.title}\ndescription: {mr.description}\n\n"
|
|
398
535
|
for change in mr.changes()["changes"]:
|
|
399
536
|
res += f"diff --git a/{change['old_path']} b/{change['new_path']}\n{change['diff']}\n"
|
|
400
537
|
return res
|
|
401
538
|
|
|
402
539
|
def create_pr_change_comment(self, pr_number: int, file_path: str, line_number: int, comment: str) -> str:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
540
|
+
"""
|
|
541
|
+
Create a comment on a specific line in a pull request (merge request) change in GitLab.
|
|
542
|
+
|
|
543
|
+
This method adds an inline comment to a specific line in the diff of a merge request.
|
|
544
|
+
The line_number parameter refers to the line index in the diff output (0-based),
|
|
545
|
+
not the line number in the original file.
|
|
546
|
+
|
|
547
|
+
**Important**: Use get_pr_changes first to see the diff and identify the correct
|
|
548
|
+
line index for commenting.
|
|
549
|
+
|
|
550
|
+
Parameters:
|
|
551
|
+
pr_number: GitLab Merge Request number
|
|
552
|
+
file_path: Path to the file being commented on (as shown in the diff)
|
|
553
|
+
line_number: Line index from the diff (0-based index)
|
|
554
|
+
comment: Comment text to add
|
|
555
|
+
|
|
556
|
+
Returns:
|
|
557
|
+
Success message or error description
|
|
558
|
+
"""
|
|
559
|
+
try:
|
|
560
|
+
mr = self.repo_instance.mergerequests.get(pr_number)
|
|
561
|
+
except GitlabGetError as e:
|
|
562
|
+
if e.response_code == 404:
|
|
563
|
+
raise ToolException(f"Merge request number {pr_number} wasn't found: {e}")
|
|
564
|
+
raise ToolException(f"Error retrieving merge request {pr_number}: {e}")
|
|
565
|
+
|
|
566
|
+
try:
|
|
567
|
+
# Calculate proper position with SHA references and line mappings
|
|
568
|
+
position = get_position(file_path=file_path, line_number=line_number, mr=mr)
|
|
569
|
+
|
|
570
|
+
# Create discussion with the comment
|
|
571
|
+
mr.discussions.create({"body": comment, "position": position})
|
|
572
|
+
return f"Comment added successfully to line {line_number} in {file_path} on MR #{pr_number}"
|
|
573
|
+
except Exception as e:
|
|
574
|
+
raise ToolException(f"Failed to create comment on MR #{pr_number}: {e}")
|
|
407
575
|
|
|
408
576
|
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
577
|
params = {}
|
|
@@ -417,7 +585,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
417
585
|
params["until"] = until
|
|
418
586
|
if author:
|
|
419
587
|
params["author"] = author
|
|
420
|
-
commits = self.
|
|
588
|
+
commits = self.repo_instance.commits.list(**params)
|
|
421
589
|
return [
|
|
422
590
|
{
|
|
423
591
|
"sha": commit.id,
|
|
@@ -429,6 +597,8 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
429
597
|
for commit in commits
|
|
430
598
|
]
|
|
431
599
|
|
|
600
|
+
@extend_with_parent_available_tools
|
|
601
|
+
@extend_with_file_operations
|
|
432
602
|
def get_available_tools(self):
|
|
433
603
|
return [
|
|
434
604
|
{
|
|
@@ -479,6 +649,12 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
479
649
|
"description": self.comment_on_issue.__doc__ or "Comment on an issue in the repository.",
|
|
480
650
|
"args_schema": CommentOnIssueModel,
|
|
481
651
|
},
|
|
652
|
+
{
|
|
653
|
+
"name": "comment_on_pr",
|
|
654
|
+
"ref": self.comment_on_pr,
|
|
655
|
+
"description": self.comment_on_pr.__doc__ or "Comment on a pull request (merge request) in the repository.",
|
|
656
|
+
"args_schema": CommentOnPRModel,
|
|
657
|
+
},
|
|
482
658
|
{
|
|
483
659
|
"name": "create_file",
|
|
484
660
|
"ref": self.create_file,
|
|
@@ -524,7 +700,7 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
524
700
|
{
|
|
525
701
|
"name": "create_pr_change_comment",
|
|
526
702
|
"ref": self.create_pr_change_comment,
|
|
527
|
-
"description": "Create
|
|
703
|
+
"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
704
|
"args_schema": CreatePRChangeCommentModel,
|
|
529
705
|
},
|
|
530
706
|
{
|
|
@@ -533,4 +709,4 @@ class GitLabAPIWrapper(BaseCodeToolApiWrapper):
|
|
|
533
709
|
"description": "Retrieve a list of commits from the repository.",
|
|
534
710
|
"args_schema": GetCommitsModel,
|
|
535
711
|
}
|
|
536
|
-
]
|
|
712
|
+
]
|
|
@@ -4,8 +4,11 @@ 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
|
|
11
|
+
from ...runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
9
12
|
|
|
10
13
|
name = "gitlab_org"
|
|
11
14
|
|
|
@@ -20,17 +23,13 @@ def get_tools(tool):
|
|
|
20
23
|
|
|
21
24
|
class AlitaGitlabSpaceToolkit(BaseToolkit):
|
|
22
25
|
tools: List[BaseTool] = []
|
|
23
|
-
toolkit_max_length: int = 0
|
|
24
26
|
|
|
25
27
|
@staticmethod
|
|
26
28
|
def toolkit_config_schema() -> BaseModel:
|
|
27
29
|
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
30
|
return create_model(
|
|
30
31
|
name,
|
|
31
|
-
|
|
32
|
-
'max_toolkit_length': AlitaGitlabSpaceToolkit.toolkit_max_length})),
|
|
33
|
-
gitlab_configuration=(Optional[GitlabConfiguration], Field(description="GitLab configuration",
|
|
32
|
+
gitlab_configuration=(GitlabConfiguration, Field(description="GitLab configuration",
|
|
34
33
|
json_schema_extra={
|
|
35
34
|
'configuration_types': ['gitlab']})),
|
|
36
35
|
repositories=(str, Field(
|
|
@@ -51,6 +50,7 @@ class AlitaGitlabSpaceToolkit(BaseToolkit):
|
|
|
51
50
|
)
|
|
52
51
|
|
|
53
52
|
@classmethod
|
|
53
|
+
@filter_missconfigured_index_tools
|
|
54
54
|
def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
|
|
55
55
|
if selected_tools is None:
|
|
56
56
|
selected_tools = []
|
|
@@ -60,18 +60,22 @@ class AlitaGitlabSpaceToolkit(BaseToolkit):
|
|
|
60
60
|
**kwargs['gitlab_configuration'],
|
|
61
61
|
}
|
|
62
62
|
gitlab_wrapper = GitLabWorkspaceAPIWrapper(**wrapper_payload)
|
|
63
|
-
prefix = clean_string(toolkit_name, AlitaGitlabSpaceToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
|
|
64
63
|
available_tools = gitlab_wrapper.get_available_tools()
|
|
65
64
|
tools = []
|
|
66
65
|
for tool in available_tools:
|
|
67
66
|
if selected_tools:
|
|
68
67
|
if tool["name"] not in selected_tools:
|
|
69
68
|
continue
|
|
69
|
+
description = tool["description"]
|
|
70
|
+
if toolkit_name:
|
|
71
|
+
description = f"Toolkit: {toolkit_name}\n{description}"
|
|
72
|
+
description = description[:1000]
|
|
70
73
|
tools.append(BaseAction(
|
|
71
74
|
api_wrapper=gitlab_wrapper,
|
|
72
|
-
name=
|
|
73
|
-
description=
|
|
74
|
-
args_schema=tool["args_schema"]
|
|
75
|
+
name=tool['name'],
|
|
76
|
+
description=description,
|
|
77
|
+
args_schema=tool["args_schema"],
|
|
78
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
|
|
75
79
|
))
|
|
76
80
|
return cls(tools=tools)
|
|
77
81
|
|
|
@@ -5,9 +5,10 @@ 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
|
+
from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
|
|
11
12
|
|
|
12
13
|
name = "bigquery"
|
|
13
14
|
|
|
@@ -22,11 +23,6 @@ def get_available_tools() -> dict[str, dict]:
|
|
|
22
23
|
return available_tools
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
toolkit_max_length = lru_cache(maxsize=1)(
|
|
26
|
-
lambda: get_max_toolkit_length(get_available_tools())
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
|
|
30
26
|
class BigQueryToolkitConfig(BaseModel):
|
|
31
27
|
class Config:
|
|
32
28
|
title = name
|
|
@@ -46,7 +42,7 @@ class BigQueryToolkitConfig(BaseModel):
|
|
|
46
42
|
}
|
|
47
43
|
}
|
|
48
44
|
|
|
49
|
-
bigquery_configuration:
|
|
45
|
+
bigquery_configuration: BigQueryConfiguration = Field(
|
|
50
46
|
description="BigQuery configuration", json_schema_extra={"configuration_types": ["bigquery"]}
|
|
51
47
|
)
|
|
52
48
|
selected_tools: List[str] = Field(
|
|
@@ -86,9 +82,10 @@ class BigQueryToolkit(BaseToolkit):
|
|
|
86
82
|
|
|
87
83
|
@computed_field
|
|
88
84
|
@property
|
|
89
|
-
def
|
|
85
|
+
def toolkit_context(self) -> str:
|
|
86
|
+
"""Returns toolkit context for descriptions (max 1000 chars)."""
|
|
90
87
|
return (
|
|
91
|
-
clean_string(self.toolkit_name,
|
|
88
|
+
f" [Toolkit: {clean_string(self.toolkit_name, 0)}]"
|
|
92
89
|
if self.toolkit_name
|
|
93
90
|
else ""
|
|
94
91
|
)
|
|
@@ -122,14 +119,18 @@ class BigQueryToolkit(BaseToolkit):
|
|
|
122
119
|
selected_tools = set(selected_tools)
|
|
123
120
|
for t in instance.available_tools:
|
|
124
121
|
if t["name"] in selected_tools:
|
|
122
|
+
description = t["description"]
|
|
123
|
+
if toolkit_name:
|
|
124
|
+
description = f"Toolkit: {toolkit_name}\n{description}"
|
|
125
|
+
description = f"Project: {getattr(instance.api_wrapper, 'project', '')}\n{description}"
|
|
126
|
+
description = description[:1000]
|
|
125
127
|
instance.tools.append(
|
|
126
128
|
BigQueryAction(
|
|
127
129
|
api_wrapper=instance.api_wrapper,
|
|
128
|
-
name=
|
|
129
|
-
|
|
130
|
-
description=f"Project: {getattr(instance.api_wrapper, 'project', '')}\n"
|
|
131
|
-
+ t["description"],
|
|
130
|
+
name=t["name"],
|
|
131
|
+
description=description,
|
|
132
132
|
args_schema=t["args_schema"],
|
|
133
|
+
metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: t["name"]} if toolkit_name else {TOOL_NAME_META: t["name"]}
|
|
133
134
|
)
|
|
134
135
|
)
|
|
135
136
|
return instance
|