alita-sdk 0.3.554__py3-none-any.whl → 0.3.603__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 (116) hide show
  1. alita_sdk/cli/agent_executor.py +2 -1
  2. alita_sdk/cli/agent_loader.py +34 -4
  3. alita_sdk/cli/agents.py +433 -203
  4. alita_sdk/configurations/openapi.py +227 -15
  5. alita_sdk/runtime/clients/client.py +4 -2
  6. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  7. alita_sdk/runtime/langchain/assistant.py +61 -11
  8. alita_sdk/runtime/langchain/constants.py +419 -171
  9. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +4 -2
  10. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  11. alita_sdk/runtime/langchain/langraph_agent.py +106 -21
  12. alita_sdk/runtime/langchain/utils.py +30 -14
  13. alita_sdk/runtime/toolkits/__init__.py +3 -0
  14. alita_sdk/runtime/toolkits/artifact.py +2 -1
  15. alita_sdk/runtime/toolkits/mcp.py +6 -3
  16. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  17. alita_sdk/runtime/toolkits/skill_router.py +2 -2
  18. alita_sdk/runtime/toolkits/tools.py +64 -2
  19. alita_sdk/runtime/toolkits/vectorstore.py +1 -1
  20. alita_sdk/runtime/tools/artifact.py +15 -0
  21. alita_sdk/runtime/tools/data_analysis.py +183 -0
  22. alita_sdk/runtime/tools/llm.py +30 -11
  23. alita_sdk/runtime/tools/mcp_server_tool.py +6 -3
  24. alita_sdk/runtime/tools/router.py +2 -4
  25. alita_sdk/runtime/tools/sandbox.py +9 -6
  26. alita_sdk/runtime/utils/constants.py +5 -1
  27. alita_sdk/runtime/utils/mcp_client.py +1 -1
  28. alita_sdk/runtime/utils/mcp_sse_client.py +1 -1
  29. alita_sdk/runtime/utils/toolkit_utils.py +2 -0
  30. alita_sdk/tools/__init__.py +3 -1
  31. alita_sdk/tools/ado/repos/__init__.py +26 -8
  32. alita_sdk/tools/ado/repos/repos_wrapper.py +78 -52
  33. alita_sdk/tools/ado/test_plan/__init__.py +3 -2
  34. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  35. alita_sdk/tools/ado/utils.py +1 -18
  36. alita_sdk/tools/ado/wiki/__init__.py +2 -1
  37. alita_sdk/tools/ado/wiki/ado_wrapper.py +23 -1
  38. alita_sdk/tools/ado/work_item/__init__.py +3 -2
  39. alita_sdk/tools/ado/work_item/ado_wrapper.py +23 -1
  40. alita_sdk/tools/advanced_jira_mining/__init__.py +2 -1
  41. alita_sdk/tools/aws/delta_lake/__init__.py +2 -1
  42. alita_sdk/tools/azure_ai/search/__init__.py +2 -1
  43. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  44. alita_sdk/tools/base_indexer_toolkit.py +15 -6
  45. alita_sdk/tools/bitbucket/__init__.py +2 -1
  46. alita_sdk/tools/bitbucket/api_wrapper.py +1 -1
  47. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +3 -3
  48. alita_sdk/tools/browser/__init__.py +1 -1
  49. alita_sdk/tools/carrier/__init__.py +1 -1
  50. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  51. alita_sdk/tools/cloud/aws/__init__.py +2 -1
  52. alita_sdk/tools/cloud/azure/__init__.py +2 -1
  53. alita_sdk/tools/cloud/gcp/__init__.py +2 -1
  54. alita_sdk/tools/cloud/k8s/__init__.py +2 -1
  55. alita_sdk/tools/code/linter/__init__.py +2 -1
  56. alita_sdk/tools/code/sonar/__init__.py +2 -1
  57. alita_sdk/tools/code_indexer_toolkit.py +19 -2
  58. alita_sdk/tools/confluence/__init__.py +7 -6
  59. alita_sdk/tools/confluence/api_wrapper.py +2 -2
  60. alita_sdk/tools/custom_open_api/__init__.py +2 -1
  61. alita_sdk/tools/elastic/__init__.py +2 -1
  62. alita_sdk/tools/elitea_base.py +28 -9
  63. alita_sdk/tools/figma/__init__.py +52 -6
  64. alita_sdk/tools/figma/api_wrapper.py +1158 -123
  65. alita_sdk/tools/figma/figma_client.py +73 -0
  66. alita_sdk/tools/figma/toon_tools.py +2748 -0
  67. alita_sdk/tools/github/__init__.py +2 -1
  68. alita_sdk/tools/github/github_client.py +69 -97
  69. alita_sdk/tools/github/schemas.py +4 -4
  70. alita_sdk/tools/gitlab/__init__.py +2 -1
  71. alita_sdk/tools/gitlab/api_wrapper.py +118 -38
  72. alita_sdk/tools/gitlab_org/__init__.py +2 -1
  73. alita_sdk/tools/gitlab_org/api_wrapper.py +60 -62
  74. alita_sdk/tools/google/bigquery/__init__.py +2 -1
  75. alita_sdk/tools/google_places/__init__.py +2 -1
  76. alita_sdk/tools/jira/__init__.py +2 -1
  77. alita_sdk/tools/keycloak/__init__.py +2 -1
  78. alita_sdk/tools/localgit/__init__.py +2 -1
  79. alita_sdk/tools/memory/__init__.py +1 -1
  80. alita_sdk/tools/ocr/__init__.py +2 -1
  81. alita_sdk/tools/openapi/__init__.py +227 -15
  82. alita_sdk/tools/openapi/api_wrapper.py +1287 -802
  83. alita_sdk/tools/pandas/__init__.py +11 -5
  84. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  85. alita_sdk/tools/postman/__init__.py +2 -1
  86. alita_sdk/tools/pptx/__init__.py +2 -1
  87. alita_sdk/tools/qtest/__init__.py +21 -2
  88. alita_sdk/tools/qtest/api_wrapper.py +430 -13
  89. alita_sdk/tools/rally/__init__.py +2 -1
  90. alita_sdk/tools/rally/api_wrapper.py +1 -1
  91. alita_sdk/tools/report_portal/__init__.py +2 -1
  92. alita_sdk/tools/salesforce/__init__.py +2 -1
  93. alita_sdk/tools/servicenow/__init__.py +2 -1
  94. alita_sdk/tools/sharepoint/__init__.py +2 -1
  95. alita_sdk/tools/sharepoint/api_wrapper.py +2 -2
  96. alita_sdk/tools/slack/__init__.py +3 -2
  97. alita_sdk/tools/slack/api_wrapper.py +2 -2
  98. alita_sdk/tools/sql/__init__.py +3 -2
  99. alita_sdk/tools/testio/__init__.py +2 -1
  100. alita_sdk/tools/testrail/__init__.py +2 -1
  101. alita_sdk/tools/utils/content_parser.py +77 -3
  102. alita_sdk/tools/utils/text_operations.py +163 -71
  103. alita_sdk/tools/xray/__init__.py +3 -2
  104. alita_sdk/tools/yagmail/__init__.py +2 -1
  105. alita_sdk/tools/zephyr/__init__.py +2 -1
  106. alita_sdk/tools/zephyr_enterprise/__init__.py +2 -1
  107. alita_sdk/tools/zephyr_essential/__init__.py +2 -1
  108. alita_sdk/tools/zephyr_scale/__init__.py +3 -2
  109. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  110. alita_sdk/tools/zephyr_squad/__init__.py +2 -1
  111. {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.603.dist-info}/METADATA +7 -6
  112. {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.603.dist-info}/RECORD +116 -111
  113. {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.603.dist-info}/WHEEL +0 -0
  114. {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.603.dist-info}/entry_points.txt +0 -0
  115. {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.603.dist-info}/licenses/LICENSE +0 -0
  116. {alita_sdk-0.3.554.dist-info → alita_sdk-0.3.603.dist-info}/top_level.txt +0 -0
