alita-sdk 0.3.486__py3-none-any.whl → 0.3.515__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.

Files changed (124) hide show
  1. alita_sdk/cli/agent_loader.py +27 -6
  2. alita_sdk/cli/agents.py +10 -1
  3. alita_sdk/cli/inventory.py +12 -195
  4. alita_sdk/cli/tools/filesystem.py +95 -9
  5. alita_sdk/community/inventory/__init__.py +12 -0
  6. alita_sdk/community/inventory/toolkit.py +9 -5
  7. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  8. alita_sdk/configurations/ado.py +144 -0
  9. alita_sdk/configurations/confluence.py +76 -42
  10. alita_sdk/configurations/figma.py +76 -0
  11. alita_sdk/configurations/gitlab.py +2 -0
  12. alita_sdk/configurations/qtest.py +72 -1
  13. alita_sdk/configurations/report_portal.py +96 -0
  14. alita_sdk/configurations/sharepoint.py +148 -0
  15. alita_sdk/configurations/testio.py +83 -0
  16. alita_sdk/runtime/clients/artifact.py +2 -2
  17. alita_sdk/runtime/clients/client.py +64 -40
  18. alita_sdk/runtime/clients/sandbox_client.py +14 -0
  19. alita_sdk/runtime/langchain/assistant.py +48 -2
  20. alita_sdk/runtime/langchain/constants.py +3 -1
  21. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  22. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  23. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +2 -1
  24. alita_sdk/runtime/langchain/document_loaders/constants.py +12 -7
  25. alita_sdk/runtime/langchain/langraph_agent.py +10 -10
  26. alita_sdk/runtime/langchain/utils.py +6 -1
  27. alita_sdk/runtime/toolkits/artifact.py +14 -5
  28. alita_sdk/runtime/toolkits/datasource.py +13 -6
  29. alita_sdk/runtime/toolkits/mcp.py +94 -219
  30. alita_sdk/runtime/toolkits/planning.py +13 -6
  31. alita_sdk/runtime/toolkits/tools.py +60 -25
  32. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  33. alita_sdk/runtime/tools/artifact.py +185 -23
  34. alita_sdk/runtime/tools/function.py +2 -1
  35. alita_sdk/runtime/tools/llm.py +155 -34
  36. alita_sdk/runtime/tools/mcp_remote_tool.py +25 -10
  37. alita_sdk/runtime/tools/mcp_server_tool.py +2 -4
  38. alita_sdk/runtime/tools/vectorstore_base.py +3 -3
  39. alita_sdk/runtime/utils/AlitaCallback.py +136 -21
  40. alita_sdk/runtime/utils/mcp_client.py +492 -0
  41. alita_sdk/runtime/utils/mcp_oauth.py +125 -8
  42. alita_sdk/runtime/utils/mcp_sse_client.py +35 -6
  43. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  44. alita_sdk/runtime/utils/toolkit_utils.py +7 -13
  45. alita_sdk/runtime/utils/utils.py +2 -0
  46. alita_sdk/tools/__init__.py +15 -0
  47. alita_sdk/tools/ado/repos/__init__.py +10 -12
  48. alita_sdk/tools/ado/test_plan/__init__.py +23 -8
  49. alita_sdk/tools/ado/wiki/__init__.py +24 -8
  50. alita_sdk/tools/ado/wiki/ado_wrapper.py +21 -7
  51. alita_sdk/tools/ado/work_item/__init__.py +24 -8
  52. alita_sdk/tools/advanced_jira_mining/__init__.py +10 -8
  53. alita_sdk/tools/aws/delta_lake/__init__.py +12 -9
  54. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  55. alita_sdk/tools/azure_ai/search/__init__.py +9 -7
  56. alita_sdk/tools/base/tool.py +5 -1
  57. alita_sdk/tools/base_indexer_toolkit.py +26 -1
  58. alita_sdk/tools/bitbucket/__init__.py +14 -10
  59. alita_sdk/tools/bitbucket/api_wrapper.py +50 -2
  60. alita_sdk/tools/browser/__init__.py +5 -4
  61. alita_sdk/tools/carrier/__init__.py +5 -6
  62. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  63. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +2 -0
  64. alita_sdk/tools/chunkers/universal_chunker.py +1 -0
  65. alita_sdk/tools/cloud/aws/__init__.py +9 -7
  66. alita_sdk/tools/cloud/azure/__init__.py +9 -7
  67. alita_sdk/tools/cloud/gcp/__init__.py +9 -7
  68. alita_sdk/tools/cloud/k8s/__init__.py +9 -7
  69. alita_sdk/tools/code/linter/__init__.py +9 -8
  70. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  71. alita_sdk/tools/code/sonar/__init__.py +9 -7
  72. alita_sdk/tools/confluence/__init__.py +15 -10
  73. alita_sdk/tools/confluence/api_wrapper.py +63 -14
  74. alita_sdk/tools/custom_open_api/__init__.py +11 -5
  75. alita_sdk/tools/elastic/__init__.py +10 -8
  76. alita_sdk/tools/elitea_base.py +387 -9
  77. alita_sdk/tools/figma/__init__.py +8 -7
  78. alita_sdk/tools/github/__init__.py +12 -14
  79. alita_sdk/tools/github/github_client.py +68 -2
  80. alita_sdk/tools/github/tool.py +5 -1
  81. alita_sdk/tools/gitlab/__init__.py +14 -11
  82. alita_sdk/tools/gitlab/api_wrapper.py +81 -1
  83. alita_sdk/tools/gitlab_org/__init__.py +9 -8
  84. alita_sdk/tools/google/bigquery/__init__.py +12 -12
  85. alita_sdk/tools/google/bigquery/tool.py +5 -1
  86. alita_sdk/tools/google_places/__init__.py +9 -8
  87. alita_sdk/tools/jira/__init__.py +15 -10
  88. alita_sdk/tools/keycloak/__init__.py +10 -8
  89. alita_sdk/tools/localgit/__init__.py +8 -3
  90. alita_sdk/tools/localgit/local_git.py +62 -54
  91. alita_sdk/tools/localgit/tool.py +5 -1
  92. alita_sdk/tools/memory/__init__.py +11 -3
  93. alita_sdk/tools/ocr/__init__.py +10 -8
  94. alita_sdk/tools/openapi/__init__.py +6 -2
  95. alita_sdk/tools/pandas/__init__.py +9 -7
  96. alita_sdk/tools/postman/__init__.py +10 -11
  97. alita_sdk/tools/pptx/__init__.py +9 -9
  98. alita_sdk/tools/qtest/__init__.py +9 -8
  99. alita_sdk/tools/rally/__init__.py +9 -8
  100. alita_sdk/tools/report_portal/__init__.py +11 -9
  101. alita_sdk/tools/salesforce/__init__.py +9 -9
  102. alita_sdk/tools/servicenow/__init__.py +10 -8
  103. alita_sdk/tools/sharepoint/__init__.py +9 -8
  104. alita_sdk/tools/sharepoint/api_wrapper.py +2 -2
  105. alita_sdk/tools/slack/__init__.py +8 -7
  106. alita_sdk/tools/sql/__init__.py +9 -8
  107. alita_sdk/tools/testio/__init__.py +9 -8
  108. alita_sdk/tools/testrail/__init__.py +10 -8
  109. alita_sdk/tools/utils/__init__.py +9 -4
  110. alita_sdk/tools/utils/text_operations.py +254 -0
  111. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +16 -18
  112. alita_sdk/tools/xray/__init__.py +10 -8
  113. alita_sdk/tools/yagmail/__init__.py +8 -3
  114. alita_sdk/tools/zephyr/__init__.py +8 -7
  115. alita_sdk/tools/zephyr_enterprise/__init__.py +10 -8
  116. alita_sdk/tools/zephyr_essential/__init__.py +9 -8
  117. alita_sdk/tools/zephyr_scale/__init__.py +9 -8
  118. alita_sdk/tools/zephyr_squad/__init__.py +9 -8
  119. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/METADATA +1 -1
  120. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/RECORD +124 -119
  121. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/WHEEL +0 -0
  122. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/entry_points.txt +0 -0
  123. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/licenses/LICENSE +0 -0
  124. {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.515.dist-info}/top_level.txt +0 -0
