alita-sdk 0.3.379__py3-none-any.whl → 0.3.627__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/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 +156 -0
- alita_sdk/cli/agent_loader.py +245 -0
- alita_sdk/cli/agent_ui.py +228 -0
- alita_sdk/cli/agents.py +3113 -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 +1073 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/testcases/__init__.py +94 -0
- alita_sdk/cli/testcases/data_generation.py +119 -0
- alita_sdk/cli/testcases/discovery.py +96 -0
- alita_sdk/cli/testcases/executor.py +84 -0
- alita_sdk/cli/testcases/logger.py +85 -0
- alita_sdk/cli/testcases/parser.py +172 -0
- alita_sdk/cli/testcases/prompts.py +91 -0
- alita_sdk/cli/testcases/reporting.py +125 -0
- alita_sdk/cli/testcases/setup.py +108 -0
- alita_sdk/cli/testcases/test_runner.py +282 -0
- alita_sdk/cli/testcases/utils.py +39 -0
- alita_sdk/cli/testcases/validation.py +90 -0
- alita_sdk/cli/testcases/workflow.py +196 -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 +1751 -0
- alita_sdk/cli/tools/planning.py +389 -0
- alita_sdk/cli/tools/terminal.py +414 -0
- alita_sdk/community/__init__.py +72 -12
- alita_sdk/community/inventory/__init__.py +236 -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 +173 -0
- alita_sdk/community/inventory/toolkit_utils.py +176 -0
- alita_sdk/community/inventory/visualize.py +1370 -0
- alita_sdk/configurations/__init__.py +1 -1
- alita_sdk/configurations/ado.py +141 -20
- alita_sdk/configurations/bitbucket.py +94 -2
- alita_sdk/configurations/confluence.py +130 -1
- alita_sdk/configurations/figma.py +76 -0
- alita_sdk/configurations/gitlab.py +91 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/openapi.py +329 -0
- alita_sdk/configurations/qtest.py +72 -1
- alita_sdk/configurations/report_portal.py +96 -0
- alita_sdk/configurations/sharepoint.py +148 -0
- alita_sdk/configurations/testio.py +83 -0
- alita_sdk/configurations/testrail.py +88 -0
- alita_sdk/configurations/xray.py +93 -0
- alita_sdk/configurations/zephyr_enterprise.py +93 -0
- alita_sdk/configurations/zephyr_essential.py +75 -0
- alita_sdk/runtime/clients/artifact.py +3 -3
- alita_sdk/runtime/clients/client.py +388 -46
- alita_sdk/runtime/clients/mcp_discovery.py +342 -0
- alita_sdk/runtime/clients/mcp_manager.py +262 -0
- alita_sdk/runtime/clients/sandbox_client.py +8 -21
- alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
- alita_sdk/runtime/langchain/assistant.py +157 -39
- alita_sdk/runtime/langchain/constants.py +647 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -4
- alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
- alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
- alita_sdk/runtime/langchain/document_loaders/constants.py +40 -19
- alita_sdk/runtime/langchain/langraph_agent.py +405 -84
- alita_sdk/runtime/langchain/utils.py +106 -7
- alita_sdk/runtime/llms/preloaded.py +2 -6
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/skills/__init__.py +91 -0
- alita_sdk/runtime/skills/callbacks.py +498 -0
- alita_sdk/runtime/skills/discovery.py +540 -0
- alita_sdk/runtime/skills/executor.py +610 -0
- alita_sdk/runtime/skills/input_builder.py +371 -0
- alita_sdk/runtime/skills/models.py +330 -0
- alita_sdk/runtime/skills/registry.py +355 -0
- alita_sdk/runtime/skills/skill_runner.py +330 -0
- alita_sdk/runtime/toolkits/__init__.py +31 -0
- alita_sdk/runtime/toolkits/application.py +29 -10
- alita_sdk/runtime/toolkits/artifact.py +20 -11
- alita_sdk/runtime/toolkits/datasource.py +13 -6
- alita_sdk/runtime/toolkits/mcp.py +783 -0
- alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
- alita_sdk/runtime/toolkits/planning.py +178 -0
- alita_sdk/runtime/toolkits/skill_router.py +238 -0
- alita_sdk/runtime/toolkits/subgraph.py +251 -6
- alita_sdk/runtime/toolkits/tools.py +356 -69
- alita_sdk/runtime/toolkits/vectorstore.py +11 -5
- alita_sdk/runtime/tools/__init__.py +10 -3
- alita_sdk/runtime/tools/application.py +27 -6
- alita_sdk/runtime/tools/artifact.py +511 -28
- alita_sdk/runtime/tools/data_analysis.py +183 -0
- alita_sdk/runtime/tools/function.py +67 -35
- alita_sdk/runtime/tools/graph.py +10 -4
- alita_sdk/runtime/tools/image_generation.py +148 -46
- alita_sdk/runtime/tools/llm.py +1003 -128
- alita_sdk/runtime/tools/loop.py +3 -1
- alita_sdk/runtime/tools/loop_output.py +3 -1
- alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
- alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
- alita_sdk/runtime/tools/mcp_server_tool.py +8 -5
- 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/router.py +2 -4
- alita_sdk/runtime/tools/sandbox.py +65 -48
- alita_sdk/runtime/tools/skill_router.py +776 -0
- alita_sdk/runtime/tools/tool.py +3 -1
- alita_sdk/runtime/tools/vectorstore.py +9 -3
- alita_sdk/runtime/tools/vectorstore_base.py +70 -14
- alita_sdk/runtime/utils/AlitaCallback.py +137 -21
- alita_sdk/runtime/utils/constants.py +5 -1
- alita_sdk/runtime/utils/mcp_client.py +492 -0
- alita_sdk/runtime/utils/mcp_oauth.py +361 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
- alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
- alita_sdk/runtime/utils/serialization.py +155 -0
- alita_sdk/runtime/utils/streamlit.py +40 -13
- alita_sdk/runtime/utils/toolkit_utils.py +30 -9
- alita_sdk/runtime/utils/utils.py +36 -0
- alita_sdk/tools/__init__.py +134 -35
- alita_sdk/tools/ado/repos/__init__.py +51 -32
- alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
- alita_sdk/tools/ado/test_plan/__init__.py +25 -9
- alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
- alita_sdk/tools/ado/utils.py +1 -18
- alita_sdk/tools/ado/wiki/__init__.py +25 -12
- alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
- alita_sdk/tools/ado/work_item/__init__.py +26 -13
- alita_sdk/tools/ado/work_item/ado_wrapper.py +73 -11
- alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
- alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
- alita_sdk/tools/aws/delta_lake/tool.py +5 -1
- alita_sdk/tools/azure_ai/search/__init__.py +11 -8
- alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
- alita_sdk/tools/base/tool.py +5 -1
- alita_sdk/tools/base_indexer_toolkit.py +271 -84
- alita_sdk/tools/bitbucket/__init__.py +17 -11
- alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
- alita_sdk/tools/browser/__init__.py +5 -4
- alita_sdk/tools/carrier/__init__.py +5 -6
- alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
- alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
- alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
- alita_sdk/tools/chunkers/__init__.py +3 -1
- alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
- alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
- alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
- alita_sdk/tools/chunkers/universal_chunker.py +270 -0
- alita_sdk/tools/cloud/aws/__init__.py +10 -7
- alita_sdk/tools/cloud/azure/__init__.py +10 -7
- alita_sdk/tools/cloud/gcp/__init__.py +10 -7
- alita_sdk/tools/cloud/k8s/__init__.py +10 -7
- alita_sdk/tools/code/linter/__init__.py +10 -8
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/code/sonar/__init__.py +11 -8
- alita_sdk/tools/code_indexer_toolkit.py +82 -22
- alita_sdk/tools/confluence/__init__.py +22 -16
- alita_sdk/tools/confluence/api_wrapper.py +107 -30
- alita_sdk/tools/confluence/loader.py +14 -2
- alita_sdk/tools/custom_open_api/__init__.py +12 -5
- alita_sdk/tools/elastic/__init__.py +11 -8
- alita_sdk/tools/elitea_base.py +493 -30
- alita_sdk/tools/figma/__init__.py +58 -11
- alita_sdk/tools/figma/api_wrapper.py +1235 -143
- alita_sdk/tools/figma/figma_client.py +73 -0
- alita_sdk/tools/figma/toon_tools.py +2748 -0
- alita_sdk/tools/github/__init__.py +14 -15
- alita_sdk/tools/github/github_client.py +224 -100
- alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
- alita_sdk/tools/github/schemas.py +14 -5
- alita_sdk/tools/github/tool.py +5 -1
- alita_sdk/tools/github/tool_prompts.py +9 -22
- alita_sdk/tools/gitlab/__init__.py +16 -11
- alita_sdk/tools/gitlab/api_wrapper.py +218 -48
- alita_sdk/tools/gitlab_org/__init__.py +10 -9
- alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
- alita_sdk/tools/google/bigquery/__init__.py +13 -12
- alita_sdk/tools/google/bigquery/tool.py +5 -1
- alita_sdk/tools/google_places/__init__.py +11 -8
- alita_sdk/tools/google_places/api_wrapper.py +1 -1
- alita_sdk/tools/jira/__init__.py +17 -10
- alita_sdk/tools/jira/api_wrapper.py +92 -41
- alita_sdk/tools/keycloak/__init__.py +11 -8
- alita_sdk/tools/localgit/__init__.py +9 -3
- alita_sdk/tools/localgit/local_git.py +62 -54
- alita_sdk/tools/localgit/tool.py +5 -1
- alita_sdk/tools/memory/__init__.py +12 -4
- alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
- alita_sdk/tools/ocr/__init__.py +11 -8
- alita_sdk/tools/openapi/__init__.py +491 -106
- alita_sdk/tools/openapi/api_wrapper.py +1368 -0
- alita_sdk/tools/openapi/tool.py +20 -0
- alita_sdk/tools/pandas/__init__.py +20 -12
- alita_sdk/tools/pandas/api_wrapper.py +38 -25
- alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
- alita_sdk/tools/postman/__init__.py +10 -9
- alita_sdk/tools/pptx/__init__.py +11 -10
- alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
- alita_sdk/tools/qtest/__init__.py +31 -11
- alita_sdk/tools/qtest/api_wrapper.py +2135 -86
- alita_sdk/tools/rally/__init__.py +10 -9
- alita_sdk/tools/rally/api_wrapper.py +1 -1
- alita_sdk/tools/report_portal/__init__.py +12 -8
- alita_sdk/tools/salesforce/__init__.py +10 -8
- alita_sdk/tools/servicenow/__init__.py +17 -15
- alita_sdk/tools/servicenow/api_wrapper.py +1 -1
- alita_sdk/tools/sharepoint/__init__.py +10 -7
- alita_sdk/tools/sharepoint/api_wrapper.py +129 -38
- alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
- alita_sdk/tools/sharepoint/utils.py +8 -2
- alita_sdk/tools/slack/__init__.py +10 -7
- alita_sdk/tools/slack/api_wrapper.py +2 -2
- alita_sdk/tools/sql/__init__.py +12 -9
- alita_sdk/tools/testio/__init__.py +10 -7
- alita_sdk/tools/testrail/__init__.py +11 -10
- alita_sdk/tools/testrail/api_wrapper.py +1 -1
- alita_sdk/tools/utils/__init__.py +9 -4
- alita_sdk/tools/utils/content_parser.py +103 -18
- alita_sdk/tools/utils/text_operations.py +410 -0
- alita_sdk/tools/utils/tool_prompts.py +79 -0
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +30 -13
- alita_sdk/tools/xray/__init__.py +13 -9
- alita_sdk/tools/yagmail/__init__.py +9 -3
- alita_sdk/tools/zephyr/__init__.py +10 -7
- alita_sdk/tools/zephyr_enterprise/__init__.py +11 -7
- alita_sdk/tools/zephyr_essential/__init__.py +10 -7
- alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
- alita_sdk/tools/zephyr_essential/client.py +2 -2
- alita_sdk/tools/zephyr_scale/__init__.py +11 -8
- alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
- alita_sdk/tools/zephyr_squad/__init__.py +10 -7
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +154 -8
- alita_sdk-0.3.627.dist-info/RECORD +468 -0
- alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
- alita_sdk-0.3.379.dist-info/RECORD +0 -360
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Serialization utilities for safe JSON encoding of complex objects.
|
|
3
|
+
|
|
4
|
+
Handles Pydantic models, LangChain messages, datetime objects, and other
|
|
5
|
+
non-standard types that may appear in state variables.
|
|
6
|
+
"""
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime, date
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _convert_to_serializable(obj: Any, _seen: set = None) -> Any:
|
|
16
|
+
"""
|
|
17
|
+
Recursively convert an object to JSON-serializable primitives.
|
|
18
|
+
|
|
19
|
+
Handles nested dicts and lists that may contain non-serializable objects.
|
|
20
|
+
Uses a seen set to prevent infinite recursion with circular references.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
obj: Any object to convert
|
|
24
|
+
_seen: Internal set to track seen object ids (for circular reference detection)
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
JSON-serializable representation of the object
|
|
28
|
+
"""
|
|
29
|
+
# Initialize seen set for circular reference detection
|
|
30
|
+
if _seen is None:
|
|
31
|
+
_seen = set()
|
|
32
|
+
|
|
33
|
+
# Check for circular references (only for mutable objects)
|
|
34
|
+
obj_id = id(obj)
|
|
35
|
+
if isinstance(obj, (dict, list, set)) and obj_id in _seen:
|
|
36
|
+
return f"<circular reference: {type(obj).__name__}>"
|
|
37
|
+
|
|
38
|
+
# Primitives - return as-is
|
|
39
|
+
if obj is None or isinstance(obj, (str, int, float, bool)):
|
|
40
|
+
return obj
|
|
41
|
+
|
|
42
|
+
# Add to seen set for mutable containers
|
|
43
|
+
if isinstance(obj, (dict, list, set)):
|
|
44
|
+
_seen = _seen | {obj_id} # Create new set to avoid mutation issues
|
|
45
|
+
|
|
46
|
+
# Dict - recursively process all values
|
|
47
|
+
if isinstance(obj, dict):
|
|
48
|
+
return {
|
|
49
|
+
_convert_to_serializable(k, _seen): _convert_to_serializable(v, _seen)
|
|
50
|
+
for k, v in obj.items()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# List/tuple - recursively process all items
|
|
54
|
+
if isinstance(obj, (list, tuple)):
|
|
55
|
+
return [_convert_to_serializable(item, _seen) for item in obj]
|
|
56
|
+
|
|
57
|
+
# Set - convert to list and process
|
|
58
|
+
if isinstance(obj, set):
|
|
59
|
+
return [_convert_to_serializable(item, _seen) for item in obj]
|
|
60
|
+
|
|
61
|
+
# Bytes - decode to string
|
|
62
|
+
if isinstance(obj, bytes):
|
|
63
|
+
try:
|
|
64
|
+
return obj.decode('utf-8')
|
|
65
|
+
except UnicodeDecodeError:
|
|
66
|
+
return obj.decode('utf-8', errors='replace')
|
|
67
|
+
|
|
68
|
+
# Datetime objects
|
|
69
|
+
if isinstance(obj, datetime):
|
|
70
|
+
return obj.isoformat()
|
|
71
|
+
if isinstance(obj, date):
|
|
72
|
+
return obj.isoformat()
|
|
73
|
+
|
|
74
|
+
# Pydantic BaseModel (v2) - check for model_dump method
|
|
75
|
+
if hasattr(obj, 'model_dump') and callable(getattr(obj, 'model_dump')):
|
|
76
|
+
try:
|
|
77
|
+
return _convert_to_serializable(obj.model_dump(), _seen)
|
|
78
|
+
except Exception as e:
|
|
79
|
+
logger.debug(f"Failed to call model_dump on {type(obj).__name__}: {e}")
|
|
80
|
+
|
|
81
|
+
# Pydantic BaseModel (v1) - check for dict method
|
|
82
|
+
if hasattr(obj, 'dict') and callable(getattr(obj, 'dict')) and hasattr(obj, '__fields__'):
|
|
83
|
+
try:
|
|
84
|
+
return _convert_to_serializable(obj.dict(), _seen)
|
|
85
|
+
except Exception as e:
|
|
86
|
+
logger.debug(f"Failed to call dict on {type(obj).__name__}: {e}")
|
|
87
|
+
|
|
88
|
+
# LangChain BaseMessage - extract key fields
|
|
89
|
+
if hasattr(obj, 'type') and hasattr(obj, 'content'):
|
|
90
|
+
try:
|
|
91
|
+
result = {
|
|
92
|
+
"type": obj.type,
|
|
93
|
+
"content": _convert_to_serializable(obj.content, _seen),
|
|
94
|
+
}
|
|
95
|
+
if hasattr(obj, 'additional_kwargs') and obj.additional_kwargs:
|
|
96
|
+
result["additional_kwargs"] = _convert_to_serializable(obj.additional_kwargs, _seen)
|
|
97
|
+
if hasattr(obj, 'name') and obj.name:
|
|
98
|
+
result["name"] = obj.name
|
|
99
|
+
return result
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logger.debug(f"Failed to extract message fields from {type(obj).__name__}: {e}")
|
|
102
|
+
|
|
103
|
+
# Objects with __dict__ attribute (custom classes)
|
|
104
|
+
if hasattr(obj, '__dict__'):
|
|
105
|
+
try:
|
|
106
|
+
return _convert_to_serializable(obj.__dict__, _seen)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
logger.debug(f"Failed to serialize __dict__ of {type(obj).__name__}: {e}")
|
|
109
|
+
|
|
110
|
+
# UUID objects
|
|
111
|
+
if hasattr(obj, 'hex') and hasattr(obj, 'int'):
|
|
112
|
+
return str(obj)
|
|
113
|
+
|
|
114
|
+
# Enum objects
|
|
115
|
+
if hasattr(obj, 'value') and hasattr(obj, 'name') and hasattr(obj.__class__, '__members__'):
|
|
116
|
+
return obj.value
|
|
117
|
+
|
|
118
|
+
# Last resort - convert to string
|
|
119
|
+
try:
|
|
120
|
+
return str(obj)
|
|
121
|
+
except Exception:
|
|
122
|
+
return f"<non-serializable: {type(obj).__name__}>"
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def safe_serialize(obj: Any, **kwargs) -> str:
|
|
126
|
+
"""
|
|
127
|
+
Safely serialize any object to a JSON string.
|
|
128
|
+
|
|
129
|
+
Pre-processes the entire object tree to convert non-serializable
|
|
130
|
+
objects before passing to json.dumps. This ensures nested dicts
|
|
131
|
+
and lists with non-standard objects are handled correctly.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
obj: Any object to serialize
|
|
135
|
+
**kwargs: Additional arguments passed to json.dumps
|
|
136
|
+
(e.g., indent, sort_keys)
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
JSON string representation of the object
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> from pydantic import BaseModel
|
|
143
|
+
>>> class User(BaseModel):
|
|
144
|
+
... name: str
|
|
145
|
+
>>> state = {"user": User(name="Alice"), "count": 5}
|
|
146
|
+
>>> safe_serialize(state)
|
|
147
|
+
'{"user": {"name": "Alice"}, "count": 5}'
|
|
148
|
+
"""
|
|
149
|
+
# Pre-process the entire object tree
|
|
150
|
+
serializable = _convert_to_serializable(obj)
|
|
151
|
+
|
|
152
|
+
# Set defaults
|
|
153
|
+
kwargs.setdefault('ensure_ascii', False)
|
|
154
|
+
|
|
155
|
+
return json.dumps(serializable, **kwargs)
|
|
@@ -287,7 +287,6 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
|
287
287
|
model_config={
|
|
288
288
|
"temperature": 0.1,
|
|
289
289
|
"max_tokens": 1000,
|
|
290
|
-
"top_p": 1.0
|
|
291
290
|
}
|
|
292
291
|
)
|
|
293
292
|
except Exception as e:
|
|
@@ -868,10 +867,24 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
|
868
867
|
label = f"{'🔒 ' if is_secret else ''}{'*' if is_required else ''}{field_name.replace('_', ' ').title()}"
|
|
869
868
|
|
|
870
869
|
if field_type == 'string':
|
|
871
|
-
if
|
|
870
|
+
# Check if this is an enum field
|
|
871
|
+
if field_schema.get('enum'):
|
|
872
|
+
# Dropdown for enum values
|
|
873
|
+
options = field_schema['enum']
|
|
874
|
+
default_index = 0
|
|
875
|
+
if default_value and str(default_value) in options:
|
|
876
|
+
default_index = options.index(str(default_value))
|
|
877
|
+
toolkit_config_values[field_name] = st.selectbox(
|
|
878
|
+
label,
|
|
879
|
+
options=options,
|
|
880
|
+
index=default_index,
|
|
881
|
+
help=field_description,
|
|
882
|
+
key=f"config_{field_name}_{selected_toolkit_idx}"
|
|
883
|
+
)
|
|
884
|
+
elif is_secret:
|
|
872
885
|
toolkit_config_values[field_name] = st.text_input(
|
|
873
886
|
label,
|
|
874
|
-
value=str(default_value) if default_value else '',
|
|
887
|
+
value=str(default_value) if default_value else '',
|
|
875
888
|
help=field_description,
|
|
876
889
|
type="password",
|
|
877
890
|
key=f"config_{field_name}_{selected_toolkit_idx}"
|
|
@@ -879,7 +892,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
|
879
892
|
else:
|
|
880
893
|
toolkit_config_values[field_name] = st.text_input(
|
|
881
894
|
label,
|
|
882
|
-
value=str(default_value) if default_value else '',
|
|
895
|
+
value=str(default_value) if default_value else '',
|
|
883
896
|
help=field_description,
|
|
884
897
|
key=f"config_{field_name}_{selected_toolkit_idx}"
|
|
885
898
|
)
|
|
@@ -971,6 +984,23 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
|
971
984
|
key=f"config_{field_name}_{selected_toolkit_idx}"
|
|
972
985
|
)
|
|
973
986
|
toolkit_config_values[field_name] = [line.strip() for line in array_input.split('\n') if line.strip()]
|
|
987
|
+
elif field_type == 'object':
|
|
988
|
+
# Handle object/dict types (like headers)
|
|
989
|
+
obj_input = st.text_area(
|
|
990
|
+
f"{label} (JSON object)",
|
|
991
|
+
value=json.dumps(default_value) if isinstance(default_value, dict) else str(default_value) if default_value else '',
|
|
992
|
+
help=f"{field_description} - Enter as JSON object, e.g. {{\"Authorization\": \"Bearer token\"}}",
|
|
993
|
+
placeholder='{"key": "value"}',
|
|
994
|
+
key=f"config_{field_name}_{selected_toolkit_idx}"
|
|
995
|
+
)
|
|
996
|
+
try:
|
|
997
|
+
if obj_input.strip():
|
|
998
|
+
toolkit_config_values[field_name] = json.loads(obj_input)
|
|
999
|
+
else:
|
|
1000
|
+
toolkit_config_values[field_name] = None
|
|
1001
|
+
except json.JSONDecodeError as e:
|
|
1002
|
+
st.error(f"Invalid JSON format for {field_name}: {e}")
|
|
1003
|
+
toolkit_config_values[field_name] = None
|
|
974
1004
|
else:
|
|
975
1005
|
st.info("This toolkit doesn't require additional configuration.")
|
|
976
1006
|
|
|
@@ -1225,7 +1255,6 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
|
1225
1255
|
model_config={
|
|
1226
1256
|
"temperature": 0.1,
|
|
1227
1257
|
"max_tokens": 1000,
|
|
1228
|
-
"top_p": 1.0
|
|
1229
1258
|
}
|
|
1230
1259
|
)
|
|
1231
1260
|
except Exception as e:
|
|
@@ -1356,20 +1385,18 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
|
1356
1385
|
help="Maximum number of tokens in the AI response"
|
|
1357
1386
|
)
|
|
1358
1387
|
|
|
1359
|
-
|
|
1360
|
-
"
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
step=0.1,
|
|
1365
|
-
help="Controls diversity via nucleus sampling"
|
|
1388
|
+
reasoning_effort = st.selectbox(
|
|
1389
|
+
"Reasoning effort:",
|
|
1390
|
+
options=['null', 'low', 'medium', 'high'],
|
|
1391
|
+
index=0,
|
|
1392
|
+
help="Higher effort better reasoning, slower response"
|
|
1366
1393
|
)
|
|
1367
1394
|
|
|
1368
1395
|
# Create LLM config
|
|
1369
1396
|
llm_config = {
|
|
1370
1397
|
'max_tokens': max_tokens,
|
|
1371
1398
|
'temperature': temperature,
|
|
1372
|
-
'
|
|
1399
|
+
'reasoning_effort': reasoning_effort
|
|
1373
1400
|
}
|
|
1374
1401
|
|
|
1375
1402
|
col1, col2 = st.columns([3, 1])
|
|
@@ -12,7 +12,9 @@ logger = logging.getLogger(__name__)
|
|
|
12
12
|
|
|
13
13
|
def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
|
|
14
14
|
llm_client: Any,
|
|
15
|
-
alita_client: Optional[Any] = None
|
|
15
|
+
alita_client: Optional[Any] = None,
|
|
16
|
+
mcp_tokens: Optional[Dict[str, Any]] = None,
|
|
17
|
+
use_prefix: bool = False) -> List[Any]:
|
|
16
18
|
"""
|
|
17
19
|
Instantiate a toolkit with LLM client support.
|
|
18
20
|
|
|
@@ -22,20 +24,25 @@ def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
|
|
|
22
24
|
Args:
|
|
23
25
|
toolkit_config: Configuration dictionary for the toolkit
|
|
24
26
|
llm_client: LLM client instance for tools that need LLM capabilities
|
|
25
|
-
|
|
27
|
+
alita_client: Optional additional client instance
|
|
28
|
+
mcp_tokens: Optional dictionary of MCP OAuth tokens by server URL
|
|
29
|
+
use_prefix: If True, tools get prefixed with toolkit_name to prevent collisions
|
|
30
|
+
(for agent use). If False, tools use base names only (for testing interface).
|
|
31
|
+
Default False for backward compatibility with testing.
|
|
26
32
|
|
|
27
33
|
Returns:
|
|
28
34
|
List of instantiated tools from the toolkit
|
|
29
35
|
|
|
30
36
|
Raises:
|
|
31
37
|
ValueError: If required configuration or client is missing
|
|
38
|
+
McpAuthorizationRequired: If MCP server requires OAuth authorization
|
|
32
39
|
Exception: If toolkit instantiation fails
|
|
33
40
|
"""
|
|
41
|
+
toolkit_name = toolkit_config.get('toolkit_name', 'unknown')
|
|
34
42
|
try:
|
|
35
43
|
from ..toolkits.tools import get_tools
|
|
36
44
|
|
|
37
|
-
toolkit_name
|
|
38
|
-
if not toolkit_name:
|
|
45
|
+
if not toolkit_name or toolkit_name == 'unknown':
|
|
39
46
|
raise ValueError("toolkit_name is required in configuration")
|
|
40
47
|
|
|
41
48
|
if not llm_client:
|
|
@@ -46,18 +53,24 @@ def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
|
|
|
46
53
|
# Log the configuration being used
|
|
47
54
|
logger.info(f"Instantiating toolkit {toolkit_name} with LLM client")
|
|
48
55
|
logger.debug(f"Toolkit {toolkit_name} configuration: {toolkit_config}")
|
|
49
|
-
|
|
56
|
+
|
|
57
|
+
# Use toolkit type from config, or fall back to lowercase toolkit name
|
|
58
|
+
toolkit_type = toolkit_config.get('type', toolkit_name.lower())
|
|
59
|
+
|
|
50
60
|
# Create a tool configuration dict with required fields
|
|
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)
|
|
51
63
|
tool_config = {
|
|
52
64
|
'id': toolkit_config.get('id', random.randint(1, 1000000)),
|
|
53
|
-
'type': toolkit_config.get('type',
|
|
65
|
+
'type': toolkit_config.get('type', toolkit_type),
|
|
54
66
|
'settings': settings,
|
|
55
|
-
'
|
|
67
|
+
'name': toolkit_name, # Always pass name for provider toolkits
|
|
68
|
+
'toolkit_name': toolkit_name if (use_prefix or toolkit_type == 'mcp') else None
|
|
56
69
|
}
|
|
57
70
|
|
|
58
71
|
# Get tools using the toolkit configuration with clients
|
|
59
|
-
# Parameter order: get_tools(tools_list, alita_client, llm, memory_store)
|
|
60
|
-
tools = get_tools([tool_config], alita_client, llm_client)
|
|
72
|
+
# Parameter order: get_tools(tools_list, alita_client, llm, memory_store, debug_mode, mcp_tokens)
|
|
73
|
+
tools = get_tools([tool_config], alita_client, llm_client, mcp_tokens=mcp_tokens)
|
|
61
74
|
|
|
62
75
|
if not tools:
|
|
63
76
|
logger.warning(f"No tools returned for toolkit {toolkit_name}")
|
|
@@ -67,6 +80,14 @@ def instantiate_toolkit_with_client(toolkit_config: Dict[str, Any],
|
|
|
67
80
|
return tools
|
|
68
81
|
|
|
69
82
|
except Exception as e:
|
|
83
|
+
# Re-raise McpAuthorizationRequired without logging as error
|
|
84
|
+
from ..utils.mcp_oauth import McpAuthorizationRequired
|
|
85
|
+
|
|
86
|
+
if isinstance(e, McpAuthorizationRequired):
|
|
87
|
+
logger.info(f"Toolkit {toolkit_name} requires MCP OAuth authorization")
|
|
88
|
+
raise
|
|
89
|
+
|
|
90
|
+
# Log and re-raise other errors
|
|
70
91
|
logger.error(f"Error instantiating toolkit {toolkit_name} with client: {str(e)}")
|
|
71
92
|
raise
|
|
72
93
|
|
alita_sdk/runtime/utils/utils.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import logging
|
|
1
3
|
import re
|
|
2
4
|
from enum import Enum
|
|
5
|
+
from typing import Any
|
|
3
6
|
|
|
7
|
+
# DEPRECATED: Tool names no longer use prefixes
|
|
8
|
+
# Kept for backward compatibility only
|
|
4
9
|
TOOLKIT_SPLITTER = "___"
|
|
5
10
|
|
|
6
11
|
class IndexerKeywords(Enum):
|
|
@@ -30,3 +35,34 @@ def clean_node_str(s: str) -> str:
|
|
|
30
35
|
"""Cleans a node string by removing all non-alphanumeric characters except underscores and spaces."""
|
|
31
36
|
cleaned_string = re.sub(r'[^\w\s]', '', s)
|
|
32
37
|
return cleaned_string
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def resolve_image_from_cache(client: Any, cached_image_id: str) -> bytes:
|
|
41
|
+
"""
|
|
42
|
+
Resolve cached_image_id from client's image cache and return decoded binary data.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
client: AlitaClient instance with _generated_images_cache attribute
|
|
46
|
+
cached_image_id: The cached image ID to resolve
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
bytes: Decoded binary image data
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
ValueError: If cached_image_id not found or decoding fails
|
|
53
|
+
"""
|
|
54
|
+
cache = getattr(client, '_generated_images_cache', {})
|
|
55
|
+
|
|
56
|
+
if cached_image_id not in cache:
|
|
57
|
+
raise ValueError(f"Image reference '{cached_image_id}' not found. The image may have expired.")
|
|
58
|
+
|
|
59
|
+
cached_data = cache[cached_image_id]
|
|
60
|
+
base64_data = cached_data.get('base64_data', '')
|
|
61
|
+
logging.debug(f"Resolved cached_image_id '{cached_image_id}' from cache (length: {len(base64_data)} chars)")
|
|
62
|
+
# Decode base64 to binary data for image files
|
|
63
|
+
try:
|
|
64
|
+
binary_data = base64.b64decode(base64_data)
|
|
65
|
+
logging.debug(f"Decoded base64 to binary data ({len(binary_data)} bytes)")
|
|
66
|
+
return binary_data
|
|
67
|
+
except Exception as e:
|
|
68
|
+
raise ValueError(f"Failed to decode image data for '{cached_image_id}': {e}")
|
alita_sdk/tools/__init__.py
CHANGED
|
@@ -13,6 +13,30 @@ AVAILABLE_TOOLS = {}
|
|
|
13
13
|
AVAILABLE_TOOLKITS = {}
|
|
14
14
|
FAILED_IMPORTS = {}
|
|
15
15
|
|
|
16
|
+
|
|
17
|
+
def _inject_toolkit_id(tool_conf: dict, toolkit_tools) -> None:
|
|
18
|
+
"""Inject `toolkit_id` into tools that expose `api_wrapper.toolkit_id`.
|
|
19
|
+
|
|
20
|
+
This reads 'id' from the tool configuration and, if it is an integer,
|
|
21
|
+
assigns it to the 'toolkit_id' attribute of the 'api_wrapper' for each
|
|
22
|
+
tool in 'toolkit_tools' that supports it.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
tool_conf: Raw tool configuration item from 'tools_list'.
|
|
26
|
+
toolkit_tools: List of instantiated tools produced by a toolkit.
|
|
27
|
+
"""
|
|
28
|
+
toolkit_id = tool_conf.get('id')
|
|
29
|
+
if isinstance(toolkit_id, int):
|
|
30
|
+
for t in toolkit_tools:
|
|
31
|
+
if hasattr(t, 'api_wrapper') and hasattr(t.api_wrapper, 'toolkit_id'):
|
|
32
|
+
t.api_wrapper.toolkit_id = toolkit_id
|
|
33
|
+
else:
|
|
34
|
+
logger.error(
|
|
35
|
+
f"Toolkit ID is missing or not an integer for tool "
|
|
36
|
+
f"`{tool_conf.get('type', '')}` with name `{tool_conf.get('name', '')}`"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
16
40
|
def _safe_import_tool(tool_name, module_path, get_tools_name=None, toolkit_class_name=None):
|
|
17
41
|
"""Safely import a tool module and register available functions/classes."""
|
|
18
42
|
try:
|
|
@@ -21,6 +45,12 @@ def _safe_import_tool(tool_name, module_path, get_tools_name=None, toolkit_class
|
|
|
21
45
|
imported = {}
|
|
22
46
|
if get_tools_name and hasattr(module, get_tools_name):
|
|
23
47
|
imported['get_tools'] = getattr(module, get_tools_name)
|
|
48
|
+
|
|
49
|
+
if hasattr(module, 'get_toolkit'):
|
|
50
|
+
imported['get_toolkit'] = getattr(module, 'get_toolkit')
|
|
51
|
+
|
|
52
|
+
if hasattr(module, 'get_toolkit_available_tools'):
|
|
53
|
+
imported['get_toolkit_available_tools'] = getattr(module, 'get_toolkit_available_tools')
|
|
24
54
|
|
|
25
55
|
if toolkit_class_name and hasattr(module, toolkit_class_name):
|
|
26
56
|
imported['toolkit_class'] = getattr(module, toolkit_class_name)
|
|
@@ -34,9 +64,10 @@ def _safe_import_tool(tool_name, module_path, get_tools_name=None, toolkit_class
|
|
|
34
64
|
FAILED_IMPORTS[tool_name] = str(e)
|
|
35
65
|
logger.debug(f"Failed to import {tool_name}: {e}")
|
|
36
66
|
|
|
67
|
+
|
|
37
68
|
# Safe imports for all tools
|
|
38
69
|
_safe_import_tool('github', 'github', 'get_tools', 'AlitaGitHubToolkit')
|
|
39
|
-
_safe_import_tool('openapi', 'openapi', 'get_tools')
|
|
70
|
+
_safe_import_tool('openapi', 'openapi', 'get_tools', 'AlitaOpenAPIToolkit')
|
|
40
71
|
_safe_import_tool('jira', 'jira', 'get_tools', 'JiraToolkit')
|
|
41
72
|
_safe_import_tool('confluence', 'confluence', 'get_tools', 'ConfluenceToolkit')
|
|
42
73
|
_safe_import_tool('service_now', 'servicenow', 'get_tools', 'ServiceNowToolkit')
|
|
@@ -71,7 +102,7 @@ _safe_import_tool('k8s', 'cloud.k8s', None, 'KubernetesToolkit')
|
|
|
71
102
|
_safe_import_tool('elastic', 'elastic', None, 'ElasticToolkit')
|
|
72
103
|
_safe_import_tool('keycloak', 'keycloak', None, 'KeycloakToolkit')
|
|
73
104
|
_safe_import_tool('localgit', 'localgit', None, 'AlitaLocalGitToolkit')
|
|
74
|
-
|
|
105
|
+
# pandas toolkit removed - use Data Analysis internal tool instead
|
|
75
106
|
_safe_import_tool('azure_search', 'azure_ai.search', 'get_tools', 'AzureSearchToolkit')
|
|
76
107
|
_safe_import_tool('figma', 'figma', 'get_tools', 'FigmaToolkit')
|
|
77
108
|
_safe_import_tool('salesforce', 'salesforce', 'get_tools', 'SalesforceToolkit')
|
|
@@ -90,64 +121,83 @@ available_count = len(AVAILABLE_TOOLS)
|
|
|
90
121
|
total_attempted = len(AVAILABLE_TOOLS) + len(FAILED_IMPORTS)
|
|
91
122
|
logger.info(f"Tool imports completed: {available_count}/{total_attempted} successful")
|
|
92
123
|
|
|
124
|
+
# Import community module to trigger community toolkit registration
|
|
125
|
+
try:
|
|
126
|
+
from alita_sdk import community # noqa: F401
|
|
127
|
+
logger.debug("Community toolkits registered successfully")
|
|
128
|
+
except ImportError as e:
|
|
129
|
+
logger.debug(f"Community module not available: {e}")
|
|
130
|
+
|
|
131
|
+
|
|
93
132
|
def get_tools(tools_list, alita, llm, store: Optional[BaseStore] = None, *args, **kwargs):
|
|
94
133
|
tools = []
|
|
134
|
+
|
|
95
135
|
for tool in tools_list:
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if not
|
|
136
|
+
toolkit_tools = []
|
|
137
|
+
settings = tool.get('settings')
|
|
138
|
+
|
|
139
|
+
# Skip tools without settings early
|
|
140
|
+
if not settings:
|
|
101
141
|
logger.warning(f"Tool '{tool.get('type', '')}' has no settings, skipping...")
|
|
102
142
|
continue
|
|
103
|
-
tool['settings']['alita'] = alita
|
|
104
|
-
tool['settings']['llm'] = llm
|
|
105
|
-
tool['settings']['store'] = store
|
|
106
|
-
tool_type = tool['type']
|
|
107
143
|
|
|
108
|
-
#
|
|
109
|
-
|
|
110
|
-
|
|
144
|
+
# Validate tool names once
|
|
145
|
+
selected_tools = settings.get('selected_tools', [])
|
|
146
|
+
invalid_tools = [name for name in selected_tools if isinstance(name, str) and name.startswith('_')]
|
|
147
|
+
if invalid_tools:
|
|
148
|
+
raise ValueError(f"Tool names {invalid_tools} from toolkit '{tool.get('type', '')}' cannot start with '_'")
|
|
111
149
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
150
|
+
# Cache tool type and add common settings
|
|
151
|
+
tool_type = tool['type']
|
|
152
|
+
settings['alita'] = alita
|
|
153
|
+
settings['llm'] = llm
|
|
154
|
+
settings['store'] = store
|
|
117
155
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
156
|
+
# Set pgvector collection schema if present
|
|
157
|
+
if settings.get('pgvector_configuration'):
|
|
158
|
+
# Use tool id if available, otherwise use toolkit_name or type as fallback
|
|
159
|
+
collection_id = tool.get('id') or tool.get('toolkit_name') or tool_type
|
|
160
|
+
settings['pgvector_configuration']['collection_schema'] = str(collection_id)
|
|
121
161
|
|
|
122
|
-
# Handle ADO
|
|
162
|
+
# Handle ADO special cases
|
|
163
|
+
if tool_type in ['ado_boards', 'ado_wiki', 'ado_plans']:
|
|
164
|
+
toolkit_tools.extend(AVAILABLE_TOOLS['ado']['get_tools'](tool_type, tool))
|
|
123
165
|
elif tool_type in ['ado_repos', 'azure_devops_repos'] and 'ado_repos' in AVAILABLE_TOOLS:
|
|
124
166
|
try:
|
|
125
|
-
|
|
126
|
-
tools.extend(get_tools_func(tool))
|
|
167
|
+
toolkit_tools.extend(AVAILABLE_TOOLS['ado_repos']['get_tools'](tool))
|
|
127
168
|
except Exception as e:
|
|
128
169
|
logger.error(f"Error getting ADO repos tools: {e}")
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
elif
|
|
170
|
+
elif tool_type == 'mcp':
|
|
171
|
+
logger.debug(f"Skipping MCP toolkit '{tool.get('toolkit_name')}' - handled by runtime toolkit system")
|
|
172
|
+
elif tool_type == 'planning':
|
|
173
|
+
logger.debug(f"Skipping planning toolkit '{tool.get('toolkit_name')}' - handled by runtime toolkit system")
|
|
174
|
+
elif tool_type in AVAILABLE_TOOLS and 'get_tools' in AVAILABLE_TOOLS[tool_type]:
|
|
175
|
+
try:
|
|
176
|
+
toolkit_tools.extend(AVAILABLE_TOOLS[tool_type]['get_tools'](tool))
|
|
177
|
+
except Exception as e:
|
|
178
|
+
logger.error(f"Error getting tools for {tool_type}: {e}")
|
|
179
|
+
raise ToolException(f"Error getting tools for {tool_type}: {e}")
|
|
180
|
+
elif settings.get("module"):
|
|
132
181
|
try:
|
|
133
|
-
settings = tool.get("settings", {})
|
|
134
182
|
mod = import_module(settings.pop("module"))
|
|
135
183
|
tkitclass = getattr(mod, settings.pop("class"))
|
|
136
|
-
|
|
137
|
-
get_toolkit_params = tool["settings"].copy()
|
|
184
|
+
get_toolkit_params = settings.copy()
|
|
138
185
|
get_toolkit_params["name"] = tool.get("name")
|
|
139
|
-
#
|
|
140
186
|
toolkit = tkitclass.get_toolkit(**get_toolkit_params)
|
|
141
|
-
|
|
187
|
+
toolkit_tools.extend(toolkit.get_tools())
|
|
142
188
|
except Exception as e:
|
|
189
|
+
import traceback
|
|
143
190
|
logger.error(f"Error in getting custom toolkit: {e}")
|
|
144
|
-
|
|
191
|
+
logger.error(f"Traceback:\n{traceback.format_exc()}")
|
|
145
192
|
else:
|
|
146
|
-
# Tool not available or not found
|
|
147
193
|
if tool_type in FAILED_IMPORTS:
|
|
148
194
|
logger.warning(f"Tool '{tool_type}' is not available: {FAILED_IMPORTS[tool_type]}")
|
|
149
195
|
else:
|
|
150
196
|
logger.warning(f"Unknown tool type: {tool_type}")
|
|
197
|
+
#
|
|
198
|
+
# Always inject toolkit_id to each tool
|
|
199
|
+
_inject_toolkit_id(tool, toolkit_tools)
|
|
200
|
+
tools.extend(toolkit_tools)
|
|
151
201
|
|
|
152
202
|
return tools
|
|
153
203
|
|
|
@@ -167,6 +217,18 @@ def get_toolkits():
|
|
|
167
217
|
logger.info(f"Successfully loaded {len(toolkit_configs)} toolkit configurations")
|
|
168
218
|
return toolkit_configs
|
|
169
219
|
|
|
220
|
+
def instantiate_toolkit(tool_config):
|
|
221
|
+
"""Instantiate a toolkit from its configuration."""
|
|
222
|
+
tool_type = tool_config.get('type')
|
|
223
|
+
|
|
224
|
+
if tool_type in AVAILABLE_TOOLS:
|
|
225
|
+
tool_module = AVAILABLE_TOOLS[tool_type]
|
|
226
|
+
|
|
227
|
+
if 'get_toolkit' in tool_module:
|
|
228
|
+
return tool_module['get_toolkit'](tool_config)
|
|
229
|
+
|
|
230
|
+
raise ValueError(f"Toolkit type '{tool_type}' does not support direct instantiation or is not available.")
|
|
231
|
+
|
|
170
232
|
def get_available_tools():
|
|
171
233
|
"""Return list of available tool types."""
|
|
172
234
|
return list(AVAILABLE_TOOLS.keys())
|
|
@@ -183,6 +245,42 @@ def get_available_toolkit_models():
|
|
|
183
245
|
"""Return dict with available toolkit classes."""
|
|
184
246
|
return deepcopy(AVAILABLE_TOOLS)
|
|
185
247
|
|
|
248
|
+
|
|
249
|
+
def get_toolkit_available_tools(toolkit_type: str, settings: dict) -> dict:
|
|
250
|
+
"""Return dynamic available tools + per-tool JSON schemas for a toolkit instance.
|
|
251
|
+
|
|
252
|
+
This is the single SDK entrypoint used by backend services (e.g. indexer_worker)
|
|
253
|
+
when the UI needs spec/instance-dependent tool enumeration. Toolkits that don't
|
|
254
|
+
support dynamic enumeration should return an empty payload.
|
|
255
|
+
|
|
256
|
+
Args:
|
|
257
|
+
toolkit_type: toolkit type string (e.g. 'openapi')
|
|
258
|
+
settings: persisted toolkit settings
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
{
|
|
262
|
+
"tools": [{"name": str, "description": str}],
|
|
263
|
+
"args_schemas": {"tool_name": <json schema dict>}
|
|
264
|
+
}
|
|
265
|
+
"""
|
|
266
|
+
toolkit_type = (toolkit_type or '').strip().lower()
|
|
267
|
+
if not isinstance(settings, dict):
|
|
268
|
+
settings = {}
|
|
269
|
+
|
|
270
|
+
tool_module = AVAILABLE_TOOLS.get(toolkit_type) or {}
|
|
271
|
+
enumerator = tool_module.get('get_toolkit_available_tools')
|
|
272
|
+
if not callable(enumerator):
|
|
273
|
+
return {"tools": [], "args_schemas": {}}
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
result = enumerator(settings)
|
|
277
|
+
if not isinstance(result, dict):
|
|
278
|
+
return {"tools": [], "args_schemas": {}, "error": "Invalid response from toolkit enumerator"}
|
|
279
|
+
return result
|
|
280
|
+
except Exception as e: # pylint: disable=W0718
|
|
281
|
+
logger.exception("Failed to compute available tools for toolkit_type=%s", toolkit_type)
|
|
282
|
+
return {"tools": [], "args_schemas": {}, "error": str(e)}
|
|
283
|
+
|
|
186
284
|
def diagnose_imports():
|
|
187
285
|
"""Print diagnostic information about tool imports."""
|
|
188
286
|
available_count = len(AVAILABLE_TOOLS)
|
|
@@ -219,6 +317,7 @@ def diagnose_imports():
|
|
|
219
317
|
__all__ = [
|
|
220
318
|
'get_tools',
|
|
221
319
|
'get_toolkits',
|
|
320
|
+
'get_toolkit_available_tools',
|
|
222
321
|
'get_available_tools',
|
|
223
322
|
'get_failed_imports',
|
|
224
323
|
'get_available_toolkits',
|