alita-sdk 0.3.457__py3-none-any.whl → 0.3.486__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of alita-sdk might be problematic. Click here for more details.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- alita_sdk/cli/agent/__init__.py +5 -0
- alita_sdk/cli/agent/default.py +258 -0
- alita_sdk/cli/agent_executor.py +155 -0
- alita_sdk/cli/agent_loader.py +194 -0
- alita_sdk/cli/agent_ui.py +228 -0
- alita_sdk/cli/agents.py +3592 -0
- alita_sdk/cli/callbacks.py +647 -0
- alita_sdk/cli/cli.py +168 -0
- alita_sdk/cli/config.py +306 -0
- alita_sdk/cli/context/__init__.py +30 -0
- alita_sdk/cli/context/cleanup.py +198 -0
- alita_sdk/cli/context/manager.py +731 -0
- alita_sdk/cli/context/message.py +285 -0
- alita_sdk/cli/context/strategies.py +289 -0
- alita_sdk/cli/context/token_estimation.py +127 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/input_handler.py +419 -0
- alita_sdk/cli/inventory.py +1256 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/toolkit.py +327 -0
- alita_sdk/cli/toolkit_loader.py +85 -0
- alita_sdk/cli/tools/__init__.py +43 -0
- alita_sdk/cli/tools/approval.py +224 -0
- alita_sdk/cli/tools/filesystem.py +1665 -0
- alita_sdk/cli/tools/planning.py +389 -0
- alita_sdk/cli/tools/terminal.py +414 -0
- alita_sdk/community/__init__.py +64 -8
- alita_sdk/community/inventory/__init__.py +224 -0
- alita_sdk/community/inventory/config.py +257 -0
- alita_sdk/community/inventory/enrichment.py +2137 -0
- alita_sdk/community/inventory/extractors.py +1469 -0
- alita_sdk/community/inventory/ingestion.py +3172 -0
- alita_sdk/community/inventory/knowledge_graph.py +1457 -0
- alita_sdk/community/inventory/parsers/__init__.py +218 -0
- alita_sdk/community/inventory/parsers/base.py +295 -0
- alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
- alita_sdk/community/inventory/parsers/go_parser.py +851 -0
- alita_sdk/community/inventory/parsers/html_parser.py +389 -0
- alita_sdk/community/inventory/parsers/java_parser.py +593 -0
- alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
- alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
- alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
- alita_sdk/community/inventory/parsers/python_parser.py +604 -0
- alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
- alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
- alita_sdk/community/inventory/parsers/text_parser.py +322 -0
- alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
- alita_sdk/community/inventory/patterns/__init__.py +61 -0
- alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
- alita_sdk/community/inventory/patterns/loader.py +348 -0
- alita_sdk/community/inventory/patterns/registry.py +198 -0
- alita_sdk/community/inventory/presets.py +535 -0
- alita_sdk/community/inventory/retrieval.py +1403 -0
- alita_sdk/community/inventory/toolkit.py +169 -0
- alita_sdk/community/inventory/visualize.py +1370 -0
- alita_sdk/configurations/bitbucket.py +0 -3
- alita_sdk/runtime/clients/client.py +99 -26
- alita_sdk/runtime/langchain/assistant.py +4 -2
- alita_sdk/runtime/langchain/constants.py +2 -1
- alita_sdk/runtime/langchain/langraph_agent.py +134 -31
- alita_sdk/runtime/langchain/utils.py +1 -1
- alita_sdk/runtime/llms/preloaded.py +2 -6
- alita_sdk/runtime/toolkits/__init__.py +2 -0
- alita_sdk/runtime/toolkits/application.py +1 -1
- alita_sdk/runtime/toolkits/mcp.py +46 -36
- alita_sdk/runtime/toolkits/planning.py +171 -0
- alita_sdk/runtime/toolkits/tools.py +39 -6
- alita_sdk/runtime/tools/function.py +17 -5
- alita_sdk/runtime/tools/llm.py +249 -14
- alita_sdk/runtime/tools/planning/__init__.py +36 -0
- alita_sdk/runtime/tools/planning/models.py +246 -0
- alita_sdk/runtime/tools/planning/wrapper.py +607 -0
- alita_sdk/runtime/tools/vectorstore_base.py +41 -6
- alita_sdk/runtime/utils/mcp_oauth.py +80 -0
- alita_sdk/runtime/utils/streamlit.py +6 -10
- alita_sdk/runtime/utils/toolkit_utils.py +19 -4
- alita_sdk/tools/__init__.py +54 -27
- alita_sdk/tools/ado/repos/repos_wrapper.py +1 -2
- alita_sdk/tools/base_indexer_toolkit.py +150 -19
- alita_sdk/tools/bitbucket/__init__.py +2 -2
- alita_sdk/tools/chunkers/__init__.py +3 -1
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +95 -6
- alita_sdk/tools/chunkers/universal_chunker.py +269 -0
- alita_sdk/tools/code_indexer_toolkit.py +55 -22
- alita_sdk/tools/elitea_base.py +86 -21
- alita_sdk/tools/jira/__init__.py +1 -1
- alita_sdk/tools/jira/api_wrapper.py +91 -40
- alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
- alita_sdk/tools/qtest/__init__.py +1 -1
- alita_sdk/tools/qtest/api_wrapper.py +871 -32
- alita_sdk/tools/sharepoint/api_wrapper.py +22 -2
- alita_sdk/tools/sharepoint/authorization_helper.py +17 -1
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +8 -2
- alita_sdk/tools/zephyr_essential/api_wrapper.py +12 -13
- {alita_sdk-0.3.457.dist-info → alita_sdk-0.3.486.dist-info}/METADATA +146 -2
- {alita_sdk-0.3.457.dist-info → alita_sdk-0.3.486.dist-info}/RECORD +102 -40
- alita_sdk-0.3.486.dist-info/entry_points.txt +2 -0
- {alita_sdk-0.3.457.dist-info → alita_sdk-0.3.486.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.457.dist-info → alita_sdk-0.3.486.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.457.dist-info → alita_sdk-0.3.486.dist-info}/top_level.txt +0 -0
|
@@ -6,12 +6,11 @@ Following MCP specification: https://modelcontextprotocol.io/specification/2025-
|
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
8
|
import re
|
|
9
|
-
import requests
|
|
10
9
|
import asyncio
|
|
11
10
|
from typing import List, Optional, Any, Dict, Literal, ClassVar, Union
|
|
12
11
|
|
|
13
12
|
from langchain_core.tools import BaseToolkit, BaseTool
|
|
14
|
-
from pydantic import BaseModel, ConfigDict, Field
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field, SecretStr
|
|
15
14
|
|
|
16
15
|
from ..tools.mcp_server_tool import McpServerTool
|
|
17
16
|
from ..tools.mcp_remote_tool import McpRemoteTool
|
|
@@ -208,28 +207,32 @@ class McpToolkit(BaseToolkit):
|
|
|
208
207
|
}
|
|
209
208
|
)
|
|
210
209
|
),
|
|
211
|
-
|
|
212
|
-
|
|
210
|
+
client_id=(
|
|
211
|
+
Optional[str],
|
|
213
212
|
Field(
|
|
214
|
-
default=
|
|
215
|
-
description="
|
|
213
|
+
default=None,
|
|
214
|
+
description="OAuth Client ID (if applicable)"
|
|
216
215
|
)
|
|
217
216
|
),
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
client_secret=(
|
|
218
|
+
Optional[SecretStr],
|
|
220
219
|
Field(
|
|
221
|
-
default=
|
|
222
|
-
description="
|
|
223
|
-
json_schema_extra={
|
|
224
|
-
'tooltip': 'static: use registry, dynamic: live discovery, hybrid: try dynamic first'
|
|
225
|
-
}
|
|
220
|
+
default=None,
|
|
221
|
+
description="OAuth Client Secret (if applicable)"
|
|
226
222
|
)
|
|
227
223
|
),
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
scopes=(
|
|
225
|
+
Optional[List[str]],
|
|
226
|
+
Field(
|
|
227
|
+
default=None,
|
|
228
|
+
description="OAuth Scopes (if applicable)"
|
|
229
|
+
)
|
|
230
|
+
),
|
|
231
|
+
timeout=(
|
|
232
|
+
Union[int, str], # TODO: remove one I will figure out why UI sends str
|
|
230
233
|
Field(
|
|
231
234
|
default=300,
|
|
232
|
-
description="
|
|
235
|
+
description="Request timeout in seconds (1-3600)"
|
|
233
236
|
)
|
|
234
237
|
),
|
|
235
238
|
selected_tools=(
|
|
@@ -259,7 +262,7 @@ class McpToolkit(BaseToolkit):
|
|
|
259
262
|
__config__=ConfigDict(
|
|
260
263
|
json_schema_extra={
|
|
261
264
|
'metadata': {
|
|
262
|
-
"label": "
|
|
265
|
+
"label": "Remote MCP",
|
|
263
266
|
"icon_url": None,
|
|
264
267
|
"categories": ["other"],
|
|
265
268
|
"extra_categories": ["remote tools", "sse", "http"],
|
|
@@ -275,8 +278,6 @@ class McpToolkit(BaseToolkit):
|
|
|
275
278
|
url: str,
|
|
276
279
|
headers: Optional[Dict[str, str]] = None,
|
|
277
280
|
timeout: int = 60,
|
|
278
|
-
discovery_mode: str = "hybrid",
|
|
279
|
-
discovery_interval: int = 300,
|
|
280
281
|
selected_tools: List[str] = None,
|
|
281
282
|
enable_caching: bool = True,
|
|
282
283
|
cache_ttl: int = 300,
|
|
@@ -297,8 +298,6 @@ class McpToolkit(BaseToolkit):
|
|
|
297
298
|
url: MCP server HTTP URL
|
|
298
299
|
headers: HTTP headers for authentication
|
|
299
300
|
timeout: Request timeout in seconds
|
|
300
|
-
discovery_mode: Discovery mode ('static', 'dynamic', 'hybrid')
|
|
301
|
-
discovery_interval: Discovery interval in seconds (for periodic discovery)
|
|
302
301
|
selected_tools: List of specific tools to enable (empty = all tools)
|
|
303
302
|
enable_caching: Whether to enable caching
|
|
304
303
|
cache_ttl: Cache TTL in seconds
|
|
@@ -317,7 +316,6 @@ class McpToolkit(BaseToolkit):
|
|
|
317
316
|
|
|
318
317
|
# Convert numeric parameters that may come as strings from UI
|
|
319
318
|
timeout = safe_int(timeout, 60)
|
|
320
|
-
discovery_interval = safe_int(discovery_interval, 300)
|
|
321
319
|
cache_ttl = safe_int(cache_ttl, 300)
|
|
322
320
|
|
|
323
321
|
logger.info(f"Creating MCP toolkit: {toolkit_name}")
|
|
@@ -363,8 +361,7 @@ class McpToolkit(BaseToolkit):
|
|
|
363
361
|
connection_config=connection_config,
|
|
364
362
|
timeout=timeout,
|
|
365
363
|
selected_tools=selected_tools,
|
|
366
|
-
client=client
|
|
367
|
-
discovery_mode=discovery_mode
|
|
364
|
+
client=client
|
|
368
365
|
)
|
|
369
366
|
|
|
370
367
|
return toolkit
|
|
@@ -376,8 +373,7 @@ class McpToolkit(BaseToolkit):
|
|
|
376
373
|
connection_config: McpConnectionConfig,
|
|
377
374
|
timeout: int,
|
|
378
375
|
selected_tools: List[str],
|
|
379
|
-
client
|
|
380
|
-
discovery_mode: str = "dynamic"
|
|
376
|
+
client
|
|
381
377
|
) -> List[BaseTool]:
|
|
382
378
|
"""
|
|
383
379
|
Create tools from a single MCP server. Always performs live discovery when connection config is provided.
|
|
@@ -423,19 +419,28 @@ class McpToolkit(BaseToolkit):
|
|
|
423
419
|
|
|
424
420
|
logger.info(f"Successfully created {len(tools)} MCP tools from toolkit '{toolkit_name}' via direct discovery")
|
|
425
421
|
|
|
422
|
+
except McpAuthorizationRequired:
|
|
423
|
+
# Authorization is required; surface upstream so the caller can prompt the user
|
|
424
|
+
logger.info(f"MCP toolkit '{toolkit_name}' requires OAuth authorization")
|
|
425
|
+
raise
|
|
426
426
|
except Exception as e:
|
|
427
427
|
logger.error(f"Direct discovery failed for MCP toolkit '{toolkit_name}': {e}", exc_info=True)
|
|
428
428
|
logger.error(f"Discovery error details - URL: {connection_config.url}, Timeout: {timeout}s")
|
|
429
429
|
|
|
430
|
-
#
|
|
431
|
-
if isinstance(e, McpAuthorizationRequired):
|
|
432
|
-
|
|
430
|
+
# Check if the exception wraps McpAuthorizationRequired (can happen with asyncio)
|
|
431
|
+
if hasattr(e, '__cause__') and isinstance(e.__cause__, McpAuthorizationRequired):
|
|
432
|
+
logger.info(f"Found wrapped McpAuthorizationRequired, re-raising")
|
|
433
|
+
raise e.__cause__
|
|
434
|
+
|
|
435
|
+
# For new MCP toolkits (no client), don't silently return empty - surface the error
|
|
436
|
+
# This helps users understand why tool discovery failed
|
|
437
|
+
if not client:
|
|
438
|
+
logger.warning(f"No fallback available for toolkit '{toolkit_name}' - re-raising discovery error")
|
|
433
439
|
raise
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
logger.warning(f"No fallback available for toolkit '{toolkit_name}' - returning empty tools list")
|
|
440
|
+
|
|
441
|
+
# Only fall back to static discovery for existing toolkits with a client
|
|
442
|
+
logger.info(f"Falling back to static discovery for toolkit '{toolkit_name}'")
|
|
443
|
+
tools = cls._create_tools_static(toolkit_name, selected_tools, timeout, client)
|
|
439
444
|
|
|
440
445
|
# Don't add inspection tool to agent - it's only for internal use by toolkit
|
|
441
446
|
# inspection_tool = cls._create_inspection_tool(
|
|
@@ -481,8 +486,15 @@ class McpToolkit(BaseToolkit):
|
|
|
481
486
|
)
|
|
482
487
|
)
|
|
483
488
|
return all_tools, session_id
|
|
489
|
+
except McpAuthorizationRequired:
|
|
490
|
+
# Re-raise auth required exceptions directly
|
|
491
|
+
logger.info(f"[MCP SSE] Authorization required for '{toolkit_name}'")
|
|
492
|
+
raise
|
|
484
493
|
except Exception as e:
|
|
485
494
|
logger.error(f"[MCP SSE] Discovery failed for '{toolkit_name}': {e}")
|
|
495
|
+
# Check if the exception wraps McpAuthorizationRequired
|
|
496
|
+
if hasattr(e, '__cause__') and isinstance(e.__cause__, McpAuthorizationRequired):
|
|
497
|
+
raise e.__cause__
|
|
486
498
|
raise
|
|
487
499
|
|
|
488
500
|
@classmethod
|
|
@@ -858,8 +870,6 @@ def get_tools(tool_config: dict, alita_client, llm=None, memory_store=None) -> L
|
|
|
858
870
|
url=url,
|
|
859
871
|
headers=headers,
|
|
860
872
|
timeout=safe_int(settings.get('timeout'), 60),
|
|
861
|
-
discovery_mode=settings.get('discovery_mode', 'dynamic'),
|
|
862
|
-
discovery_interval=safe_int(settings.get('discovery_interval'), 300),
|
|
863
873
|
selected_tools=settings.get('selected_tools', []),
|
|
864
874
|
enable_caching=settings.get('enable_caching', True),
|
|
865
875
|
cache_ttl=safe_int(settings.get('cache_ttl'), 300),
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PlanningToolkit - Runtime toolkit for agent plan management.
|
|
3
|
+
|
|
4
|
+
Provides tools for creating, tracking, and completing multi-step execution plans.
|
|
5
|
+
Supports two storage backends:
|
|
6
|
+
1. PostgreSQL - when pgvector_configuration with connection_string is provided
|
|
7
|
+
2. Filesystem - when no connection string (local CLI usage)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from typing import ClassVar, List, Any, Literal, Optional, Callable
|
|
11
|
+
|
|
12
|
+
from langchain_community.agent_toolkits.base import BaseToolkit
|
|
13
|
+
from langchain_core.tools import BaseTool
|
|
14
|
+
from pydantic import create_model, BaseModel, ConfigDict, Field
|
|
15
|
+
from pydantic.fields import FieldInfo
|
|
16
|
+
|
|
17
|
+
from ..tools.planning import PlanningWrapper
|
|
18
|
+
from ...tools.base.tool import BaseAction
|
|
19
|
+
from ...tools.utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class PlanningToolkit(BaseToolkit):
|
|
23
|
+
"""
|
|
24
|
+
Toolkit for agent plan management.
|
|
25
|
+
|
|
26
|
+
Provides tools for creating, updating, and tracking execution plans.
|
|
27
|
+
Supports PostgreSQL (production) and filesystem (local) storage backends.
|
|
28
|
+
Plans are scoped by conversation_id.
|
|
29
|
+
"""
|
|
30
|
+
tools: List[BaseTool] = []
|
|
31
|
+
_toolkit_max_length: ClassVar[int] = 50 # Use ClassVar to avoid Pydantic treating it as field
|
|
32
|
+
|
|
33
|
+
@staticmethod
|
|
34
|
+
def toolkit_config_schema() -> BaseModel:
|
|
35
|
+
"""
|
|
36
|
+
Returns the configuration schema for the Planning toolkit.
|
|
37
|
+
|
|
38
|
+
Used by the UI to generate the toolkit configuration form.
|
|
39
|
+
"""
|
|
40
|
+
# Define available tools
|
|
41
|
+
selected_tools = {
|
|
42
|
+
'update_plan': {
|
|
43
|
+
'title': 'UpdatePlanInput',
|
|
44
|
+
'type': 'object',
|
|
45
|
+
'properties': {
|
|
46
|
+
'title': {'type': 'string', 'description': "Title for the plan"},
|
|
47
|
+
'steps': {'type': 'array', 'items': {'type': 'string'}, 'description': "List of step descriptions"},
|
|
48
|
+
'conversation_id': {'type': 'string', 'description': "Conversation ID (auto-injected)"}
|
|
49
|
+
},
|
|
50
|
+
'required': ['title', 'steps', 'conversation_id']
|
|
51
|
+
},
|
|
52
|
+
'complete_step': {
|
|
53
|
+
'title': 'CompleteStepInput',
|
|
54
|
+
'type': 'object',
|
|
55
|
+
'properties': {
|
|
56
|
+
'step_number': {'type': 'integer', 'description': "Step number to complete (1-indexed)"},
|
|
57
|
+
'conversation_id': {'type': 'string', 'description': "Conversation ID (auto-injected)"}
|
|
58
|
+
},
|
|
59
|
+
'required': ['step_number', 'conversation_id']
|
|
60
|
+
},
|
|
61
|
+
'get_plan_status': {
|
|
62
|
+
'title': 'GetPlanStatusInput',
|
|
63
|
+
'type': 'object',
|
|
64
|
+
'properties': {
|
|
65
|
+
'conversation_id': {'type': 'string', 'description': "Conversation ID (auto-injected)"}
|
|
66
|
+
},
|
|
67
|
+
'required': ['conversation_id']
|
|
68
|
+
},
|
|
69
|
+
'delete_plan': {
|
|
70
|
+
'title': 'DeletePlanInput',
|
|
71
|
+
'type': 'object',
|
|
72
|
+
'properties': {
|
|
73
|
+
'conversation_id': {'type': 'string', 'description': "Conversation ID (auto-injected)"}
|
|
74
|
+
},
|
|
75
|
+
'required': ['conversation_id']
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
PlanningToolkit._toolkit_max_length = get_max_toolkit_length(selected_tools)
|
|
80
|
+
|
|
81
|
+
return create_model(
|
|
82
|
+
"planning",
|
|
83
|
+
# Tool selection
|
|
84
|
+
selected_tools=(
|
|
85
|
+
List[Literal[tuple(selected_tools)]],
|
|
86
|
+
Field(
|
|
87
|
+
default=list(selected_tools.keys()),
|
|
88
|
+
json_schema_extra={'args_schemas': selected_tools}
|
|
89
|
+
)
|
|
90
|
+
),
|
|
91
|
+
__config__=ConfigDict(
|
|
92
|
+
json_schema_extra={
|
|
93
|
+
'metadata': {
|
|
94
|
+
"label": "Planning",
|
|
95
|
+
"description": "Tools for managing multi-step execution plans with progress tracking. Uses PostgreSQL when configured, filesystem otherwise.",
|
|
96
|
+
"icon_url": None,
|
|
97
|
+
"max_length": PlanningToolkit._toolkit_max_length
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def get_toolkit(
|
|
105
|
+
cls,
|
|
106
|
+
toolkit_name: Optional[str] = None,
|
|
107
|
+
selected_tools: Optional[List[str]] = None,
|
|
108
|
+
pgvector_configuration: Optional[dict] = None,
|
|
109
|
+
storage_dir: Optional[str] = None,
|
|
110
|
+
plan_callback: Optional[Any] = None,
|
|
111
|
+
conversation_id: Optional[str] = None,
|
|
112
|
+
**kwargs
|
|
113
|
+
):
|
|
114
|
+
"""
|
|
115
|
+
Create a PlanningToolkit instance with configured tools.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
toolkit_name: Optional name prefix for tools
|
|
119
|
+
selected_tools: List of tool names to include (default: all)
|
|
120
|
+
pgvector_configuration: PostgreSQL configuration dict with connection_string.
|
|
121
|
+
If not provided, uses filesystem storage.
|
|
122
|
+
storage_dir: Directory for filesystem storage (when no pgvector_configuration)
|
|
123
|
+
plan_callback: Optional callback function called when plan changes (for CLI UI)
|
|
124
|
+
conversation_id: Conversation ID for scoping plans.
|
|
125
|
+
For server: from elitea_core payload. For CLI: session_id.
|
|
126
|
+
**kwargs: Additional configuration options
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
PlanningToolkit instance with configured tools
|
|
130
|
+
"""
|
|
131
|
+
if selected_tools is None:
|
|
132
|
+
selected_tools = ['update_plan', 'complete_step', 'get_plan_status', 'delete_plan']
|
|
133
|
+
|
|
134
|
+
tools = []
|
|
135
|
+
|
|
136
|
+
# Extract connection string from pgvector configuration (if provided)
|
|
137
|
+
connection_string = None
|
|
138
|
+
if pgvector_configuration:
|
|
139
|
+
connection_string = pgvector_configuration.get('connection_string', '')
|
|
140
|
+
if hasattr(connection_string, 'get_secret_value'):
|
|
141
|
+
connection_string = connection_string.get_secret_value()
|
|
142
|
+
|
|
143
|
+
# Create wrapper - it will auto-select storage backend
|
|
144
|
+
wrapper = PlanningWrapper(
|
|
145
|
+
connection_string=connection_string if connection_string else None,
|
|
146
|
+
conversation_id=conversation_id,
|
|
147
|
+
storage_dir=storage_dir,
|
|
148
|
+
plan_callback=plan_callback,
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Build tool name prefix
|
|
152
|
+
prefix = clean_string(toolkit_name, cls._toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
|
|
153
|
+
|
|
154
|
+
# Create tools from wrapper
|
|
155
|
+
available_tools = wrapper.get_available_tools()
|
|
156
|
+
for tool in available_tools:
|
|
157
|
+
if tool["name"] not in selected_tools:
|
|
158
|
+
continue
|
|
159
|
+
|
|
160
|
+
tools.append(BaseAction(
|
|
161
|
+
api_wrapper=wrapper,
|
|
162
|
+
name=prefix + tool["name"],
|
|
163
|
+
description=tool["description"],
|
|
164
|
+
args_schema=tool["args_schema"]
|
|
165
|
+
))
|
|
166
|
+
|
|
167
|
+
return cls(tools=tools)
|
|
168
|
+
|
|
169
|
+
def get_tools(self) -> List[BaseTool]:
|
|
170
|
+
"""Return the list of configured tools."""
|
|
171
|
+
return self.tools
|
|
@@ -9,6 +9,7 @@ from alita_sdk.tools import get_tools as alita_tools
|
|
|
9
9
|
from .application import ApplicationToolkit
|
|
10
10
|
from .artifact import ArtifactToolkit
|
|
11
11
|
from .datasource import DatasourcesToolkit
|
|
12
|
+
from .planning import PlanningToolkit
|
|
12
13
|
from .prompt import PromptToolkit
|
|
13
14
|
from .subgraph import SubgraphToolkit
|
|
14
15
|
from .vectorstore import VectorStoreToolkit
|
|
@@ -21,6 +22,7 @@ from ...community import get_toolkits as community_toolkits, get_tools as commun
|
|
|
21
22
|
from ...tools.memory import MemoryToolkit
|
|
22
23
|
from ..utils.mcp_oauth import canonical_resource, McpAuthorizationRequired
|
|
23
24
|
from ...tools.utils import TOOLKIT_SPLITTER
|
|
25
|
+
from alita_sdk.tools import _inject_toolkit_id
|
|
24
26
|
|
|
25
27
|
logger = logging.getLogger(__name__)
|
|
26
28
|
|
|
@@ -29,6 +31,7 @@ def get_toolkits():
|
|
|
29
31
|
core_toolkits = [
|
|
30
32
|
ArtifactToolkit.toolkit_config_schema(),
|
|
31
33
|
MemoryToolkit.toolkit_config_schema(),
|
|
34
|
+
PlanningToolkit.toolkit_config_schema(),
|
|
32
35
|
VectorStoreToolkit.toolkit_config_schema(),
|
|
33
36
|
SandboxToolkit.toolkit_config_schema(),
|
|
34
37
|
ImageGenerationToolkit.toolkit_config_schema(),
|
|
@@ -38,7 +41,7 @@ def get_toolkits():
|
|
|
38
41
|
return core_toolkits + community_toolkits() + alita_toolkits()
|
|
39
42
|
|
|
40
43
|
|
|
41
|
-
def get_tools(tools_list: list, alita_client, llm, memory_store: BaseStore = None, debug_mode: Optional[bool] = False, mcp_tokens: Optional[dict] = None) -> list:
|
|
44
|
+
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) -> list:
|
|
42
45
|
prompts = []
|
|
43
46
|
tools = []
|
|
44
47
|
|
|
@@ -92,7 +95,7 @@ def get_tools(tools_list: list, alita_client, llm, memory_store: BaseStore = Non
|
|
|
92
95
|
logger.warning("Image generation internal tool requested "
|
|
93
96
|
"but no image generation model configured")
|
|
94
97
|
elif tool['type'] == 'artifact':
|
|
95
|
-
|
|
98
|
+
toolkit_tools = ArtifactToolkit.get_toolkit(
|
|
96
99
|
client=alita_client,
|
|
97
100
|
bucket=tool['settings']['bucket'],
|
|
98
101
|
toolkit_name=tool.get('toolkit_name', ''),
|
|
@@ -102,13 +105,36 @@ def get_tools(tools_list: list, alita_client, llm, memory_store: BaseStore = Non
|
|
|
102
105
|
pgvector_configuration=tool['settings'].get('pgvector_configuration', {}),
|
|
103
106
|
embedding_model=tool['settings'].get('embedding_model'),
|
|
104
107
|
collection_name=f"{tool.get('toolkit_name')}",
|
|
105
|
-
collection_schema
|
|
106
|
-
).get_tools()
|
|
108
|
+
collection_schema=str(tool['id']),
|
|
109
|
+
).get_tools()
|
|
110
|
+
# Inject toolkit_id for artifact tools as well
|
|
111
|
+
_inject_toolkit_id(tool, toolkit_tools)
|
|
112
|
+
tools.extend(toolkit_tools)
|
|
113
|
+
|
|
107
114
|
elif tool['type'] == 'vectorstore':
|
|
108
115
|
tools.extend(VectorStoreToolkit.get_toolkit(
|
|
109
116
|
llm=llm,
|
|
110
117
|
toolkit_name=tool.get('toolkit_name', ''),
|
|
111
118
|
**tool['settings']).get_tools())
|
|
119
|
+
elif tool['type'] == 'planning':
|
|
120
|
+
# Planning toolkit for multi-step task tracking
|
|
121
|
+
# Try to fetch pgvector_project_connstr from project secrets
|
|
122
|
+
pgvector_connstr = None
|
|
123
|
+
if alita_client:
|
|
124
|
+
try:
|
|
125
|
+
pgvector_connstr = alita_client.unsecret('pgvector_project_connstr')
|
|
126
|
+
if pgvector_connstr:
|
|
127
|
+
logger.info("Using pgvector_project_connstr for planning toolkit")
|
|
128
|
+
except Exception as e:
|
|
129
|
+
logger.debug(f"pgvector_project_connstr not available: {e}")
|
|
130
|
+
|
|
131
|
+
pgvector_config = {'connection_string': pgvector_connstr} if pgvector_connstr else {}
|
|
132
|
+
tools.extend(PlanningToolkit.get_toolkit(
|
|
133
|
+
toolkit_name=tool.get('toolkit_name', ''),
|
|
134
|
+
selected_tools=tool['settings'].get('selected_tools', []),
|
|
135
|
+
pgvector_configuration=pgvector_config,
|
|
136
|
+
conversation_id=conversation_id or tool['settings'].get('conversation_id'),
|
|
137
|
+
).get_tools())
|
|
112
138
|
elif tool['type'] == 'mcp':
|
|
113
139
|
# remote mcp tool initialization with token injection
|
|
114
140
|
settings = dict(tool['settings'])
|
|
@@ -153,8 +179,15 @@ def get_tools(tools_list: list, alita_client, llm, memory_store: BaseStore = Non
|
|
|
153
179
|
toolkit_name=tool.get('toolkit_name', ''),
|
|
154
180
|
client=alita_client,
|
|
155
181
|
**settings).get_tools())
|
|
182
|
+
except McpAuthorizationRequired:
|
|
183
|
+
# Re-raise auth required exceptions directly
|
|
184
|
+
raise
|
|
156
185
|
except Exception as e:
|
|
157
|
-
|
|
186
|
+
# Check for wrapped McpAuthorizationRequired
|
|
187
|
+
if hasattr(e, '__cause__') and isinstance(e.__cause__, McpAuthorizationRequired):
|
|
188
|
+
raise e.__cause__
|
|
189
|
+
# Check exception class name as fallback
|
|
190
|
+
if e.__class__.__name__ == 'McpAuthorizationRequired':
|
|
158
191
|
raise
|
|
159
192
|
logger.error(f"Error initializing toolkit for tool '{tool.get('name', 'unknown')}': {e}", exc_info=True)
|
|
160
193
|
if debug_mode:
|
|
@@ -271,7 +304,7 @@ def _init_single_mcp_tool(server_toolkit_name, toolkit_name, available_tool, ali
|
|
|
271
304
|
tool_name = f'{toolkit_name}{TOOLKIT_SPLITTER}{available_tool["name"]}'
|
|
272
305
|
return McpServerTool(
|
|
273
306
|
name=tool_name,
|
|
274
|
-
description=f"MCP for a tool '{tool_name}': {available_tool.get(
|
|
307
|
+
description=f"MCP for a tool '{tool_name}': {available_tool.get('description', '')}",
|
|
275
308
|
args_schema=McpServerTool.create_pydantic_model_from_schema(
|
|
276
309
|
available_tool.get("inputSchema", {})
|
|
277
310
|
),
|
|
@@ -16,6 +16,18 @@ from ..langchain.utils import propagate_the_input_mapping
|
|
|
16
16
|
logger = logging.getLogger(__name__)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def replace_escaped_newlines(data):
|
|
20
|
+
"""
|
|
21
|
+
Replace \\n with \n in all string values recursively.
|
|
22
|
+
Required for sanitization of state variables in code node
|
|
23
|
+
"""
|
|
24
|
+
if isinstance(data, dict):
|
|
25
|
+
return {key: replace_escaped_newlines(value) for key, value in data.items()}
|
|
26
|
+
elif isinstance(data, str):
|
|
27
|
+
return data.replace('\\n', '\n')
|
|
28
|
+
else:
|
|
29
|
+
return data
|
|
30
|
+
|
|
19
31
|
class FunctionTool(BaseTool):
|
|
20
32
|
name: str = 'FunctionalTool'
|
|
21
33
|
description: str = 'This is direct call node for tools'
|
|
@@ -30,11 +42,13 @@ class FunctionTool(BaseTool):
|
|
|
30
42
|
def _prepare_pyodide_input(self, state: Union[str, dict, ToolCall]) -> str:
|
|
31
43
|
"""Prepare input for PyodideSandboxTool by injecting state into the code block."""
|
|
32
44
|
# add state into the code block here since it might be changed during the execution of the code
|
|
33
|
-
state_copy = deepcopy(state)
|
|
45
|
+
state_copy = replace_escaped_newlines(deepcopy(state))
|
|
34
46
|
|
|
35
47
|
del state_copy['messages'] # remove messages to avoid issues with pickling without langchain-core
|
|
36
48
|
# inject state into the code block as alita_state variable
|
|
37
|
-
|
|
49
|
+
state_json = json.dumps(state_copy, ensure_ascii=False)
|
|
50
|
+
pyodide_predata = f'#state dict\nimport json\nalita_state = json.loads({json.dumps(state_json)})\n'
|
|
51
|
+
|
|
38
52
|
return pyodide_predata
|
|
39
53
|
|
|
40
54
|
def _handle_pyodide_output(self, tool_result: Any) -> dict:
|
|
@@ -94,9 +108,7 @@ class FunctionTool(BaseTool):
|
|
|
94
108
|
# special handler for PyodideSandboxTool
|
|
95
109
|
if self._is_pyodide_tool():
|
|
96
110
|
code = func_args['code']
|
|
97
|
-
func_args['code'] =
|
|
98
|
-
# handle new lines in the code properly
|
|
99
|
-
.replace('\\n','\\\\n'))
|
|
111
|
+
func_args['code'] = f"{self._prepare_pyodide_input(state)}\n{code}"
|
|
100
112
|
try:
|
|
101
113
|
tool_result = self.tool.invoke(func_args, config, **kwargs)
|
|
102
114
|
dispatch_custom_event(
|