@@ -72,7 +72,7 @@ class SkillRouterToolkit(BaseToolkit):
72
72
  default=list(selected_tools_options.keys()),
73
73
  json_schema_extra={'args_schemas': selected_tools_options}
74
74
  )),
75
- __config__=ConfigDict(json_schema_extra={'metadata': {"label": "Skill Router", "icon_url": None}})
75
+ __config__=ConfigDict(json_schema_extra={'metadata': {"label": "Skill Router", "icon_url": None, "hidden": True}})
76
76
  )
77
77
 
78
78
  @classmethod
@@ -147,7 +147,7 @@ class SkillRouterToolkit(BaseToolkit):
147
147
  name=tool["name"],
148
148
  description=description,
149
149
  args_schema=tool["args_schema"],
150
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
150
+ metadata={"toolkit_name": toolkit_name, "toolkit_type": "skill_router"} if toolkit_name else {}
151
151
  ))
152
152
 
153
153
  return cls(tools=tools)
@@ -14,10 +14,12 @@ from .prompt import PromptToolkit
14
14
  from .subgraph import SubgraphToolkit
15
15
  from .vectorstore import VectorStoreToolkit
16
16
  from .mcp import McpToolkit
17
+ from .mcp_config import McpConfigToolkit, get_mcp_config_toolkit_schemas
17
18
  from .skill_router import SkillRouterToolkit