@@ -71,6 +71,7 @@ class McpSseClient:
71
71
  McpAuthorizationRequired,
72
72
  canonical_resource,
73
73
  extract_resource_metadata_url,
74
+ extract_authorization_uri,
74
75
  fetch_resource_metadata_async,
75
76
  infer_authorization_servers_from_realm,
76
77
  fetch_oauth_authorization_server_metadata
@@ -79,13 +80,41 @@ class McpSseClient:
79
80
  auth_header = self._stream_response.headers.get('WWW-Authenticate', '')
80
81
  resource_metadata_url = extract_resource_metadata_url(auth_header, self.url)
81
82
 
83
+ # First, try authorization_uri from WWW-Authenticate header (preferred)
84
+ authorization_uri = extract_authorization_uri(auth_header)
85
+
82
86
  metadata = None
83
- if resource_metadata_url:
84
- metadata = await fetch_resource_metadata_async(
85
- resource_metadata_url,
86
- session=self._stream_session,
87
- timeout=30
88
- )
87
+ if authorization_uri:
88
+ # Fetch OAuth metadata directly from authorization_uri
89
+ auth_server_metadata = fetch_oauth_authorization_server_metadata(authorization_uri, timeout=30)
90
+ if auth_server_metadata:
91
+ # Extract base authorization server URL from the issuer or the well-known URL
92
+ base_auth_server = auth_server_metadata.get('issuer')
93
+ if not base_auth_server and '/.well-known/' in authorization_uri:
94
+ base_auth_server = authorization_uri.split('/.well-known/')[0]
95
+
96
+ metadata = {
97
+ 'authorization_servers': [base_auth_server] if base_auth_server else [authorization_uri],
98
+ 'oauth_authorization_server': auth_server_metadata
99
+ }
100
+ logger.info(f"[MCP SSE Client] Using authorization_uri: {authorization_uri}, base: {base_auth_server}")
101
+
102
+ # Fall back to resource_metadata if authorization_uri didn't work
103
+ if not metadata:
104
+ if resource_metadata_url:
105
+ metadata = await fetch_resource_metadata_async(
106
+ resource_metadata_url,
107
+ session=self._stream_session,
108
+ timeout=30
109
+ )
110
+ # If we got resource_metadata, also fetch oauth_authorization_server
111
+ if metadata and metadata.get('authorization_servers'):
112
+ auth_server_metadata = fetch_oauth_authorization_server_metadata(
113
+ metadata['authorization_servers'][0], timeout=30
114
+ )
115
+ if auth_server_metadata:
116
+ metadata['oauth_authorization_server'] = auth_server_metadata
117
+ logger.info(f"[MCP SSE Client] Fetched OAuth metadata from resource_metadata")
89
118
 
