alita-sdk 0.3.150__py3-none-any.whl → 0.3.151__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
alita_sdk/__init__.py CHANGED
@@ -9,13 +9,13 @@ This package contains three main modules:
9
9
 
10
10
  __version__ = "0.3.142"
11
11
 
12
- # Import key components
13
- from .runtime import *
14
- from .tools import *
15
- from .community import *
12
+ __all__ = ["runtime", "tools", "community"]
16
13
 
17
- __all__ = [
18
- "runtime",
19
- "tools",
20
- "community",
21
- ]
14
+ import importlib
15
+
16
+ def __getattr__(name):
17
+ if name in __all__:
18
+ module = importlib.import_module(f".{name}", __name__)
19
+ globals()[name] = module
20
+ return module
21
+ raise AttributeError(f"module {__name__} has no attribute {name}")
@@ -7,23 +7,27 @@ Includes agents, clients, language models, and utilities.
7
7
 
8
8
  import importlib
9
9
 
10
- # Import available runtime modules
11
- __all__ = []
10
+ _modules = [
11
+ "agents",
12
+ "clients",
13
+ "langchain",
14
+ "llamaindex",
15
+ "llms",
16
+ "toolkits",
17
+ "tools",
18
+ "utils",
19
+ ]
12
20
 
13
- # Standard imports with fallback
14
- _modules = ['agents', 'clients', 'langchain', 'llamaindex', 'llms', 'toolkits', 'tools', 'utils']
21
+ __all__ = _modules + ["get_tools", "get_toolkits"]
15
22
 
16
- for module_name in _modules:
17
- try:
18
- module = importlib.import_module(f'.{module_name}', package=__name__)
19
- globals()[module_name] = module
20
- __all__.append(module_name)
21
- except ImportError:
22
- pass
23
-
24
- # Always try to export core functions from toolkits
25
- try:
26
- from .toolkits.tools import get_tools, get_toolkits
27
- __all__.extend(["get_tools", "get_toolkits"])
28
- except ImportError:
29
- pass
23
+ def __getattr__(name):
24
+ if name in _modules:
25
+ module = importlib.import_module(f".{name}", __name__)
26
+ globals()[name] = module
27
+ return module
28
+ if name in {"get_tools", "get_toolkits"}:
29
+ toolkits = importlib.import_module(".toolkits.tools", __name__)
30
+ value = getattr(toolkits, name)
31
+ globals()[name] = value
32
+ return value
33
+ raise AttributeError(f"module {__name__} has no attribute {name}")
@@ -1 +1,12 @@
1
- from .client import AlitaClient
1
+ """
2
+ Runtime clients package.
3
+ """
4
+
5
+ try:
6
+ from .client import AlitaClient
7
+ __all__ = ['AlitaClient']
8
+ except ImportError as e:
9
+ # Handle case where dependencies are not available
10
+ import logging
11
+ logging.getLogger(__name__).debug(f"Failed to import AlitaClient: {e}")
12
+ __all__ = []
@@ -30,7 +30,11 @@ from langchain_core.language_models import BaseChatModel # pylint: disable=E040
30
30
  from langchain_core.messages import AIMessage, AIMessageChunk # pylint: disable=E0401
31
31
  from langchain_core.outputs import ChatGeneration, ChatGenerationChunk, ChatResult # pylint: disable=E0401
32
32
 
33
- from ..langchain.tools import log
33
+ try:
34
+ from ..langchain.tools import log
35
+ except ImportError:
36
+ import logging as _logging
37
+ log = _logging.getLogger(__name__)
34
38
 
35
39
 
36
40
  class PreloadedEmbeddings(Embeddings):
@@ -1,7 +1,12 @@
1
1
  import logging
2
2
  from functools import wraps
3
3
 
4
- from langchain_core.callbacks import dispatch_custom_event
4
+ try:
5
+ from langchain_core.callbacks import dispatch_custom_event
6
+ except ImportError:
7
+ # Fallback stub if langchain_core is unavailable
8
+ def dispatch_custom_event(name: str, data: dict): # pragma: no cover
9
+ pass
5
10
 
6
11
 
7
12
  class StreamlitCallbackHandler(logging.Handler):