18
19
  from ..tools.mcp_server_tool import McpServerTool
19
20
  from ..tools.sandbox import SandboxToolkit
20
21
  from ..tools.image_generation import ImageGenerationToolkit
22
+ from ..tools.data_analysis import DataAnalysisToolkit
21
23
  # Import community tools
22
24
  from ...community import get_toolkits as community_toolkits, get_tools as community_tools
23
25
  from ...tools.memory import MemoryToolkit
@@ -36,11 +38,16 @@ def get_toolkits():
36
38
  VectorStoreToolkit.toolkit_config_schema(),
37
39
  SandboxToolkit.toolkit_config_schema(),
38
40
  ImageGenerationToolkit.toolkit_config_schema(),
41
+ DataAnalysisToolkit.toolkit_config_schema(),
39
42
  McpToolkit.toolkit_config_schema(),
43
+ McpConfigToolkit.toolkit_config_schema(),
40
44
  SkillRouterToolkit.toolkit_config_schema()
41
45
  ]
42
46
 
43
- return core_toolkits + community_toolkits() + alita_toolkits()
47
+ # Add configured MCP servers (stdio and http) as available toolkits
48
+ mcp_config_toolkits = get_mcp_config_toolkit_schemas()
49
+
50
+ return core_toolkits + mcp_config_toolkits + community_toolkits() + alita_toolkits()
44
51
 
45
52
 
46
53
  def get_tools(tools_list: list, alita_client=None, llm=None, memory_store: BaseStore = None, debug_mode: Optional[bool] = False, mcp_tokens: Optional[dict] = None, conversation_id: Optional[str] = None, ignored_mcp_servers: Optional[list] = None) -> list:
@@ -135,6 +142,20 @@ def get_tools(tools_list: list, alita_client=None, llm=None, memory_store: BaseS
135
142
  pgvector_configuration=tool.get('settings', {}).get('pgvector_configuration'),
136
143
  conversation_id=conversation_id,
137
144
  ).get_tools()
145
+ elif tool['name'] == 'data_analysis':
146
+ # Data Analysis internal tool - uses conversation attachment bucket
147
+ settings = tool.get('settings', {})
148
+ bucket_name = settings.get('bucket_name')
149
+ if bucket_name:
150
+ tools += DataAnalysisToolkit.get_toolkit(
151
+ alita_client=alita_client,
152
+ llm=llm,
153
+ bucket_name=bucket_name,
154
+ toolkit_name="Data Analyst",
155
+ ).get_tools()
156
+ else:
157
+ logger.warning("Data Analysis internal tool requested "
158
+ "but no bucket_name provided in settings")
138
159
  elif tool['type'] == 'artifact':
139
160
  tool_handled = True
