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
@@ -6,7 +6,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
6
6
  from .api_wrapper import KubernetesApiWrapper
7
7
  from ...base.tool import BaseAction
8
8
  from ...elitea_base import filter_missconfigured_index_tools
9
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
9
+ from ...utils import clean_string, get_max_toolkit_length
10
10
 
11
11
  name = "kubernetes"
12
12
 
@@ -21,12 +21,10 @@ def get_tools(tool):
21
21
 
22
22
  class KubernetesToolkit(BaseToolkit):
23
23
  tools: list[BaseTool] = []
24
- toolkit_max_length: int = 0
25
24
 
26
25
  @staticmethod
27
26
  def toolkit_config_schema() -> BaseModel:
28
27
  selected_tools = {x['name']: x['args_schema'].schema() for x in KubernetesApiWrapper.model_construct().get_available_tools()}
29
- KubernetesToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
30
28
  return create_model(
31
29
  name,
32
30
  url=(str, Field(default="", title="Cluster URL", description="The URL of the Kubernetes cluster")),
@@ -51,15 +49,19 @@ class KubernetesToolkit(BaseToolkit):
51
49
  kubernetes_api_wrapper = KubernetesApiWrapper(**kwargs)
52
50
  available_tools = kubernetes_api_wrapper.get_available_tools()
53
51
  tools = []
54
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
55
52
  for tool in available_tools:
56
53
  if selected_tools and tool["name"] not in selected_tools:
57
54
  continue
55
+ description = tool["description"]
56
+ if toolkit_name:
57
+ description = f"Toolkit: {toolkit_name}\n{description}"
58
+ description = description[:1000]
58
59
  tools.append(BaseAction(
59
60
  api_wrapper=kubernetes_api_wrapper,
60
- name=prefix + tool["name"],
61
- description=tool["description"],
62
- args_schema=tool["args_schema"]
61
+ name=tool["name"],
62
+ description=description,
63
+ args_schema=tool["args_schema"],
64
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
63
65
  ))
64
66
  return cls(tools=tools)
65
67
 
@@ -5,7 +5,7 @@ from pydantic import BaseModel, create_model, Field
5
5
 
6
6
  from .api_wrapper import PythonLinter
7
7
  from ...base.tool import BaseAction
8
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
8
+ from ...utils import clean_string, get_max_toolkit_length
9
9
 
10
10
  name = "python_linter"
11
11
 
@@ -19,11 +19,9 @@ def get_tools(tool):
19
19
 
20
20
  class PythonLinterToolkit(BaseToolkit):
21
21
  tools: list[BaseTool] = []
22
- toolkit_max_length: int = 0
23
22
 
24
23
  @staticmethod
25
24
  def toolkit_config_schema() -> BaseModel:
26
- PythonLinterToolkit.toolkit_max_length = get_max_toolkit_length([])
27
25
  return create_model(
28
26
  name,
29
27
  error_codes=(str, Field(description="Error codes to be used by the linter")),
@@ -39,16 +37,19 @@ class PythonLinterToolkit(BaseToolkit):
39
37
  python_linter = PythonLinter(**kwargs)
40
38
  available_tools = python_linter.get_available_tools()
41
39
  tools = []
42
- toolkit_max_length = get_max_toolkit_length(selected_tools)
43
- prefix = clean_string(toolkit_name, PythonLinterToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
44
40
  for tool in available_tools:
45
41
  if selected_tools and tool["name"] not in selected_tools:
46
42
  continue
43
+ description = tool["description"]
44
+ if toolkit_name:
45
+ description = f"Toolkit: {toolkit_name}\n{description}"
46
+ description = description[:1000]
47
47
  tools.append(BaseAction(
48
48
  api_wrapper=python_linter,
49
- name=prefix + tool["name"],
50
- description=tool["description"],
51
- args_schema=tool["args_schema"]
49
+ name=tool["name"],
50
+ description=description,
51
+ args_schema=tool["args_schema"],
52
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
52
53
  ))
53
54
  return cls(tools=tools)
54
55
 
@@ -4,8 +4,9 @@ def search_format(items):
4
4
  results = []
5
5
  for (doc, score) in items:
6
6
  res_chunk = ''
7
- language = get_programming_language(get_file_extension(doc.metadata["filename"]))
8
- res_chunk += doc.metadata["filename"] + " -> " + doc.metadata["method_name"] + " (score: " + str(score) + ")"
7
+ language = get_programming_language(get_file_extension(doc.metadata.get("filename", "unknown")))
8
+ method_name = doc.metadata.get("method_name", "text")
9
+ res_chunk += doc.metadata.get("filename", "unknown") + " -> " + method_name + " (score: " + str(score) + ")"
9
10
  res_chunk += "\n\n```" + language.value + "\n"+ doc.page_content + "\n```\n\n"
10
11
  results.append(res_chunk)
11
12
  return results
@@ -5,7 +5,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field
5
5
  from .api_wrapper import SonarApiWrapper
6
6
  from ...base.tool import BaseAction
7
7
  from ...elitea_base import filter_missconfigured_index_tools
8
- from ...utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
8
+ from ...utils import clean_string, get_max_toolkit_length
9
9
  from ....configurations.sonar import SonarConfiguration
10
10
 
11
11
  name = "sonar"
@@ -21,12 +21,10 @@ def get_tools(tool):
21
21
 
22
22
  class SonarToolkit(BaseToolkit):
23
23
  tools: list[BaseTool] = []
24
- toolkit_max_length: int = 0
25
24
 
26
25
  @staticmethod
27
26
  def toolkit_config_schema() -> BaseModel:
28
27
  selected_tools = {x['name']: x['args_schema'].schema() for x in SonarApiWrapper.model_construct().get_available_tools()}
29
- SonarToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
30
28
  return create_model(
31
29
  name,
32
30
  sonar_project_name=(str, Field(description="Project name of the desired repository")),
@@ -55,15 +53,19 @@ class SonarToolkit(BaseToolkit):
55
53
  sonar_api_wrapper = SonarApiWrapper(**wrapper_payload)
56
54
  available_tools = sonar_api_wrapper.get_available_tools()
57
55
  tools = []
58
- prefix = clean_string(toolkit_name, SonarToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
59
56
  for tool in available_tools:
60
57
  if selected_tools and tool["name"] not in selected_tools:
61
58
  continue
59
+ description = tool["description"]
60
+ if toolkit_name:
61
+ description = f"Toolkit: {toolkit_name}\n{description}"
62
+ description = description[:1000]
62
63
  tools.append(BaseAction(
63
64
  api_wrapper=sonar_api_wrapper,
64
- name=prefix + tool["name"],
65
- description=tool["description"],
66
- args_schema=tool["args_schema"]
65
+ name=tool["name"],
66
+ description=description,
67
+ args_schema=tool["args_schema"],
68
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
67
69
  ))
68
70
  return cls(tools=tools)
69
71
 
@@ -6,14 +6,14 @@ from ..base.tool import BaseAction
6
6
  from pydantic import create_model, BaseModel, ConfigDict, Field
7
7
 
8
8
  from ..elitea_base import filter_missconfigured_index_tools
9
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length, parse_list, check_connection_response
9
+ from ..utils import clean_string, get_max_toolkit_length, parse_list, check_connection_response
10
10
  from ...configurations.confluence import ConfluenceConfiguration
11
11
  from ...configurations.pgvector import PgVectorConfiguration
12
12
  import requests
13
13
 
14
14
  name = "confluence"
15
15
 
16
- def get_tools(tool):
16
+ def get_toolkit(tool):
17
17
  return ConfluenceToolkit().get_toolkit(
18
18
  selected_tools=tool['settings'].get('selected_tools', []),
19
19
  space=tool['settings'].get('space', None),
@@ -33,18 +33,19 @@ def get_tools(tool):
33
33
  doctype='doc',
34
34
  embedding_model=tool['settings'].get('embedding_model'),
35
35
  vectorstore_type="PGVector"
36
- ).get_tools()
36
+ )
37
+
38
+ def get_tools(tool):
39
+ return get_toolkit(tool).get_tools()
37
40
 
38
41
 
39
42
  class ConfluenceToolkit(BaseToolkit):
40
43
  tools: List[BaseTool] = []
41
- toolkit_max_length: int = 0
42
44
 
43
45
  @staticmethod
44
46
  def toolkit_config_schema() -> BaseModel:
45
47
  selected_tools = {x['name']: x['args_schema'].schema() for x in
46
48
  ConfluenceAPIWrapper.model_construct().get_available_tools()}
47
- ConfluenceToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
48
49
 
49
50
  @check_connection_response
50
51
  def check_connection(self):
@@ -94,7 +95,6 @@ class ConfluenceToolkit(BaseToolkit):
94
95
  'metadata': {
95
96
  "label": "Confluence",
96
97
  "icon_url": None,
97
- "max_length": ConfluenceToolkit.toolkit_max_length,
98
98
  "categories": ["documentation"],
99
99
  "extra_categories": ["confluence", "wiki", "knowledge base", "documentation", "atlassian"]
100
100
  }
@@ -115,18 +115,23 @@ class ConfluenceToolkit(BaseToolkit):
115
115
  **(kwargs.get('pgvector_configuration') or {}),
116
116
  }
117
117
  confluence_api_wrapper = ConfluenceAPIWrapper(**wrapper_payload)
118
- prefix = clean_string(toolkit_name, ConfluenceToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
119
118
  available_tools = confluence_api_wrapper.get_available_tools()
120
119
  tools = []
121
120
  for tool in available_tools:
122
121
  if selected_tools:
123
122
  if tool["name"] not in selected_tools:
124
123
  continue
124
+ description = tool["description"]
125
+ if toolkit_name:
126
+ description = f"Toolkit: {toolkit_name}\n{description}"
127
+ description = f"Confluence space: {confluence_api_wrapper.space}\n{description}"
128
+ description = description[:1000]
125
129
  tools.append(BaseAction(
126
130
  api_wrapper=confluence_api_wrapper,
127
- name=prefix + tool["name"],
128
- description=f"Confluence space: {confluence_api_wrapper.space}" + tool["description"],
129
- args_schema=tool["args_schema"]
131
+ name=tool["name"],
132
+ description=description,
133
+ args_schema=tool["args_schema"],
134
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
130
135
  ))
131
136
  return cls(tools=tools)
132
137
 
@@ -480,21 +480,69 @@ class ConfluenceAPIWrapper(NonCodeIndexerToolkit):
480
480
  """Gets pages with specific label in the Confluence space."""
481
481
 
482
482
  start = 0
483
- pages_info = []
484
- for _ in range((self.max_pages + self.limit - 1) // self.limit):
485
- pages = self.client.get_all_pages_by_label(label, start=start,
486
- limit=self.limit) # , expand="body.view.value"
483
+ pages_info: List[Dict[str, Any]] = []
484
+ seen_ids: set[str] = set()
485
+
486
+ # Use a while-loop driven by unique pages collected and
487
+ # presence of additional results instead of a fixed number
488
+ # of iterations based purely on max_pages/limit.
489
+ while len(pages_info) < (self.max_pages or 0):
490
+ pages = self.client.get_all_pages_by_label(
491
+ label,
492
+ start=start,
493
+ limit=self.limit,
494
+ ) # , expand="body.view.value"
487
495
  if not pages:
488
496
  break
489
497
 
490
- pages_info += [{
491
- 'page_id': page.metadata['id'],
492
- 'page_title': page.metadata['title'],
493
- 'page_url': page.metadata['source'],
494
- 'content': page.page_content
495
- } for page in self.get_pages_by_id([page["id"] for page in pages])]
498
+ # Collect only ids we haven't processed yet to avoid
499
+ # calling get_page_by_id multiple times for the same
500
+ # Confluence page.
501
+ new_ids: List[str] = []
502
+ for p in pages:
503
+ page_id = p["id"] if isinstance(p, dict) else getattr(p, "id", None)
504
+ if page_id is None:
505
+ continue
506
+ if page_id in seen_ids:
507
+ continue
508
+ seen_ids.add(page_id)
509
+ new_ids.append(page_id)
510
+
511
+ if new_ids:
512
+ for page in self.get_pages_by_id(new_ids):
513
+ meta = getattr(page, "metadata", {}) or {}
514
+ page_id = meta.get("id")
515
+ page_title = meta.get("title")
516
+ page_url = meta.get("source")
517
+ content = getattr(page, "page_content", None)
518
+
519
+ if page_id is None:
520
+ continue
521
+
522
+ pages_info.append(
523
+ {
524
+ "page_id": page_id,
525
+ "page_title": page_title,
526
+ "page_url": page_url,
527
+ "content": content,
528
+ }
529
+ )
530
+
531
+ # Respect max_pages on unique pages collected.
532
+ if len(pages_info) >= (self.max_pages or 0):
533
+ break
534
+
535
+ # Advance the offset by the requested page size.
496
536
  start += self.limit
497
- return pages_info
537
+
538
+ # Defensive break: if the API returns fewer items than
539
+ # requested, there are likely no more pages to fetch.
540
+ if len(pages) < self.limit:
541
+ break
542
+
543
+ # Slice as an extra safety net in case of any race conditions
544
+ # around the max_pages guard in the loop above.
545
+ return pages_info[: (self.max_pages or len(pages_info))]
498
546
 
499
547
  def is_public_page(self, page: dict) -> bool:
500
548
  """Check if a page is publicly accessible."""
@@ -896,14 +944,14 @@ class ConfluenceAPIWrapper(NonCodeIndexerToolkit):
896
944
 
897
945
  # Re-verify extension filters
898
946
  # Check if file should be skipped based on skip_extensions
899
- if any(re.match(pattern.replace('*', '.*') + '$', title, re.IGNORECASE)
947
+ if any(re.match(re.escape(pattern).replace(r'\*', '.*') + '$', title, re.IGNORECASE)
900
948
  for pattern in self._skip_extensions):
901
949
  continue
902
950
 
903
951
  # Check if file should be included based on include_extensions
904
952
  # If include_extensions is empty, process all files (that weren't skipped)
905
953
  if self._include_extensions and not (
906
- any(re.match(pattern.replace('*', '.*') + '$', title, re.IGNORECASE)
954
+ any(re.match(re.escape(pattern).replace(r'\*', '.*') + '$', title, re.IGNORECASE)
907
955
  for pattern in self._include_extensions)):
908
956
  continue
909
957
 
@@ -1820,4 +1868,5 @@ class ConfluenceAPIWrapper(NonCodeIndexerToolkit):
1820
1868
  "description": self.get_page_attachments.__doc__,
1821
1869
  "args_schema": GetPageAttachmentsInput,
1822
1870
  }
1823
- ]
1871
+ ]
1872
+
@@ -5,7 +5,7 @@ from pydantic import create_model, BaseModel, ConfigDict, Field
5
5
 
6
6
  from .api_wrapper import OpenApiWrapper
7
7
  from ..base.tool import BaseAction
8
- from ..utils import clean_string, TOOLKIT_SPLITTER
8
+ from ..utils import clean_string
9
9
 
10
10
  name = "openapi"
11
11
 
@@ -43,15 +43,21 @@ class OpenApiToolkit(BaseToolkit):
43
43
  openapi_api_wrapper = OpenApiWrapper(**kwargs)
44
44
  available_tools = openapi_api_wrapper.get_available_tools()
45
45
  tools = []
46
- prefix = clean_string(toolkit_name + TOOLKIT_SPLITTER) if toolkit_name else ''
46
+ # Use clean toolkit name for context (max 1000 chars in description)
47
+ toolkit_context = f" [Toolkit: {clean_string(toolkit_name)}]" if toolkit_name else ''
47
48
  for tool in available_tools:
48
49
  if selected_tools and tool["name"] not in selected_tools:
49
50
  continue
51
+ # Add toolkit context to description with character limit
52
+ description = tool["description"]
53
+ if toolkit_context and len(description + toolkit_context) <= 1000:
54
+ description = description + toolkit_context
50
55
  tools.append(BaseAction(
51
56
  api_wrapper=openapi_api_wrapper,
52
- name=prefix + tool["name"],
53
- description=tool["description"],
54
- args_schema=tool["args_schema"]
57
+ name=tool["name"],
58
+ description=description,
59
+ args_schema=tool["args_schema"],
60
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
55
61
  ))
56
62
  return cls(tools=tools)
57
63
 
@@ -5,7 +5,7 @@ from pydantic import BaseModel, ConfigDict, create_model, Field, SecretStr
5
5
 
6
6
  from .api_wrapper import ELITEAElasticApiWrapper
7
7
  from ..base.tool import BaseAction
8
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
8
+ from ..utils import clean_string, get_max_toolkit_length
9
9
 
10
10
  name = "elastic"
11
11
 
@@ -19,15 +19,13 @@ def get_tools(tool):
19
19
 
20
20
  class ElasticToolkit(BaseToolkit):
21
21
  tools: list[BaseTool] = []
22
- toolkit_max_length: int = 0
23
22
 
24
23
  @staticmethod
25
24
  def toolkit_config_schema() -> BaseModel:
26
25
  selected_tools = {x['name']: x['args_schema'].schema() for x in ELITEAElasticApiWrapper.model_construct().get_available_tools()}
27
- ElasticToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
28
26
  return create_model(
29
27
  name,
30
- url=(str, Field(default=None, title="Elasticsearch URL", description="Elasticsearch URL", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': ElasticToolkit.toolkit_max_length})),
28
+ url=(str, Field(default=None, title="Elasticsearch URL", description="Elasticsearch URL", json_schema_extra={'toolkit_name': True})),
31
29
  api_key=(
32
30
  Optional[SecretStr],
33
31
  Field(
@@ -48,15 +46,19 @@ class ElasticToolkit(BaseToolkit):
48
46
  elastic_api_wrapper = ELITEAElasticApiWrapper(**kwargs)
49
47
  available_tools = elastic_api_wrapper.get_available_tools()
50
48
  tools = []
51
- prefix = clean_string(toolkit_name, ElasticToolkit.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
52
49
  for tool in available_tools:
53
50
  if selected_tools and tool["name"] not in selected_tools:
54
51
  continue
52
+ description = tool["description"]
53
+ if toolkit_name:
54
+ description = f"Toolkit: {toolkit_name}\n{description}"
55
+ description = description[:1000]
55
56
  tools.append(BaseAction(
56
57
  api_wrapper=elastic_api_wrapper,
57
- name=prefix + tool["name"],
58
- description=tool["description"],
59
- args_schema=tool["args_schema"]
58
+ name=tool["name"],
59
+ description=description,
60
+ args_schema=tool["args_schema"],
61
+ metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
60
62
  ))
61
63
  return cls(tools=tools)
62
64