@@ -42,6 +47,7 @@ def setup_streamlit_logging(
42
47
  formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(message)s")
43
48
  handler.setFormatter(formatter)
44
49
  logger.addHandler(handler)
50
+ logger.setLevel(logging.INFO)
45
51
 
46
52
  return handler
47
53
 
@@ -6,13 +6,16 @@ from typing import Any, Dict, Optional
6
6
  from langchain_core.tools import ToolException
7
7
  import pandas as pd
8
8
 
9
- from ..tools.artifact import ArtifactWrapper
9
+ from typing import TYPE_CHECKING
10
+
11
+ if TYPE_CHECKING: # pragma: no cover - avoid heavy imports at runtime
12
+ from ..tools.artifact import ArtifactWrapper
10
13
 
11
14
  logger = logging.getLogger(__name__)
12
15
 
13
16
 
14
17
  def save_dataframe_to_artifact(
15
- artifacts_wrapper: ArtifactWrapper,
18
+ artifacts_wrapper: 'ArtifactWrapper',
16
19
  df: pd.DataFrame,
17
20
  target_file: str,
18
21
  csv_options: Optional[Dict[str, Any]] = None,
@@ -1,178 +1,205 @@
1
1
  import logging
2
2
  from importlib import import_module
3
3
 
4
- from .github import get_tools as get_github, AlitaGitHubToolkit
5
- from .openapi import get_tools as get_openapi
6
- from .jira import get_tools as get_jira, JiraToolkit
7
- from .confluence import get_tools as get_confluence, ConfluenceToolkit
8
- from .servicenow import get_tools as get_service_now, ServiceNowToolkit
9
- from .gitlab import get_tools as get_gitlab, AlitaGitlabToolkit
10
- from .gitlab_org import get_tools as get_gitlab_org, AlitaGitlabSpaceToolkit
11
- from .zephyr import get_tools as get_zephyr, ZephyrToolkit
12
- from .browser import get_tools as get_browser, BrowserToolkit
13
- from .report_portal import get_tools as get_report_portal, ReportPortalToolkit
14
- from .bitbucket import get_tools as get_bitbucket, AlitaBitbucketToolkit
15
- from .testrail import get_tools as get_testrail, TestrailToolkit
16
- from .testio import get_tools as get_testio, TestIOToolkit
17
- from .xray import get_tools as get_xray_cloud, XrayToolkit
18
- from .sharepoint import get_tools as get_sharepoint, SharepointToolkit
19
- from .qtest import get_tools as get_qtest, QtestToolkit
20
- from .zephyr_scale import get_tools as get_zephyr_scale, ZephyrScaleToolkit
21
- from .zephyr_enterprise import get_tools as get_zephyr_enterprise, ZephyrEnterpriseToolkit
22
- from .ado import get_tools as get_ado
23
- from .ado.repos import get_tools as get_ado_repo, AzureDevOpsReposToolkit
24
- from .ado.test_plan import AzureDevOpsPlansToolkit
25
- from .ado.work_item import AzureDevOpsWorkItemsToolkit
26
- from .ado.wiki import AzureDevOpsWikiToolkit
27
- from .rally import get_tools as get_rally, RallyToolkit
28
- from .sql import get_tools as get_sql, SQLToolkit
29
- from .code.sonar import get_tools as get_sonar, SonarToolkit
30
- from .google_places import get_tools as get_google_places, GooglePlacesToolkit
31
- from .yagmail import get_tools as get_yagmail, AlitaYagmailToolkit
32
- from .cloud.aws import AWSToolkit
33
- from .cloud.azure import AzureToolkit
34
- from .cloud.gcp import GCPToolkit
35
- from .cloud.k8s import KubernetesToolkit
36
- from .custom_open_api import OpenApiToolkit as CustomOpenApiToolkit
37
- from .elastic import ElasticToolkit
38
- from .keycloak import KeycloakToolkit
39
- from .localgit import AlitaLocalGitToolkit
40
- from .pandas import get_tools as get_pandas, PandasToolkit
41
- from .azure_ai.search import AzureSearchToolkit, get_tools as get_azure_search
42
- from .figma import get_tools as get_figma, FigmaToolkit
43
- from .salesforce import get_tools as get_salesforce, SalesforceToolkit
44
- from .carrier import get_tools as get_carrier, AlitaCarrierToolkit
45
- from .ocr import get_tools as get_ocr, OCRToolkit
46
- from .pptx import get_tools as get_pptx, PPTXToolkit
47
-
48
4
  logger = logging.getLogger(__name__)
49
5
 
50
- def get_tools(tools_list, alita: 'AlitaClient', llm: 'LLMLikeObject', *args, **kwargs):
6
+ # Available tools and toolkits - populated by safe imports
7
+ AVAILABLE_TOOLS = {}
8
+ AVAILABLE_TOOLKITS = {}
9
+ FAILED_IMPORTS = {}
10
+
11
+ def _safe_import_tool(tool_name, module_path, get_tools_name=None, toolkit_class_name=None):
12
+ """Safely import a tool module and register available functions/classes."""
13
+ try:
14
+ module = __import__(f'alita_sdk.tools.{module_path}', fromlist=[''])
15
+
16
+ imported = {}
17
+ if get_tools_name and hasattr(module, get_tools_name):
18
+ imported['get_tools'] = getattr(module, get_tools_name)
19
+
20
+ if toolkit_class_name and hasattr(module, toolkit_class_name):
21
+ imported['toolkit_class'] = getattr(module, toolkit_class_name)
22
+ AVAILABLE_TOOLKITS[toolkit_class_name] = getattr(module, toolkit_class_name)
23
+
24
+ if imported:
25
+ AVAILABLE_TOOLS[tool_name] = imported
26
+ logger.debug(f"Successfully imported {tool_name}")
27
+
28
+ except Exception as e:
29
+ FAILED_IMPORTS[tool_name] = str(e)
30
+ logger.debug(f"Failed to import {tool_name}: {e}")
31
+
32
+ # Safe imports for all tools
33
+ _safe_import_tool('github', 'github', 'get_tools', 'AlitaGitHubToolkit')
34
+ _safe_import_tool('openapi', 'openapi', 'get_tools')
35
+ _safe_import_tool('jira', 'jira', 'get_tools', 'JiraToolkit')
36
+ _safe_import_tool('confluence', 'confluence', 'get_tools', 'ConfluenceToolkit')
37
+ _safe_import_tool('service_now', 'servicenow', 'get_tools', 'ServiceNowToolkit')
38
+ _safe_import_tool('gitlab', 'gitlab', 'get_tools', 'AlitaGitlabToolkit')
39
+ _safe_import_tool('gitlab_org', 'gitlab_org', 'get_tools', 'AlitaGitlabSpaceToolkit')
40
+ _safe_import_tool('zephyr', 'zephyr', 'get_tools', 'ZephyrToolkit')
41
+ _safe_import_tool('browser', 'browser', 'get_tools', 'BrowserToolkit')
42
+ _safe_import_tool('report_portal', 'report_portal', 'get_tools', 'ReportPortalToolkit')
43
+ _safe_import_tool('bitbucket', 'bitbucket', 'get_tools', 'AlitaBitbucketToolkit')
44
+ _safe_import_tool('testrail', 'testrail', 'get_tools', 'TestrailToolkit')
45
+ _safe_import_tool('testio', 'testio', 'get_tools', 'TestIOToolkit')
46
+ _safe_import_tool('xray_cloud', 'xray', 'get_tools', 'XrayToolkit')
47
+ _safe_import_tool('sharepoint', 'sharepoint', 'get_tools', 'SharepointToolkit')
48
+ _safe_import_tool('qtest', 'qtest', 'get_tools', 'QtestToolkit')
49
+ _safe_import_tool('zephyr_scale', 'zephyr_scale', 'get_tools', 'ZephyrScaleToolkit')
50
+ _safe_import_tool('zephyr_enterprise', 'zephyr_enterprise', 'get_tools', 'ZephyrEnterpriseToolkit')
51
+ _safe_import_tool('ado', 'ado', 'get_tools')
52
+ _safe_import_tool('ado_repos', 'ado.repos', 'get_tools', 'AzureDevOpsReposToolkit')
53
+ _safe_import_tool('ado_plans', 'ado.test_plan', None, 'AzureDevOpsPlansToolkit')
54
+ _safe_import_tool('ado_work_items', 'ado.work_item', None, 'AzureDevOpsWorkItemsToolkit')
55
+ _safe_import_tool('ado_wiki', 'ado.wiki', None, 'AzureDevOpsWikiToolkit')
56
+ _safe_import_tool('rally', 'rally', 'get_tools', 'RallyToolkit')
57
+ _safe_import_tool('sql', 'sql', 'get_tools', 'SQLToolkit')
58
+ _safe_import_tool('sonar', 'code.sonar', 'get_tools', 'SonarToolkit')
59
+ _safe_import_tool('google_places', 'google_places', 'get_tools', 'GooglePlacesToolkit')
60
+ _safe_import_tool('yagmail', 'yagmail', 'get_tools', 'AlitaYagmailToolkit')
61
+ _safe_import_tool('aws', 'cloud.aws', None, 'AWSToolkit')
62
+ _safe_import_tool('azure', 'cloud.azure', None, 'AzureToolkit')
63
+ _safe_import_tool('gcp', 'cloud.gcp', None, 'GCPToolkit')
64
+ _safe_import_tool('k8s', 'cloud.k8s', None, 'KubernetesToolkit')
65
+ _safe_import_tool('custom_open_api', 'custom_open_api', None, 'OpenApiToolkit')
66
+ _safe_import_tool('elastic', 'elastic', None, 'ElasticToolkit')
67
+ _safe_import_tool('keycloak', 'keycloak', None, 'KeycloakToolkit')
68
+ _safe_import_tool('localgit', 'localgit', None, 'AlitaLocalGitToolkit')
69
+ _safe_import_tool('pandas', 'pandas', 'get_tools', 'PandasToolkit')
70
+ _safe_import_tool('azure_search', 'azure_ai.search', 'get_tools', 'AzureSearchToolkit')
71
+ _safe_import_tool('figma', 'figma', 'get_tools', 'FigmaToolkit')
72
+ _safe_import_tool('salesforce', 'salesforce', 'get_tools', 'SalesforceToolkit')
73
+ _safe_import_tool('carrier', 'carrier', 'get_tools', 'AlitaCarrierToolkit')
74
+ _safe_import_tool('ocr', 'ocr', 'get_tools', 'OCRToolkit')
75
+ _safe_import_tool('pptx', 'pptx', 'get_tools', 'PPTXToolkit')
76
+ _safe_import_tool('postman', 'postman', 'get_tools', 'PostmanToolkit')
77
+
78
+ # Log import summary
79
+ available_count = len(AVAILABLE_TOOLS)
80
+ total_attempted = len(AVAILABLE_TOOLS) + len(FAILED_IMPORTS)
81
+ logger.info(f"Tool imports completed: {available_count}/{total_attempted} successful")
82
+
83
+ def get_tools(tools_list, alita, llm, *args, **kwargs):
51
84
  tools = []
52
85
  for tool in tools_list:
53
86
  # validate tool name syntax - it cannot be started with _
54
87
  for tool_name in tool.get('settings', {}).get('selected_tools', []):
55
88
  if isinstance(tool_name, str) and tool_name.startswith('_'):
56
89
  raise ValueError(f"Tool name '{tool_name}' from toolkit '{tool.get('type', '')}' cannot start with '_'")
90
+
57
91
  tool['settings']['alita'] = alita
58
92
  tool['settings']['llm'] = llm
59
- if tool['type'] == 'openapi':
60
- tools.extend(get_openapi(tool))
61
- elif tool['type'] == 'github':
62
- tools.extend(get_github(tool))
63
- elif tool['type'] == 'jira':
64
- tools.extend(get_jira(tool))
65
- elif tool['type'] == 'confluence':
66
- tools.extend(get_confluence(tool))
67
- elif tool['type'] == 'service_now':
68
- tools.extend(get_service_now(tool))
69
- elif tool['type'] == 'gitlab':
70
- tools.extend(get_gitlab(tool))
71
- elif tool['type'] == 'gitlab_org':
72
- tools.extend(get_gitlab_org(tool))
73
- elif tool['type'] == 'zephyr':
74
- tools.extend(get_zephyr(tool))
75
- elif tool['type'] == 'browser':
76
- tools.extend(get_browser(tool))
77
- elif tool['type'] == 'yagmail':
78
- tools.extend(get_yagmail(tool))
79
- elif tool['type'] == 'report_portal':
80
- tools.extend(get_report_portal(tool))
81
- elif tool['type'] == 'bitbucket':
82
- tools.extend(get_bitbucket(tool))
83
- elif tool['type'] == 'testrail':
84
- tools.extend(get_testrail(tool))
85
- elif tool['type'] in ['ado_boards', 'ado_wiki', 'ado_plans']:
86
- tools.extend(get_ado(tool['type'], tool))
87
- elif tool['type'] in ['ado_repos', 'azure_devops_repos']:
88
- tools.extend(get_ado_repo(tool))
89
- elif tool['type'] == 'testio':
90
- tools.extend(get_testio(tool))
91
- elif tool['type'] == 'xray_cloud':
92
- tools.extend(get_xray_cloud(tool))
93
- elif tool['type'] == 'sharepoint':
94
- tools.extend(get_sharepoint(tool))
95
- elif tool['type'] == 'qtest':
96
- tools.extend(get_qtest(tool))
97
- elif tool['type'] == 'zephyr_scale':
98
- tools.extend(get_zephyr_scale(tool))
99
- elif tool['type'] == 'zephyr_enterprise':
100
- tools.extend(get_zephyr_enterprise(tool))
101
- elif tool['type'] == 'rally':
102
- tools.extend(get_rally(tool))
103
- elif tool['type'] == 'sql':
104
- tools.extend(get_sql(tool))
105
- elif tool['type'] == 'sonar':
106
- tools.extend(get_sonar(tool))
107
- elif tool['type'] == 'google_places':
108
- tools.extend(get_google_places(tool))
109
- elif tool['type'] == 'azure_search':
110
- tools.extend(get_azure_search(tool))
111
- elif tool['type'] == 'pandas':
112
- tools.extend(get_pandas(tool))
113
- elif tool['type'] == 'figma':
114
- tools.extend(get_figma(tool))
115
- elif tool['type'] == 'salesforce':
116
- tools.extend(get_salesforce(tool))
117
- elif tool['type'] == 'carrier':
118
- tools.extend(get_carrier(tool))
119
- elif tool['type'] == 'ocr':
120
- tools.extend(get_ocr(tool))
121
- elif tool['type'] == 'pptx':
122
- tools.extend(get_pptx(tool))
93
+ tool_type = tool['type']
94
+
95
+ # Check if tool is available and has get_tools function
96
+ if tool_type in AVAILABLE_TOOLS and 'get_tools' in AVAILABLE_TOOLS[tool_type]:
97
+ try:
98
+ get_tools_func = AVAILABLE_TOOLS[tool_type]['get_tools']
99
+
100
+ # Handle special cases for ADO tools
101
+ if tool_type in ['ado_boards', 'ado_wiki', 'ado_plans']:
102
+ tools.extend(get_tools_func(tool_type, tool))
103
+ else:
104
+ tools.extend(get_tools_func(tool))
105
+
106
+ except Exception as e:
107
+ logger.error(f"Error getting tools for {tool_type}: {e}")
108
+
109
+ # Handle ADO repos special case (it might be requested as azure_devops_repos)
110
+ elif tool_type in ['ado_repos', 'azure_devops_repos'] and 'ado_repos' in AVAILABLE_TOOLS:
111
+ try:
112
+ get_tools_func = AVAILABLE_TOOLS['ado_repos']['get_tools']
113
+ tools.extend(get_tools_func(tool))
114
+ except Exception as e:
115
+ logger.error(f"Error getting ADO repos tools: {e}")
116
+
117
+ # Handle custom modules
118
+ elif tool.get("settings", {}).get("module"):
119
+ try:
120
+ settings = tool.get("settings", {})
121
+ mod = import_module(settings.pop("module"))
122
+ tkitclass = getattr(mod, settings.pop("class"))
123
+ toolkit = tkitclass.get_toolkit(**tool["settings"])
124
+ tools.extend(toolkit.get_tools())
125
+ except Exception as e:
126
+ logger.error(f"Error in getting custom toolkit: {e}")
127
+
123
128
  else:
124
- if tool.get("settings", {}).get("module"):
125
- try:
126
- settings = tool.get("settings", {})
127
- mod = import_module(settings.pop("module"))
128
- tkitclass = getattr(mod, settings.pop("class"))
129
- toolkit = tkitclass.get_toolkit(**tool["settings"])
130
- tools.extend(toolkit.get_tools())
131
- except Exception as e:
132
- logger.error(f"Error in getting toolkit: {e}")
129
+ # Tool not available or not found
130
+ if tool_type in FAILED_IMPORTS:
131
+ logger.warning(f"Tool '{tool_type}' is not available: {FAILED_IMPORTS[tool_type]}")
132
+ else:
133
+ logger.warning(f"Unknown tool type: {tool_type}")
134
+
133
135
  return tools
134
136
 
135
137
  def get_toolkits():
136
- return [
137
- AlitaGitHubToolkit.toolkit_config_schema(),
138
- TestrailToolkit.toolkit_config_schema(),
139
- JiraToolkit.toolkit_config_schema(),
140
- AzureDevOpsPlansToolkit.toolkit_config_schema(),
141
- AzureDevOpsWikiToolkit.toolkit_config_schema(),
142
- AzureDevOpsWorkItemsToolkit.toolkit_config_schema(),
143
- RallyToolkit.toolkit_config_schema(),
144
- QtestToolkit.toolkit_config_schema(),
145
- ReportPortalToolkit.toolkit_config_schema(),
146
- TestIOToolkit.toolkit_config_schema(),
147
- SQLToolkit.toolkit_config_schema(),
148
- SonarToolkit.toolkit_config_schema(),
149
- GooglePlacesToolkit.toolkit_config_schema(),
150
- BrowserToolkit.toolkit_config_schema(),
151
- XrayToolkit.toolkit_config_schema(),
152
- AlitaGitlabToolkit.toolkit_config_schema(),
153
- ConfluenceToolkit.toolkit_config_schema(),
154
- ServiceNowToolkit.toolkit_config_schema(),
155
- AlitaBitbucketToolkit.toolkit_config_schema(),
156
- AlitaGitlabSpaceToolkit.toolkit_config_schema(),
157
- ZephyrScaleToolkit.toolkit_config_schema(),
158
- ZephyrEnterpriseToolkit.toolkit_config_schema(),
159
- ZephyrToolkit.toolkit_config_schema(),
160
- AlitaYagmailToolkit.toolkit_config_schema(),
161
- SharepointToolkit.toolkit_config_schema(),
162
- AzureDevOpsReposToolkit.toolkit_config_schema(),
163
- AWSToolkit.toolkit_config_schema(),
164
- AzureToolkit.toolkit_config_schema(),
165
- GCPToolkit.toolkit_config_schema(),
166
- KubernetesToolkit.toolkit_config_schema(),
167
- CustomOpenApiToolkit.toolkit_config_schema(),
168
- ElasticToolkit.toolkit_config_schema(),
169
- KeycloakToolkit.toolkit_config_schema(),
170
- AlitaLocalGitToolkit.toolkit_config_schema(),
171
- PandasToolkit.toolkit_config_schema(),
172
- AzureSearchToolkit.toolkit_config_schema(),
173
- FigmaToolkit.toolkit_config_schema(),
174
- SalesforceToolkit.toolkit_config_schema(),
175
- AlitaCarrierToolkit.toolkit_config_schema(),
176
- OCRToolkit.toolkit_config_schema(),
177
- PPTXToolkit.toolkit_config_schema(),
178
- ]
138
+ """Return toolkit configurations for all successfully imported toolkits."""
139
+ toolkit_configs = []
140
+
141
+ for toolkit_name, toolkit_class in AVAILABLE_TOOLKITS.items():
142
+ try:
143
+ if hasattr(toolkit_class, 'toolkit_config_schema'):
144
+ toolkit_configs.append(toolkit_class.toolkit_config_schema())
145
+ else:
146
+ logger.debug(f"Toolkit {toolkit_name} does not have toolkit_config_schema method")
147
+ except Exception as e:
148
+ logger.error(f"Error getting config schema for {toolkit_name}: {e}")
149
+
150
+ logger.info(f"Successfully loaded {len(toolkit_configs)} toolkit configurations")
151
+ return toolkit_configs
152
+
153
+ def get_available_tools():
154
+ """Return list of available tool types."""
155
+ return list(AVAILABLE_TOOLS.keys())
156
+
157
+ def get_failed_imports():
158
+ """Return dictionary of failed imports and their error messages."""
159
+ return FAILED_IMPORTS.copy()
160
+
161
+ def get_available_toolkits():
162
+ """Return list of available toolkit class names."""
163
+ return list(AVAILABLE_TOOLKITS.keys())
164
+
165
+ def diagnose_imports():
166
+ """Print diagnostic information about tool imports."""
167
+ available_count = len(AVAILABLE_TOOLS)
168
+ failed_count = len(FAILED_IMPORTS)
169
+ total_count = available_count + failed_count
170
+
171
+ print(f"=== Tool Import Diagnostic ===")
172
+ print(f"Total tools: {total_count}")
173
+ print(f"Successfully imported: {available_count}")
174
+ print(f"Failed imports: {failed_count}")
175
+ print(f"Success rate: {(available_count/total_count*100):.1f}%")
176
+
177
+ if AVAILABLE_TOOLS:
178
+ print(f"\n✅ Available tools ({len(AVAILABLE_TOOLS)}):")
179
+ for tool_name in sorted(AVAILABLE_TOOLS.keys()):
180
+ functions = []
181
+ if 'get_tools' in AVAILABLE_TOOLS[tool_name]:
182
+ functions.append('get_tools')
183
+ if 'toolkit_class' in AVAILABLE_TOOLS[tool_name]:
184
+ functions.append('toolkit')
185
+ print(f" - {tool_name}: {', '.join(functions)}")
186
+
187
+ if FAILED_IMPORTS:
188
+ print(f"\n❌ Failed imports ({len(FAILED_IMPORTS)}):")
189
+ for tool_name, error in FAILED_IMPORTS.items():
190
+ print(f" - {tool_name}: {error}")
191
+
192
+ if AVAILABLE_TOOLKITS:
193
+ print(f"\n🔧 Available toolkits ({len(AVAILABLE_TOOLKITS)}):")
194
+ for toolkit_name in sorted(AVAILABLE_TOOLKITS.keys()):
195
+ print(f" - {toolkit_name}")
196
+
197
+ # Export useful functions
198
+ __all__ = [
199
+ 'get_tools',
200
+ 'get_toolkits',
201
+ 'get_available_tools',
202
+ 'get_failed_imports',
203
+ 'get_available_toolkits',
204
+ 'diagnose_imports'
205
+ ]
@@ -2,7 +2,15 @@ from typing import Optional, List
2
2
 
3
3
  from langchain_core.tools import BaseToolkit, BaseTool
4
4
  from langgraph.store.postgres import PostgresStore
5
- from langmem import create_manage_memory_tool, create_search_memory_tool
5
+ try:
6
+ from langmem import create_manage_memory_tool, create_search_memory_tool
7
+ except ImportError:
8
+ # langmem is optional; define stubs to avoid import errors
9
+ def create_manage_memory_tool(*args, **kwargs): # pragma: no cover
10
+ raise ImportError("langmem is required for MemoryToolkit")
11
+
12
+ def create_search_memory_tool(*args, **kwargs): # pragma: no cover
13
+ raise ImportError("langmem is required for MemoryToolkit")
6
14
  from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr
7
15
 
8
16
  name = "memory"
@@ -0,0 +1,103 @@
1
+ from typing import List, Literal, Optional, Type
2
+
3
+ import requests
4
+ from langchain_core.tools import BaseToolkit, BaseTool
5
+ from pydantic import create_model, BaseModel, ConfigDict, Field, SecretStr, field_validator
6
+ from ..base.tool import BaseAction
7
+
8
+ from .api_wrapper import PostmanApiWrapper
9
+ from ..utils import clean_string, get_max_toolkit_length, TOOLKIT_SPLITTER, check_connection_response
10
+
11
+ name = "postman"
12
+
13
+ class PostmanAction(BaseAction):
14
+ """Tool for interacting with the Postman API."""
15
+
16
+ api_wrapper: PostmanApiWrapper = Field(default_factory=PostmanApiWrapper)
17
+ name: str
18
+ mode: str = ""
19
+ description: str = ""
20
+ args_schema: Optional[Type[BaseModel]] = None
21
+
22
+ @field_validator('name', mode='before')
23
+ @classmethod
24
+ def remove_spaces(cls, v):
25
+ return v.replace(' ', '')
26
+
27
+ def get_tools(tool):
28
+ toolkit = PostmanToolkit.get_toolkit(
29
+ selected_tools=tool['settings'].get('selected_tools', []),
30
+ api_key=tool['settings'].get('api_key', None),
31
+ base_url=tool['settings'].get(
32
+ 'base_url', 'https://api.getpostman.com'),
33
+ collection_id=tool['settings'].get('collection_id', None),
34
+ workspace_id=tool['settings'].get('workspace_id', None),
35
+ toolkit_name=tool.get('toolkit_name')
36
+ )
37
+ return toolkit.tools
38
+
39
+
40
+ class PostmanToolkit(BaseToolkit):
41
+ tools: List[BaseTool] = []
42
+ toolkit_max_length: int = 0
43
+
44
+ @staticmethod
45
+ def toolkit_config_schema() -> BaseModel:
46
+ selected_tools = {x['name']: x['args_schema'].schema(
47
+ ) for x in PostmanApiWrapper.model_construct().get_available_tools()}
48
+ PostmanToolkit.toolkit_max_length = get_max_toolkit_length(
49
+ selected_tools)
50
+ m = create_model(
51
+ name,
52
+ api_key=(SecretStr, Field(description="Postman API key",
53
+ json_schema_extra={'secret': True, 'configuration': True})),
54
+ base_url=(str, Field(description="Postman API base URL",
55
+ default="https://api.getpostman.com", json_schema_extra={'configuration': True})),
56
+ collection_id=(str, Field(description="Default collection ID", json_schema_extra={
57
+ 'toolkit_name': True, 'max_toolkit_length': PostmanToolkit.toolkit_max_length})),
58
+ workspace_id=(str, Field(description="Default workspace ID",
59
+ default="", json_schema_extra={'configuration': True})),
60
+ selected_tools=(List[Literal[tuple(selected_tools)]], Field(
61
+ default=[], json_schema_extra={'args_schemas': selected_tools})),
62
+ __config__=ConfigDict(json_schema_extra={'metadata': {
63
+ "label": "Elitea Postman", "icon_url": "postman.svg"}})
64
+ )
65
+
66
+ @check_connection_response
67
+ def check_connection(self):
68
+ response = requests.get(
69
+ f'{self.base_url}/collections',
70
+ headers={
71
+ 'X-API-Key': self.api_key.get_secret_value(),
72
+ 'Content-Type': 'application/json'
73
+ },
74
+ timeout=5
75
+ )
76
+ return response
77
+ m.check_connection = check_connection
78
+ return m
79
+
80
+ @classmethod
81
+ def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
82
+ if selected_tools is None:
83
+ selected_tools = []
84
+ postman_api_wrapper = PostmanApiWrapper(**kwargs)
85
+ prefix = clean_string(str(toolkit_name), cls.toolkit_max_length) + \
86
+ TOOLKIT_SPLITTER if toolkit_name else ''
87
+ available_tools = postman_api_wrapper.get_available_tools()
88
+ tools = []
89
+ for tool in available_tools:
90
+ if selected_tools:
91
+ if tool["name"] not in selected_tools:
92
+ continue
93
+ tools.append(PostmanAction(
94
+ api_wrapper=postman_api_wrapper,
95
+ name=prefix + tool["name"],
96
+ mode=tool["mode"],
97
+ description=f"{tool['description']}\nAPI URL: {postman_api_wrapper.base_url}",
98
+ args_schema=tool["args_schema"]
99
+ ))
100
+ return cls(tools=tools)
101
+
102
+ def get_tools(self):
103
+ return self.tools