140
161
  toolkit_tools = ArtifactToolkit.get_toolkit(
@@ -261,7 +282,7 @@ def get_tools(tools_list: list, alita_client=None, llm=None, memory_store: BaseS
261
282
  settings = tool.get('settings', {})
262
283
  toolkit_name = tool.get('toolkit_name', '')
263
284
  selected_tools = settings.get('selected_tools', [])
264
-
285
+
265
286
  toolkit_tools = SkillRouterToolkit.get_toolkit(
266
287
  client=alita_client,
267
288
  llm=llm,
@@ -275,6 +296,47 @@ def get_tools(tools_list: list, alita_client=None, llm=None, memory_store: BaseS
275
296
  except Exception as e:
276
297
  logger.error(f"❌ Failed to initialize SkillRouterToolkit: {e}")
277
298
  raise
299
+ elif tool['type'] == 'mcp_config' or tool['type'].startswith('mcp_'):
300
+ tool_handled = True
301
+ # MCP Config toolkit - pre-configured MCP servers (stdio or http)
302
+ # Handle both explicit 'mcp_config' type and dynamic names like 'mcp_playwright'
303
+ logger.info(f"Processing mcp_config toolkit: {tool}")
304
+ try:
305
+ settings = tool.get('settings', {})
306
+
307
+ # Server name can come from settings or be extracted from type name
308
+ server_name = settings.get('server_name')
309
+ if not server_name and tool['type'].startswith('mcp_') and tool['type'] != 'mcp_config':
310
+ # Extract server name from type (e.g., 'mcp_playwright' -> 'playwright')
311
+ server_name = tool['type'][4:] # Remove 'mcp_' prefix
312
+
313
+ if not server_name:
314
+ logger.error(f"❌ No server_name found for mcp_config toolkit: {tool}")
315
+ continue
316
+
317
+ toolkit_name = tool.get('toolkit_name', '') or server_name
318
+ selected_tools = settings.get('selected_tools', [])
319
+ excluded_tools = settings.get('excluded_tools', [])
320
+
321
+ # Get server config (may be in settings or from global config)
322
+ server_config = settings.get('server_config')
323
+
324
+ toolkit_tools = McpConfigToolkit.get_toolkit(
325
+ server_name=server_name,
326
+ server_config=server_config,
327
+ user_config=settings,
328
+ selected_tools=selected_tools if selected_tools else None,
329
+ excluded_tools=excluded_tools if excluded_tools else None,
330
+ toolkit_name=toolkit_name,
331
+ client=alita_client,
332
+ ).get_tools()
333
+
334
+ tools.extend(toolkit_tools)
335
+ logger.info(f"✅ Successfully added {len(toolkit_tools)} tools from McpConfigToolkit ({server_name})")
336
+ except Exception as e:
337
+ logger.error(f"❌ Failed to initialize McpConfigToolkit: {e}")
338
+ if not debug_mode:
339
+ raise
278
340
  except McpAuthorizationRequired:
279
341
  # Re-raise auth required exceptions directly
280
342
  raise
@@ -56,7 +56,7 @@ class VectorStoreToolkit(BaseToolkit):
56
56
  name=tool["name"],
57
57
  description=description,
58
58
  args_schema=tool["args_schema"],
59
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
59
+ metadata={"toolkit_name": toolkit_name, "toolkit_type": "vectorstore"} if toolkit_name else {}
60
60
  ))
61
61
  return cls(tools=tools)
62
62
 
@@ -350,6 +350,21 @@ class ArtifactWrapper(NonCodeIndexerToolkit):
350
350
 
351
351
  include_extensions = kwargs.get('include_extensions', [])
352
352
  skip_extensions = kwargs.get('skip_extensions', [])
353
+ chunking_config = kwargs.get('chunking_config', {})
354
+
355
+ # Auto-include extensions from chunking_config if include_extensions is specified
356
+ # This allows chunking config to work without manually adding extensions to include_extensions
357
+ if chunking_config and include_extensions:
358
+ for ext_pattern in chunking_config.keys():
359
+ # Normalize extension pattern (both ".cbl" and "*.cbl" should work)
360
+ normalized = ext_pattern if ext_pattern.startswith('*') else f'*{ext_pattern}'
361
+ if normalized not in include_extensions:
362
+ include_extensions.append(normalized)
363
+ self._log_tool_event(
364
+ message=f"Auto-included extension '{normalized}' from chunking_config",
365
+ tool_name="loader"
366
+ )
367
+
353
368
  self._log_tool_event(message=f"Files filtering started. Include extensions: {include_extensions}. "
354
369
  f"Skip extensions: {skip_extensions}", tool_name="loader")
355
370
  # show the progress of filtering
@@ -0,0 +1,183 @@
1
+ """
2
+ Data Analysis internal tool for Alita SDK.
3
+
4
+ This tool provides Pandas-based data analysis capabilities as an internal tool,
5
+ accessible through the "Enable internal tools" dropdown menu.
6
+
7
+ It uses the conversation attachment bucket for file storage, providing seamless
8
+ integration with drag-and-drop file uploads in chat.
9
+ """
10
+ import logging
11
+ from typing import Any, List, Literal, Optional, Type
12
+
13
+ from langchain_core.tools import BaseTool, BaseToolkit
14
+ from pydantic import BaseModel, ConfigDict, create_model, Field
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ name = "data_analysis"
19
+
20
+
21
+ def get_tools(tools_list: list, alita_client=None, llm=None, memory_store=None):
22
+ """
23
+ Get data analysis tools for the provided tool configurations.
24
+
25
+ Args:
26
+ tools_list: List of tool configurations
27
+ alita_client: Alita client instance (required for data analysis)
28
+ llm: LLM client instance (required for code generation)
29
+ memory_store: Optional memory store instance (unused)
30
+
31
+ Returns:
32
+ List of data analysis tools
33
+ """
34
+ all_tools = []
35
+
36
+ for tool in tools_list:
37
+ if (tool.get('type') == 'data_analysis' or
38
+ tool.get('toolkit_name') == 'data_analysis'):
39
+ try:
40
+ if not alita_client:
41
+ logger.error("Alita client is required for data analysis tools")
42
+ continue
43
+
44
+ settings = tool.get('settings', {})
45
+ bucket_name = settings.get('bucket_name')
46
+
47
+ if not bucket_name:
48
+ logger.error("bucket_name is required for data analysis tools")
49
+ continue
50
+
51
+ toolkit_instance = DataAnalysisToolkit.get_toolkit(
52
+ alita_client=alita_client,
53
+ llm=llm,
54
+ bucket_name=bucket_name,
55
+ toolkit_name=tool.get('toolkit_name', '')
56
+ )
57
+ all_tools.extend(toolkit_instance.get_tools())
58
+ except Exception as e:
59
+ logger.error(f"Error in data analysis toolkit get_tools: {e}")
60
+ logger.error(f"Tool config: {tool}")
61
+ raise
62
+
63
+ return all_tools
64
+
65
+
66
+ class DataAnalysisToolkit(BaseToolkit):
67
+ """
68
+ Data Analysis toolkit providing Pandas-based data analysis capabilities.
69
+
70
+ This is an internal tool that uses the conversation attachment bucket
71
+ for file storage, enabling seamless integration with chat file uploads.
72
+ """
73
+ tools: List[BaseTool] = []
74
+
75
+ @staticmethod
76
+ def toolkit_config_schema() -> Type[BaseModel]:
77
+ """Get the configuration schema for the data analysis toolkit."""
78
+ # Import PandasWrapper to get available tools schema
79
+ from alita_sdk.tools.pandas.api_wrapper import PandasWrapper
80
+
81
+ selected_tools = {
82
+ x['name']: x['args_schema'].model_json_schema()
83
+ for x in PandasWrapper.model_construct().get_available_tools()
84
+ }
85
+
86
+ return create_model(
87
+ 'data_analysis',
88
+ bucket_name=(
89
+ Optional[str],
90
+ Field(
91
+ default=None,
92
+ title="Bucket name",
93
+ description="Bucket where files are stored (auto-injected from conversation)"
94
+ )
95
+ ),
96
+ selected_tools=(
97
+ List[Literal[tuple(selected_tools)]],
98
+ Field(
99
+ default=[],
100
+ json_schema_extra={'args_schemas': selected_tools}
101
+ )
102
+ ),
103
+ __config__=ConfigDict(json_schema_extra={
104
+ 'metadata': {
105
+ "label": "Data Analysis",
106
+ "icon_url": "data-analysis.svg",
107
+ "hidden": True, # Hidden from regular toolkit menu
108
+ "categories": ["internal_tool"],
109
+ "extra_categories": ["data analysis", "pandas", "dataframes", "data science"],
110
+ }
111
+ })
112
+ )
113
+
114
+ @classmethod
115
+ def get_toolkit(
116
+ cls,
117
+ alita_client=None,
118
+ llm=None,
119
+ bucket_name: str = None,
120
+ toolkit_name: Optional[str] = None,
121
+ selected_tools: Optional[List[str]] = None,
122
+ **kwargs
123
+ ):
124
+ """
125
+ Get toolkit with data analysis tools.
126
+
127
+ Args:
128
+ alita_client: Alita client instance (required)
129
+ llm: LLM for code generation (optional, uses alita_client.llm if not provided)
130
+ bucket_name: Conversation attachment bucket (required)
131
+ toolkit_name: Optional name prefix for tools
132
+ selected_tools: Optional list of tool names to include (default: all)
133
+ **kwargs: Additional arguments
134
+
135
+ Returns:
136
+ DataAnalysisToolkit instance with configured tools
137
+
138
+ Raises:
139
+ ValueError: If alita_client or bucket_name is not provided
140
+ """
141
+ if not alita_client:
142
+ raise ValueError("Alita client is required for data analysis")
143
+
144
+ if not bucket_name:
145
+ raise ValueError("bucket_name is required for data analysis (should be conversation attachment bucket)")
146
+
147
+ # Import the PandasWrapper from existing toolkit
148
+ from alita_sdk.tools.pandas.api_wrapper import PandasWrapper
149
+ from alita_sdk.tools.base.tool import BaseAction
150
+
151
+ # Create wrapper with conversation bucket
152
+ wrapper = PandasWrapper(
153
+ alita=alita_client,
154
+ llm=llm,
155
+ bucket_name=bucket_name
156
+ )
157
+
158
+ # Get tools from wrapper
159
+ available_tools = wrapper.get_available_tools()
160
+ tools = []
161
+
162
+ for tool in available_tools:
163
+ # Filter by selected_tools if provided
164
+ if selected_tools and tool["name"] not in selected_tools:
165
+ continue
166
+
167
+ description = tool["description"]
168
+ if toolkit_name:
169
+ description = f"Toolkit: {toolkit_name}\n{description}"
170
+ description = description[:1000]
171
+
172
+ tools.append(BaseAction(
173
+ api_wrapper=wrapper,
174
+ name=tool["name"],
175
+ description=description,
176
+ args_schema=tool["args_schema"],
177
+ metadata={"toolkit_name": toolkit_name, "toolkit_type": name} if toolkit_name else {}
178
+ ))
179
+
180
+ return cls(tools=tools)
181
+
182
+ def get_tools(self):
183
+ return self.tools
@@ -81,22 +81,41 @@ class LLMNode(BaseTool):
81
81
  """
82
82
  Prepare structured output parameters from structured_output_dict.
83
83
 
84
+ Expected self.structured_output_dict formats:
85
+ - {"field": "str"} / {"field": "list"} / {"field": "list[str]"} / {"field": "any"} ...
86
+ - OR {"field": {"type": "...", "description": "...", "default": ...}} (optional)
87
+
84
88
  Returns:
85
- Dictionary with parameter definitions for creating Pydantic model
89
+ Dict[str, Dict] suitable for create_pydantic_model(...)
86
90
  """
87
- struct_params = {
88
- key: {
89
- "type": 'list[str]' if 'list' in value else value,
90
- "description": ""
91
- }
92
- for key, value in (self.structured_output_dict or {}).items()
93
- }
91
+ struct_params: dict[str, dict] = {}
92
+
93
+ for key, value in (self.structured_output_dict or {}).items():
94
+ # Allow either a plain type string or a dict with details
95
+ if isinstance(value, dict):
96
+ type_str = (value.get("type") or "any")
97
+ desc = value.get("description", "") or ""
98
+ entry: dict = {"type": type_str, "description": desc}
99
+ if "default" in value:
100
+ entry["default"] = value["default"]
101
+ else:
102
+ type_str = (value or "any") if isinstance(value, str) else "any"
103
+ entry = {"type": type_str, "description": ""}
104
+
105
+ # Normalize: only convert the *exact* "list" into "list[str]"
106
+ # (avoid the old bug where "if 'list' in value" also hits "blacklist", etc.)
107
+ if isinstance(entry.get("type"), str) and entry["type"].strip().lower() == "list":
108
+ entry["type"] = "list[str]"
109
+
110
+ struct_params[key] = entry
111
+
94
112
  # Add default output field for proper response to user
95
113
  struct_params[ELITEA_RS] = {
96
- 'description': 'final output to user (summarized output from LLM)',
97
- 'type': 'str',
98
- "default": None
114
+ "description": "final output to user (summarized output from LLM)",
115
+ "type": "str",
116
+ "default": None,
99
117
  }
118
+
100
119
  return struct_params
101
120
 
102
121
  def _invoke_with_structured_output(self, llm_client: Any, messages: List, struct_model: Any, config: RunnableConfig):
@@ -1,9 +1,12 @@
1
1
  import uuid
2
2
  from logging import getLogger
3
- from typing import Any, Type, Literal, Optional, Union, List
3
+ from typing import Any, Type, Literal, Optional, Union, List, Annotated
4
4
 
5
5
  from langchain_core.tools import BaseTool
6
- from pydantic import BaseModel, Field, create_model, EmailStr, constr, ConfigDict
6
+ from pydantic import BaseModel, Field, create_model, ConfigDict, StringConstraints
7
+
8
+ # EmailStr moved to pydantic_extra_types in pydantic v2, use str for simplicity
9
+ EmailStr = str
7
10
 
8
11
  logger = getLogger(__name__)
9
12
 
@@ -59,7 +62,7 @@ class McpServerTool(BaseTool):
59
62
  if field.get("format") == "email":
60
63
  return EmailStr
61
64
  if "pattern" in field:
62
- return constr(regex=field["pattern"])
65
+ return Annotated[str, StringConstraints(pattern=field["pattern"])]
63
66
  return str
64
67
  if t == "integer":
65
68
  return int
@@ -27,10 +27,8 @@ class RouterNode(BaseTool):
27
27
  if result in [clean_string(formatted_result) for formatted_result in self.routes]:
28
28
  # If the result is one of the routes, return it
29
29
  return {"router_output": result}
30
- elif result == self.default_output:
31
- # If the result is the default output, return it
32
- return {"router_output": clean_string(self.default_output)}
33
- return {"router_output": 'END'}
30
+ # For any unmatched condition (including empty string), use the configured default_output
31
+ return {"router_output": clean_string(self.default_output)}
34
32
 
35
33
  def _run(self, *args, **kwargs):
36
34
  return self.invoke(**kwargs)
@@ -326,12 +326,15 @@ class SandboxToolkit(BaseToolkit):
326
326
 
327
327
  @staticmethod
328
328
  def toolkit_config_schema() -> Type[BaseModel]:
329
- # Create sample tools to get their schemas
330
- sample_tools = [
331
- PyodideSandboxTool(),
332
- StatefulPyodideSandboxTool()
333
- ]
334
- selected_tools = {x.name: x.args_schema.model_json_schema() for x in sample_tools}
329
+ # Get tool schemas without instantiating the tools (avoids Deno requirement)
330
+ try:
331
+ selected_tools = {
332
+ "pyodide_sandbox": sandbox_tool_input.model_json_schema(),
333
+ "stateful_pyodide_sandbox": sandbox_tool_input.model_json_schema(),
334
+ }
335
+ except Exception as e:
336
+ logger.warning(f"Could not generate sandbox tool schemas: {e}")
337
+ selected_tools = {}
335
338
 
336
339
  return create_model(
337
340
  'sandbox',
@@ -436,4 +436,8 @@ STYLES = r"""
436
436
  filter: hue-rotate(180deg) brightness(1.2);
437
437
  }
438
438
  </style>
439
- """
439
+ """
440
+
441
+ TOOLKIT_NAME_META = "toolkit_name"
442
+ TOOL_NAME_META = "tool_name"
443
+ TOOLKIT_TYPE_META = "toolkit_type"
@@ -192,7 +192,7 @@ class McpClient:
192
192
  "sampling": {}
193
193
  },
194
194
  "clientInfo": {
195
- "name": "Alita MCP Client",
195
+ "name": "ELITEA MCP Client",
196
196
  "version": "1.0.0"
197
197
  }
198
198
  }
@@ -374,7 +374,7 @@ class McpSseClient:
374
374
  "sampling": {}
375
375
  },
376
376
  "clientInfo": {
377
- "name": "Alita MCP Client",
377
+ "name": "ELITEA MCP Client",
378
378
  "version": "1.0.0"
379
379
  }
380
380
  }
@@ -59,10 +59,12 @@ def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
59
59
 
60
60
  # Create a tool configuration dict with required fields
61
61
  # Note: MCP toolkit always requires toolkit_name, other toolkits respect use_prefix flag
62
+ # Note: 'name' is always set for provider-based toolkits (used by provider_worker.utils.tools)
62
63
  tool_config = {
63
64
  'id': toolkit_config.get('id', random.randint(1, 1000000)),
64
65
  'type': toolkit_config.get('type', toolkit_type),
65
66
  'settings': settings,
67
+ 'name': toolkit_name, # Always pass name for provider toolkits
66
68
  'toolkit_name': toolkit_name if (use_prefix or toolkit_type == 'mcp') else None
67
69
  }
68
70
 
@@ -102,7 +102,7 @@ _safe_import_tool('k8s', 'cloud.k8s', None, 'KubernetesToolkit')
102
102
  _safe_import_tool('elastic', 'elastic', None, 'ElasticToolkit')
103
103
  _safe_import_tool('keycloak', 'keycloak', None, 'KeycloakToolkit')
104
104
  _safe_import_tool('localgit', 'localgit', None, 'AlitaLocalGitToolkit')
105
- _safe_import_tool('pandas', 'pandas', 'get_tools', 'PandasToolkit')
105
+ # pandas toolkit removed - use Data Analysis internal tool instead
106
106
  _safe_import_tool('azure_search', 'azure_ai.search', 'get_tools', 'AzureSearchToolkit')
107
107
  _safe_import_tool('figma', 'figma', 'get_tools', 'FigmaToolkit')
108
108
  _safe_import_tool('salesforce', 'salesforce', 'get_tools', 'SalesforceToolkit')
@@ -186,7 +186,9 @@ def get_tools(tools_list, alita, llm, store: Optional[BaseStore] = None, *args,
186
186
  toolkit = tkitclass.get_toolkit(**get_toolkit_params)
187
187
  toolkit_tools.extend(toolkit.get_tools())
188
188
  except Exception as e:
189
+ import traceback
189
190
  logger.error(f"Error in getting custom toolkit: {e}")
191
+ logger.error(f"Traceback:\n{traceback.format_exc()}")
190
192
  else:
191
193
  if tool_type in FAILED_IMPORTS:
192
194
  logger.warning(f"Tool '{tool_type}' is not available: {FAILED_IMPORTS[tool_type]}")
@@ -11,6 +11,7 @@ from ....configurations.pgvector import PgVectorConfiguration
11
11
  from ...base.tool import BaseAction
12
12
  from .repos_wrapper import ReposApiWrapper
13
13
  from ...utils import clean_string, get_max_toolkit_length, check_connection_response
14
+ from ....runtime.utils.constants import TOOLKIT_NAME_META, TOOL_NAME_META, TOOLKIT_TYPE_META
14
15
 
15
16
  name = "ado_repos"
16
17
 
@@ -52,13 +53,30 @@ class AzureDevOpsReposToolkit(BaseToolkit):
52
53
  embedding_model=(Optional[str], Field(default=None, description="Embedding configuration.", json_schema_extra={'configuration_model': 'embedding'})),
53
54
 
54
55
  selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
55
- __config__={'json_schema_extra': {'metadata':
56
- {
57
- "label": "ADO repos",
58
- "icon_url": "ado-repos-icon.svg",
59
- "categories": ["code repositories"],
60
- "extra_categories": ["code", "repository", "version control"],
61
- }}}
56
+ __config__={
57
+ 'json_schema_extra': {
58
+ 'metadata': {
59
+ "label": "ADO repos",
60
+ "icon_url": "ado-repos-icon.svg",
61
+ "categories": ["code repositories"],
62
+ "extra_categories": ["code", "repository", "version control"],
63
+ "sections": {
64
+ "auth": {
65
+ "required": True,
66
+ "subsections": [
67
+ {
68
+ "name": "Token",
69
+ "fields": ["token"]
70
+ }
71
+ ]
72
+ }
73
+ },
74
+ "configuration_group": {
75
+ "name": "ado",
76
+ }
77
+ }
78
+ }
79
+ }
62
80
  )
63
81
 
64
82
  @check_connection_response
@@ -107,7 +125,7 @@ class AzureDevOpsReposToolkit(BaseToolkit):
107
125
  name=tool["name"],
108
126
  description=description,
109
127
  args_schema=tool["args_schema"],
110
- metadata={"toolkit_name": toolkit_name} if toolkit_name else {}
128
+ metadata={TOOLKIT_NAME_META: toolkit_name, TOOLKIT_TYPE_META: name, TOOL_NAME_META: tool["name"]} if toolkit_name else {TOOL_NAME_META: tool["name"]}
111
129
  )
112
130
  )
113
131
  return cls(tools=tools)