90
119
  # Infer authorization servers if not in metadata
91
120
  if not metadata or not metadata.get('authorization_servers'):
@@ -0,0 +1,124 @@
1
+ """
2
+ MCP Tools Discovery Utility.
3
+ Provides a standalone function to discover tools from remote MCP servers.
4
+ Supports both SSE (Server-Sent Events) and Streamable HTTP transports with auto-detection.
5
+ """
6
+
7
+ import asyncio
8
+ import logging
9
+ from typing import Any, Dict, List, Optional
10
+
11
+ from .mcp_oauth import McpAuthorizationRequired
12
+ from .mcp_client import McpClient
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+
17
+ def discover_mcp_tools(
18
+ url: str,
19
+ headers: Optional[Dict[str, str]] = None,
20
+ timeout: int = 60,
21
+ session_id: Optional[str] = None,
22
+ ) -> List[Dict[str, Any]]:
23
+ """
24
+ Discover available tools from a remote MCP server.
25
+
26
+ This function connects to a remote MCP server and retrieves the list of
27
+ available tools using the MCP protocol. Automatically detects and uses
28
+ the appropriate transport (SSE or Streamable HTTP).
29
+
30
+ Args:
31
+ url: MCP server HTTP URL (http:// or https://)
32
+ headers: Optional HTTP headers for authentication
33
+ timeout: Request timeout in seconds (default: 60)
34
+ session_id: Optional session ID for stateful connections
35
+
36
+ Returns:
37
+ List of tool definitions, each containing:
38
+ - name: Tool name
39
+ - description: Tool description
40
+ - inputSchema: JSON schema for tool input parameters
41
+
42
+ Raises:
43
+ McpAuthorizationRequired: If the server requires OAuth authorization (401)
44
+ Exception: For other connection or protocol errors
45
+
46
+ Example:
47
+ >>> tools = discover_mcp_tools(
48
+ ... url="https://mcp.example.com/sse",
49
+ ... headers={"Authorization": "Bearer token123"}
50
+ ... )
51
+ >>> print(f"Found {len(tools)} tools")
52
+ """
53
+ logger.info(f"[MCP Discovery] Starting tool discovery from {url}")
54
+
55
+ try:
56
+ # Run the async discovery in a new event loop
57
+ tools_list = asyncio.run(
58
+ _discover_tools_async(url, headers, timeout, session_id)
59
+ )
60
+ logger.info(f"[MCP Discovery] Successfully discovered {len(tools_list)} tools from {url}")
61
+ return tools_list
62
+
63
+ except McpAuthorizationRequired:
64
+ # Re-raise auth exceptions directly
65
+ logger.info(f"[MCP Discovery] Authorization required for {url}")
66
+ raise
67
+
68
+ except Exception as e:
69
+ logger.error(f"[MCP Discovery] Failed to discover tools from {url}: {e}")
70
+ raise
71
+
72
+
73
+ async def _discover_tools_async(
74
+ url: str,
75
+ headers: Optional[Dict[str, str]],
76
+ timeout: int,
77
+ session_id: Optional[str],
78
+ ) -> List[Dict[str, Any]]:
79
+ """
80
+ Async implementation of tool discovery using unified MCP client.
81
+ """
82
+ all_tools = []
83
+
84
+ # Create unified MCP client (auto-detects transport)
85
+ client = McpClient(
86
+ url=url,
87
+ session_id=session_id,
88
+ headers=headers,
89
+ timeout=timeout
90
+ )
91
+
92
+ async with client:
93
+ # Initialize MCP session
94
+ await client.initialize()
95
+ logger.debug(f"[MCP Discovery] Session initialized (transport={client.detected_transport})")
96
+
97
+ # Get tools list
98
+ tools = await client.list_tools()
99
+ logger.debug(f"[MCP Discovery] Received {len(tools)} tools")
100
+
101
+ # Convert tools to standard format
102
+ for tool in tools:
103
+ tool_def = {
104
+ 'name': tool.get('name'),
105
+ 'description': tool.get('description', ''),
106
+ 'inputSchema': tool.get('inputSchema', {}),
107
+ }
108
+ all_tools.append(tool_def)
109
+
110
+ return all_tools
111
+
112
+
113
+ async def discover_mcp_tools_async(
114
+ url: str,
115
+ headers: Optional[Dict[str, str]] = None,
116
+ timeout: int = 60,
117
+ session_id: Optional[str] = None,
118
+ ) -> List[Dict[str, Any]]:
119
+ """
120
+ Async version of discover_mcp_tools.
121
+
122
+ See discover_mcp_tools for full documentation.
123
+ """
124
+ return await _discover_tools_async(url, headers, timeout, session_id)
@@ -13,7 +13,8 @@ logger = logging.getLogger(__name__)
13
13
  def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
14
14
  llm_client: Any,
15
15
  alita_client: Optional[Any] = None,
16
- mcp_tokens: Optional[Dict[str, Any]] = None) -> List[Any]:
16
+ mcp_tokens: Optional[Dict[str, Any]] = None,
17
+ use_prefix: bool = False) -> List[Any]:
17
18
  """
18
19
  Instantiate a toolkit with LLM client support.
19
20
 
@@ -25,6 +26,9 @@ def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
25
26
  llm_client: LLM client instance for tools that need LLM capabilities
26
27
  alita_client: Optional additional client instance
27
28
  mcp_tokens: Optional dictionary of MCP OAuth tokens by server URL
29
+ use_prefix: If True, tools get prefixed with toolkit_name to prevent collisions
30
+ (for agent use). If False, tools use base names only (for testing interface).
31
+ Default False for backward compatibility with testing.
28
32
 
29
33
  Returns:
30
34
  List of instantiated tools from the toolkit
@@ -54,11 +58,12 @@ def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
54
58
  toolkit_type = toolkit_config.get('type', toolkit_name.lower())
55
59
 
56
60
  # Create a tool configuration dict with required fields
61
+ # Note: MCP toolkit always requires toolkit_name, other toolkits respect use_prefix flag
57
62
  tool_config = {
58
63
  'id': toolkit_config.get('id', random.randint(1, 1000000)),
59
64
  'type': toolkit_config.get('type', toolkit_type),
60
65
  'settings': settings,
61
- 'toolkit_name': toolkit_name
66
+ 'toolkit_name': toolkit_name if (use_prefix or toolkit_type == 'mcp') else None
62
67
  }
63
68
 
64
69
  # Get tools using the toolkit configuration with clients
@@ -76,21 +81,10 @@ def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
76
81
  # Re-raise McpAuthorizationRequired without logging as error
77
82
  from ..utils.mcp_oauth import McpAuthorizationRequired
78
83
 
79
- # Check if it's McpAuthorizationRequired directly
80
84
  if isinstance(e, McpAuthorizationRequired):
81
85
  logger.info(f"Toolkit {toolkit_name} requires MCP OAuth authorization")
82
86
  raise
83
87
 
84
- # Also check for wrapped exceptions
85
- if hasattr(e, '__cause__') and isinstance(e.__cause__, McpAuthorizationRequired):
86
- logger.info(f"Toolkit {toolkit_name} requires MCP OAuth authorization (wrapped)")
87
- raise e.__cause__
88
-
89
- # Check exception class name as fallback
90
- if e.__class__.__name__ == 'McpAuthorizationRequired':
91
- logger.info(f"Toolkit {toolkit_name} requires MCP OAuth authorization (by name)")
92
- raise
93
-
94
88
  # Log and re-raise other errors
95
89
  logger.error(f"Error instantiating toolkit {toolkit_name} with client: {str(e)}")
96
90
  raise
@@ -1,6 +1,8 @@
1
1
  import re
2
2
  from enum import Enum
3
3
 
4
+ # DEPRECATED: Tool names no longer use prefixes
5
+ # Kept for backward compatibility only
4
6
  TOOLKIT_SPLITTER = "___"
5
7
 
6
8
  class IndexerKeywords(Enum):
@@ -45,6 +45,9 @@ def _safe_import_tool(tool_name, module_path, get_tools_name=None, toolkit_class
45
45
  imported = {}
46
46
  if get_tools_name and hasattr(module, get_tools_name):
47
47
  imported['get_tools'] = getattr(module, get_tools_name)
48
+
49
+ if hasattr(module, 'get_toolkit'):
50
+ imported['get_toolkit'] = getattr(module, 'get_toolkit')
48
51
 
49
52
  if toolkit_class_name and hasattr(module, toolkit_class_name):
50
53
  imported['toolkit_class'] = getattr(module, toolkit_class_name)
@@ -209,6 +212,18 @@ def get_toolkits():
209
212
  logger.info(f"Successfully loaded {len(toolkit_configs)} toolkit configurations")
210
213
  return toolkit_configs
211
214
 
215
+ def instantiate_toolkit(tool_config):
216
+ """Instantiate a toolkit from its configuration."""
217
+ tool_type = tool_config.get('type')
218
+
219
+ if tool_type in AVAILABLE_TOOLS:
220
+ tool_module = AVAILABLE_TOOLS[tool_type]
221
+
222
+ if 'get_toolkit' in tool_module:
223
+ return tool_module['get_toolkit'](tool_config)
224
+
225
+ raise ValueError(f"Toolkit type '{tool_type}' does not support direct instantiation or is not available.")
226
+
212
227
  def get_available_tools():
213
228
  """Return list of available tool types."""
214
229
  return list(AVAILABLE_TOOLS.keys())
@@ -10,12 +10,12 @@ from ....configurations.ado import AdoReposConfiguration
10
10
  from ....configurations.pgvector import PgVectorConfiguration
11
11
  from ...base.tool import BaseAction
12
12
  from .repos_wrapper import ReposApiWrapper
13
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
13
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
14
14
 
15
15
  name = "ado_repos"
16
16
 
17
17
 
18
- def _get_toolkit(tool) -> BaseToolkit:
18
+ def get_toolkit(tool) -> BaseToolkit:
19
19
  return AzureDevOpsReposToolkit().get_toolkit(
20
20
  selected_tools=tool['settings'].get('selected_tools', []),
21
21
  ado_repos_configuration=tool['settings']['ado_repos_configuration'],
@@ -30,20 +30,15 @@ def _get_toolkit(tool) -> BaseToolkit:
30
30
  llm=tool['settings'].get('llm', None),
31
31
  )
32
32
 
33
- def get_toolkit():
34
- return AzureDevOpsReposToolkit.toolkit_config_schema()
35
-
36
33
  def get_tools(tool):
37
- return _get_toolkit(tool).get_tools()
34
+ return get_toolkit(tool).get_tools()
38
35
 
39
36
  class AzureDevOpsReposToolkit(BaseToolkit):
40
37
  tools: List[BaseTool] = []
41
- toolkit_max_length: int = 0
42
38
 
43
39
  @staticmethod
44
40
  def toolkit_config_schema() -> BaseModel:
45
41
  selected_tools = {x['name']: x['args_schema'].schema() for x in ReposApiWrapper.model_construct().get_available_tools()}
46
- AzureDevOpsReposToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
47
42
  m = create_model(
48
43
  name,
49
44
  ado_repos_configuration=(AdoReposConfiguration, Field(description="Ado Repos configuration", default=None,
@@ -63,7 +58,6 @@ class AzureDevOpsReposToolkit(BaseToolkit):
63
58
  "icon_url": "ado-repos-icon.svg",
64
59
  "categories": ["code repositories"],
65
60
  "extra_categories": ["code", "repository", "version control"],
66
- "max_length": AzureDevOpsReposToolkit.toolkit_max_length
67
61
  }}}
68
62
  )
69
63
 
@@ -99,17 +93,21 @@ class AzureDevOpsReposToolkit(BaseToolkit):
99
93
  azure_devops_repos_wrapper = ReposApiWrapper(**wrapper_payload)
100
94
  available_tools = azure_devops_repos_wrapper.get_available_tools()
101
95
  tools = []
102
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
103
96
  for tool in available_tools:
104
97
  if selected_tools:
105
98
  if tool["name"] not in selected_tools:
106
99
  continue
100
+ description = tool["description"] + f"\nADO instance: {azure_devops_repos_wrapper.organization_url}/{azure_devops_repos_wrapper.project}"
101
+ if toolkit_name:
102
+ description = f"{description}\nToolkit: {toolkit_name}"
103
+ description = description[:1000]
107
104
  tools.append(
108
105
  BaseAction(
109
106
  api_wrapper=azure_devops_repos_wrapper,
110
- name=prefix + tool["name"],
111
- description=tool["description"] + f"\nADO instance: {azure_devops_repos_wrapper.organization_url}/{azure_devops_repos_wrapper.project}",
107
+ name=tool["name"],
108
+ description=description,
112
109
  args_schema=tool["args_schema"],
110
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
113
111
  )
114
112
  )
115
113
  return cls(tools=tools)
@@ -10,21 +10,33 @@ from ....configurations.ado import AdoConfiguration
10
10
  from ....configurations.pgvector import PgVectorConfiguration
11
11
  from .test_plan_wrapper import TestPlanApiWrapper
12
12
  from ...base.tool import BaseAction
13
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
13
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
14
14
 
15
15
 
16
16
  name = "azure_devops_plans"
17
17
  name_alias = "ado_plans"
18
18
 
19
+ def get_toolkit(tool):
20
+ return AzureDevOpsPlansToolkit().get_toolkit(
21
+ selected_tools=tool['settings'].get('selected_tools', []),
22
+ ado_configuration=tool['settings']['ado_configuration'],
23
+ limit=tool['settings'].get('limit', 5),
24
+ toolkit_name=tool.get('toolkit_name', ''),
25
+ alita=tool['settings'].get('alita', None),
26
+ llm=tool['settings'].get('llm', None),
27
+ pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
28
+ collection_name=tool['toolkit_name'],
29
+ doctype='doc',
30
+ embedding_model=tool['settings'].get('embedding_model'),
31
+ vectorstore_type="PGVector"
32
+ )
19
33
 
20
34
  class AzureDevOpsPlansToolkit(BaseToolkit):
21
35
  tools: List[BaseTool] = []
22
- toolkit_max_length: int = 0
23
36
 
24
37
  @staticmethod
25
38
  def toolkit_config_schema() -> BaseModel:
26
39
  selected_tools = {x['name']: x['args_schema'].schema() for x in TestPlanApiWrapper.model_construct().get_available_tools()}
27
- AzureDevOpsPlansToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
28
40
  m = create_model(
29
41
  name_alias,
30
42
  ado_configuration=(AdoConfiguration, Field(description="Ado configuration", json_schema_extra={'configuration_types': ['ado']})),
@@ -39,7 +51,6 @@ class AzureDevOpsPlansToolkit(BaseToolkit):
39
51
  {
40
52
  "label": "ADO plans",
41
53
  "icon_url": "ado-plans.svg",
42
- "max_length": AzureDevOpsPlansToolkit.toolkit_max_length,
43
54
  "categories": ["test management"],
44
55
  "extra_categories": ["test case management", "qa"],
45
56
  "sections": {
@@ -97,17 +108,21 @@ class AzureDevOpsPlansToolkit(BaseToolkit):
97
108
  azure_devops_api_wrapper = TestPlanApiWrapper(**wrapper_payload)
98
109
  available_tools = azure_devops_api_wrapper.get_available_tools()
99
110
  tools = []
100
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
101
111
  for tool in available_tools:
102
112
  if selected_tools:
103
113
  if tool["name"] not in selected_tools:
104
114
  continue
105
115
  print(tool)
116
+ description = tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}"
117
+ if toolkit_name:
118
+ description = f"{description}\nToolkit: {toolkit_name}"
119
+ description = description[:1000]
106
120
  tools.append(BaseAction(
107
121
  api_wrapper=azure_devops_api_wrapper,
108
- name=prefix + tool["name"],
109
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}",
110
- args_schema=tool["args_schema"]
122
+ name=tool["name"],
123
+ description=description,
124
+ args_schema=tool["args_schema"],
125
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
111
126
  ))
112
127
  return cls(tools=tools)
113
128
 
@@ -9,19 +9,32 @@ from ...elitea_base import filter_missconfigured_index_tools
9
9
  from ....configurations.ado import AdoConfiguration
10
10
  from ....configurations.pgvector import PgVectorConfiguration
11
11
  from ...base.tool import BaseAction
12
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
12
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
13
13
 
14
14
  name = "azure_devops_wiki"
15
15
  name_alias = 'ado_wiki'
16
16
 
17
+ def get_toolkit(tool):
18
+ return AzureDevOpsWikiToolkit().get_toolkit(
19
+ selected_tools=tool['settings'].get('selected_tools', []),
20
+ ado_configuration=tool['settings']['ado_configuration'],
21
+ limit=tool['settings'].get('limit', 5),
22
+ toolkit_name=tool.get('toolkit_name', ''),
23
+ alita=tool['settings'].get('alita', None),
24
+ llm=tool['settings'].get('llm', None),
25
+ pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
26
+ collection_name=tool['toolkit_name'],
27
+ doctype='doc',
28
+ embedding_model=tool['settings'].get('embedding_model'),
29
+ vectorstore_type="PGVector"
30
+ )
31
+
17
32
  class AzureDevOpsWikiToolkit(BaseToolkit):
18
33
  tools: List[BaseTool] = []
19
- toolkit_max_length: int = 0
20
34
 
21
35
  @staticmethod
22
36
  def toolkit_config_schema() -> BaseModel:
23
37
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureDevOpsApiWrapper.model_construct().get_available_tools()}
24
- AzureDevOpsWikiToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
25
38
  m = create_model(
26
39
  name_alias,
27
40
  ado_configuration=(AdoConfiguration, Field(description="Ado configuration", json_schema_extra={'configuration_types': ['ado']})),
@@ -37,7 +50,6 @@ class AzureDevOpsWikiToolkit(BaseToolkit):
37
50
  'metadata': {
38
51
  "label": "ADO wiki",
39
52
  "icon_url": "ado-wiki-icon.svg",
40
- "max_length": AzureDevOpsWikiToolkit.toolkit_max_length,
41
53
  "categories": ["documentation"],
42
54
  "extra_categories": ["knowledge base", "documentation management", "wiki"],
43
55
  "sections": {
@@ -91,16 +103,20 @@ class AzureDevOpsWikiToolkit(BaseToolkit):
91
103
  azure_devops_api_wrapper = AzureDevOpsApiWrapper(**wrapper_payload)
92
104
  available_tools = azure_devops_api_wrapper.get_available_tools()
93
105
  tools = []
94
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
95
106
  for tool in available_tools:
96
107
  if selected_tools:
97
108
  if tool["name"] not in selected_tools:
98
109
  continue
110
+ description = tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}"
111
+ if toolkit_name:
112
+ description = f"{description}\nToolkit: {toolkit_name}"
113
+ description = description[:1000]
99
114
  tools.append(BaseAction(
100
115
  api_wrapper=azure_devops_api_wrapper,
101
- name=prefix + tool["name"],
102
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}",
103
- args_schema=tool["args_schema"]
116
+ name=tool["name"],
117
+ description=description,
118
+ args_schema=tool["args_schema"],
119
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
104
120
  ))
105
121
  return cls(tools=tools)
106
122
 
@@ -52,8 +52,9 @@ ModifyPageInput = create_model(
52
52
  wiki_identified=(str, Field(description="Wiki ID or wiki name")),
53
53
  page_name=(str, Field(description="Wiki page name")),
54
54
  page_content=(str, Field(description="Wiki page content")),
55
- version_identifier=(str, Field(description="Version string identifier (name of tag/branch, SHA1 of commit)")),
56
- version_type=(Optional[str], Field(description="Version type (branch, tag, or commit). Determines how Id is interpreted", default="branch"))
55
+ version_identifier=(str, Field(description="Version string identifier (name of tag/branch, SHA1 of commit). Usually for wiki the branch is 'wikiMaster'")),
56
+ version_type=(Optional[str], Field(description="Version type (branch, tag, or commit). Determines how Id is interpreted", default="branch")),
57
+ expanded=(Optional[bool], Field(description="Whether to return the full page object or just its simplified version.", default=False))
57
58
  )
58
59
 
59
60
  RenamePageInput = create_model(
@@ -66,6 +67,19 @@ RenamePageInput = create_model(
66
67
  )
67
68
 
68
69
 
70
+ def _format_wiki_page_response(wiki_page_response, expanded: bool = False):
71
+ """Format wiki page response."""
72
+ try:
73
+ return {
74
+ 'eTag': wiki_page_response.eTag,
75
+ 'page': wiki_page_response.page.__dict__ if wiki_page_response.page else None
76
+ } if expanded else {"eTag": wiki_page_response.eTag, "id": wiki_page_response.page.id,
77
+ "page": wiki_page_response.page.url}
78
+ except:
79
+ logger.error(f"Unable to format wiki page response: {wiki_page_response}")
80
+ return wiki_page_response
81
+
82
+
69
83
  class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
70
84
  # TODO use ado_configuration instead of organization_url, project and token
71
85
  organization_url: str
@@ -226,7 +240,7 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
226
240
  logger.error(f"Unable to rename wiki page: {str(e)}")
227
241
  return ToolException(f"Unable to rename wiki page: {str(e)}")
228
242
 
229
- def modify_wiki_page(self, wiki_identified: str, page_name: str, page_content: str, version_identifier: str, version_type: str = "branch"):
243
+ def modify_wiki_page(self, wiki_identified: str, page_name: str, page_content: str, version_identifier: str, version_type: str = "branch", expanded: Optional[bool] = False):
230
244
  """Create or Update ADO wiki page content."""
231
245
  try:
232
246
  all_wikis = [wiki.name for wiki in self._client.get_all_wikis(project=self.project)]
@@ -257,24 +271,24 @@ class AzureDevOpsApiWrapper(NonCodeIndexerToolkit):
257
271
  return ToolException(f"Unable to extract page by path {page_name}: {str(get_page_e)}")
258
272
 
259
273
  try:
260
- return self._client.create_or_update_page(
274
+ return _format_wiki_page_response(self._client.create_or_update_page(
261
275
  project=self.project,
262
276
  wiki_identifier=wiki_identified,
263
277
  path=page_name,
264
278
  parameters=WikiPageCreateOrUpdateParameters(content=page_content),
265
279
  version=version,
266
280
  version_descriptor=GitVersionDescriptor(version=version_identifier, version_type=version_type)
267
- )
281
+ ), expanded=expanded)
268
282
  except AzureDevOpsServiceError as e:
269
283
  if "The version '{0}' either is invalid or does not exist." in str(e):
270
284
  # Retry the request without version_descriptor
271
- return self._client.create_or_update_page(
285
+ return _format_wiki_page_response(wiki_page_response=self._client.create_or_update_page(
272
286
  project=self.project,
273
287
  wiki_identifier=wiki_identified,
274
288
  path=page_name,
275
289
  parameters=WikiPageCreateOrUpdateParameters(content=page_content),
276
290
  version=version
277
- )
291
+ ), expanded=expanded)
278
292
  else:
279
293
  raise
280
294
  except Exception as e:
@@ -9,18 +9,31 @@ from ...elitea_base import filter_missconfigured_index_tools
9
9
  from ....configurations.ado import AdoConfiguration
10
10
  from ....configurations.pgvector import PgVectorConfiguration
11
11
  from ...base.tool import BaseAction
12
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, check_connection_response
12
+ from ...utils import clean_string, get_max_toolkit_length, check_connection_response
13
13
 
14
14
  name = "ado_boards"
15
15
 
16
+ def get_toolkit(tool):
17
+ return AzureDevOpsWorkItemsToolkit().get_toolkit(
18
+ selected_tools=tool['settings'].get('selected_tools', []),
19
+ ado_configuration=tool['settings']['ado_configuration'],
20
+ limit=tool['settings'].get('limit', 5),
21
+ toolkit_name=tool.get('toolkit_name', ''),
22
+ alita=tool['settings'].get('alita', None),
23
+ llm=tool['settings'].get('llm', None),
24
+ pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
25
+ collection_name=tool['toolkit_name'],
26
+ doctype='doc',
27
+ embedding_model=tool['settings'].get('embedding_model'),
28
+ vectorstore_type="PGVector"
29
+ )
30
+
16
31
  class AzureDevOpsWorkItemsToolkit(BaseToolkit):
17
32
  tools: List[BaseTool] = []
18
- toolkit_max_length: int = 0
19
33
 
20
34
  @staticmethod
21
35
  def toolkit_config_schema() -> BaseModel:
22
36
  selected_tools = {x['name']: x['args_schema'].schema() for x in AzureDevOpsApiWrapper.model_construct().get_available_tools()}
23
- AzureDevOpsWorkItemsToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
24
37
  m = create_model(
25
38
  name,
26
39
  ado_configuration=(AdoConfiguration, Field(description="Ado Work Item configuration", json_schema_extra={'configuration_types': ['ado']})),
@@ -37,7 +50,6 @@ class AzureDevOpsWorkItemsToolkit(BaseToolkit):
37
50
  'metadata': {
38
51
  "label": "ADO boards",
39
52
  "icon_url": "ado-boards-icon.svg",
40
- "max_length": AzureDevOpsWorkItemsToolkit.toolkit_max_length,
41
53
  "categories": ["project management"],
42
54
  "extra_categories": ["work item management", "issue tracking", "agile boards"],
43
55
  "sections": {
@@ -92,16 +104,20 @@ class AzureDevOpsWorkItemsToolkit(BaseToolkit):
92
104
  azure_devops_api_wrapper = AzureDevOpsApiWrapper(**wrapper_payload)
93
105
  available_tools = azure_devops_api_wrapper.get_available_tools()
94
106
  tools = []
95
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
96
107
  for tool in available_tools:
97
108
  if selected_tools:
98
109
  if tool["name"] not in selected_tools:
99
110
  continue
111
+ description = tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}"
112
+ if toolkit_name:
113
+ description = f"{description}\nToolkit: {toolkit_name}"
114
+ description = description[:1000]
100
115
  tools.append(BaseAction(
101
116
  api_wrapper=azure_devops_api_wrapper,
102
- name=prefix + tool["name"],
103
- description=tool["description"] + f"\nADO instance: {azure_devops_api_wrapper.organization_url}/{azure_devops_api_wrapper.project}",
104
- args_schema=tool["args_schema"]
117
+ name=tool["name"],
118
+ description=description,
119
+ args_schema=tool["args_schema"],
120
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
105
121
  ))
106
122
  return cls(tools=tools)
107
123