alita-sdk 0.3.486__py3-none-any.whl → 0.3.497__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/agent_loader.py +27 -6
- alita_sdk/cli/agents.py +10 -1
- alita_sdk/cli/tools/filesystem.py +95 -9
- alita_sdk/runtime/clients/client.py +40 -21
- alita_sdk/runtime/langchain/constants.py +3 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
- alita_sdk/runtime/langchain/document_loaders/constants.py +10 -6
- alita_sdk/runtime/langchain/langraph_agent.py +2 -1
- alita_sdk/runtime/toolkits/mcp.py +68 -62
- alita_sdk/runtime/toolkits/planning.py +3 -1
- alita_sdk/runtime/toolkits/tools.py +37 -18
- alita_sdk/runtime/tools/artifact.py +46 -17
- alita_sdk/runtime/tools/function.py +2 -1
- alita_sdk/runtime/tools/llm.py +135 -24
- alita_sdk/runtime/tools/mcp_remote_tool.py +23 -7
- alita_sdk/runtime/tools/vectorstore_base.py +3 -3
- alita_sdk/runtime/utils/AlitaCallback.py +106 -20
- alita_sdk/runtime/utils/mcp_client.py +465 -0
- alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
- alita_sdk/runtime/utils/toolkit_utils.py +7 -13
- alita_sdk/tools/base_indexer_toolkit.py +1 -1
- alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
- alita_sdk/tools/chunkers/sematic/markdown_chunker.py +2 -0
- alita_sdk/tools/chunkers/universal_chunker.py +1 -0
- alita_sdk/tools/code/loaders/codesearcher.py +3 -2
- alita_sdk/tools/confluence/api_wrapper.py +63 -14
- alita_sdk/tools/sharepoint/api_wrapper.py +2 -2
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +16 -18
- {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.497.dist-info}/METADATA +1 -1
- {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.497.dist-info}/RECORD +34 -32
- {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.497.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.497.dist-info}/entry_points.txt +0 -0
- {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.497.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.486.dist-info → alita_sdk-0.3.497.dist-info}/top_level.txt +0 -0
|
@@ -20,10 +20,14 @@ from ..utils.mcp_oauth import (
|
|
|
20
20
|
fetch_resource_metadata_async,
|
|
21
21
|
infer_authorization_servers_from_realm,
|
|
22
22
|
)
|
|
23
|
-
from ..utils.
|
|
23
|
+
from ..utils.mcp_client import McpClient
|
|
24
24
|
|
|
25
25
|
logger = logging.getLogger(__name__)
|
|
26
26
|
|
|
27
|
+
# Global registry to store MCP tool session metadata by tool name
|
|
28
|
+
# This is used to pass session info to callbacks since LangChain's serialization doesn't include all fields
|
|
29
|
+
MCP_TOOL_SESSION_REGISTRY: Dict[str, Dict[str, Any]] = {}
|
|
30
|
+
|
|
27
31
|
|
|
28
32
|
class McpRemoteTool(McpServerTool):
|
|
29
33
|
"""
|
|
@@ -43,6 +47,7 @@ class McpRemoteTool(McpServerTool):
|
|
|
43
47
|
"""Update metadata with session info after model initialization."""
|
|
44
48
|
super().model_post_init(__context)
|
|
45
49
|
self._update_metadata_with_session()
|
|
50
|
+
self._register_session_metadata()
|
|
46
51
|
|
|
47
52
|
def _update_metadata_with_session(self):
|
|
48
53
|
"""Update the metadata dict with current session information."""
|
|
@@ -54,6 +59,15 @@ class McpRemoteTool(McpServerTool):
|
|
|
54
59
|
'mcp_server_url': canonical_resource(self.server_url)
|
|
55
60
|
})
|
|
56
61
|
|
|
62
|
+
def _register_session_metadata(self):
|
|
63
|
+
"""Register session metadata in global registry for callback access."""
|
|
64
|
+
if self.session_id and self.server_url:
|
|
65
|
+
MCP_TOOL_SESSION_REGISTRY[self.name] = {
|
|
66
|
+
'mcp_session_id': self.session_id,
|
|
67
|
+
'mcp_server_url': canonical_resource(self.server_url)
|
|
68
|
+
}
|
|
69
|
+
logger.debug(f"[MCP] Registered session metadata for tool '{self.name}': session={self.session_id}")
|
|
70
|
+
|
|
57
71
|
def __getstate__(self):
|
|
58
72
|
"""Custom serialization for pickle compatibility."""
|
|
59
73
|
state = super().__getstate__()
|
|
@@ -98,7 +112,7 @@ class McpRemoteTool(McpServerTool):
|
|
|
98
112
|
tool_name_for_server = self.name.rsplit(TOOLKIT_SPLITTER, 1)[-1] if TOOLKIT_SPLITTER in self.name else self.name
|
|
99
113
|
logger.warning(f"original_tool_name not set for '{self.name}', using extracted: {tool_name_for_server}")
|
|
100
114
|
|
|
101
|
-
logger.info(f"[MCP
|
|
115
|
+
logger.info(f"[MCP] Executing tool '{tool_name_for_server}' with session {self.session_id}")
|
|
102
116
|
|
|
103
117
|
try:
|
|
104
118
|
# Prepare headers
|
|
@@ -106,16 +120,18 @@ class McpRemoteTool(McpServerTool):
|
|
|
106
120
|
if self.server_headers:
|
|
107
121
|
headers.update(self.server_headers)
|
|
108
122
|
|
|
109
|
-
# Create
|
|
110
|
-
client =
|
|
123
|
+
# Create unified MCP client (auto-detects transport)
|
|
124
|
+
client = McpClient(
|
|
111
125
|
url=self.server_url,
|
|
112
126
|
session_id=self.session_id,
|
|
113
127
|
headers=headers,
|
|
114
128
|
timeout=self.tool_timeout_sec
|
|
115
129
|
)
|
|
116
130
|
|
|
117
|
-
# Execute tool call
|
|
118
|
-
|
|
131
|
+
# Execute tool call (client auto-detects SSE vs Streamable HTTP)
|
|
132
|
+
async with client:
|
|
133
|
+
await client.initialize()
|
|
134
|
+
result = await client.call_tool(tool_name_for_server, kwargs)
|
|
119
135
|
|
|
120
136
|
# Format the result
|
|
121
137
|
if isinstance(result, dict):
|
|
@@ -144,7 +160,7 @@ class McpRemoteTool(McpServerTool):
|
|
|
144
160
|
return str(result)
|
|
145
161
|
|
|
146
162
|
except Exception as e:
|
|
147
|
-
logger.error(f"[MCP
|
|
163
|
+
logger.error(f"[MCP] Tool execution failed: {e}", exc_info=True)
|
|
148
164
|
raise
|
|
149
165
|
|
|
150
166
|
def _parse_sse(self, text: str) -> Dict[str, Any]:
|
|
@@ -270,7 +270,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
|
|
|
270
270
|
)
|
|
271
271
|
).count()
|
|
272
272
|
|
|
273
|
-
def _clean_collection(self, index_name: str = ''):
|
|
273
|
+
def _clean_collection(self, index_name: str = '', including_index_meta: bool = False):
|
|
274
274
|
"""
|
|
275
275
|
Clean the vectorstore collection by deleting all indexed data.
|
|
276
276
|
"""
|
|
@@ -279,7 +279,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
|
|
|
279
279
|
f"Cleaning collection '{self.dataset}'",
|
|
280
280
|
tool_name="_clean_collection"
|
|
281
281
|
)
|
|
282
|
-
self.vector_adapter.clean_collection(self, index_name)
|
|
282
|
+
self.vector_adapter.clean_collection(self, index_name, including_index_meta)
|
|
283
283
|
self._log_tool_event(
|
|
284
284
|
f"Collection '{self.dataset}' has been cleaned. ",
|
|
285
285
|
tool_name="_clean_collection"
|
|
@@ -303,7 +303,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
|
|
|
303
303
|
logger.info("Cleaning index before re-indexing all documents.")
|
|
304
304
|
self._log_tool_event("Cleaning index before re-indexing all documents. Previous index will be removed", tool_name="index_documents")
|
|
305
305
|
try:
|
|
306
|
-
self._clean_collection(index_name)
|
|
306
|
+
self._clean_collection(index_name, including_index_meta=False)
|
|
307
307
|
self._log_tool_event("Previous index has been removed",
|
|
308
308
|
tool_name="index_documents")
|
|
309
309
|
except Exception as e:
|
|
@@ -23,9 +23,45 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
|
|
|
23
23
|
self.tokens_out = 0
|
|
24
24
|
self.pending_llm_requests = defaultdict(int)
|
|
25
25
|
self.current_model_name = 'gpt-4'
|
|
26
|
+
self._event_queue = [] # Queue for events when context is unavailable
|
|
26
27
|
#
|
|
27
28
|
super().__init__()
|
|
28
29
|
|
|
30
|
+
def _has_streamlit_context(self) -> bool:
|
|
31
|
+
"""Check if Streamlit context is available in the current thread."""
|
|
32
|
+
try:
|
|
33
|
+
# Try to import streamlit runtime context checker
|
|
34
|
+
from streamlit.runtime.scriptrunner import get_script_run_ctx
|
|
35
|
+
ctx = get_script_run_ctx()
|
|
36
|
+
return ctx is not None
|
|
37
|
+
except (ImportError, Exception) as e:
|
|
38
|
+
if self.debug:
|
|
39
|
+
log.debug(f"Streamlit context check failed: {e}")
|
|
40
|
+
return False
|
|
41
|
+
|
|
42
|
+
def _safe_streamlit_call(self, func, *args, **kwargs):
|
|
43
|
+
"""Safely execute a Streamlit UI operation, handling missing context gracefully."""
|
|
44
|
+
if not self._has_streamlit_context():
|
|
45
|
+
func_name = getattr(func, '__name__', str(func))
|
|
46
|
+
if self.debug:
|
|
47
|
+
log.warning(f"Streamlit context not available for {func_name}, queueing event")
|
|
48
|
+
# Store the event for potential replay when context is available
|
|
49
|
+
self._event_queue.append({
|
|
50
|
+
'func': func_name,
|
|
51
|
+
'args': args,
|
|
52
|
+
'kwargs': kwargs,
|
|
53
|
+
'timestamp': datetime.now(tz=timezone.utc)
|
|
54
|
+
})
|
|
55
|
+
return None
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
return func(*args, **kwargs)
|
|
59
|
+
except Exception as e:
|
|
60
|
+
func_name = getattr(func, '__name__', str(func))
|
|
61
|
+
# Handle any Streamlit-specific exceptions gracefully
|
|
62
|
+
log.warning(f"Streamlit operation {func_name} failed: {e}")
|
|
63
|
+
return None
|
|
64
|
+
|
|
29
65
|
#
|
|
30
66
|
# Chain
|
|
31
67
|
#
|
|
@@ -76,10 +112,14 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
|
|
|
76
112
|
json.dumps(payload, ensure_ascii=False, default=lambda o: str(o))
|
|
77
113
|
)
|
|
78
114
|
|
|
79
|
-
|
|
80
|
-
|
|
115
|
+
status_widget = self._safe_streamlit_call(
|
|
116
|
+
self.st.status,
|
|
117
|
+
f"Running {payload.get('tool_name')}...",
|
|
118
|
+
expanded=True
|
|
81
119
|
)
|
|
82
|
-
|
|
120
|
+
if status_widget:
|
|
121
|
+
self.callback_state[str(run_id)] = status_widget
|
|
122
|
+
self._safe_streamlit_call(status_widget.write, f"Tool inputs: {payload}")
|
|
83
123
|
|
|
84
124
|
def on_tool_start(self, *args, run_id: UUID, **kwargs):
|
|
85
125
|
""" Callback """
|
|
@@ -95,8 +135,15 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
|
|
|
95
135
|
"tool_inputs": kwargs.get('inputs')
|
|
96
136
|
}
|
|
97
137
|
payload = json.loads(json.dumps(payload, ensure_ascii=False, default=lambda o: str(o)))
|
|
98
|
-
|
|
99
|
-
|
|
138
|
+
|
|
139
|
+
status_widget = self._safe_streamlit_call(
|
|
140
|
+
self.st.status,
|
|
141
|
+
f"Running {tool_name}...",
|
|
142
|
+
expanded=True
|
|
143
|
+
)
|
|
144
|
+
if status_widget:
|
|
145
|
+
self.callback_state[tool_run_id] = status_widget
|
|
146
|
+
self._safe_streamlit_call(status_widget.write, f"Tool inputs: {kwargs.get('inputs')}")
|
|
100
147
|
|
|
101
148
|
def on_tool_end(self, *args, run_id: UUID, **kwargs):
|
|
102
149
|
""" Callback """
|
|
@@ -104,11 +151,16 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
|
|
|
104
151
|
log.info("on_tool_end(%s, %s)", args, kwargs)
|
|
105
152
|
tool_run_id = str(run_id)
|
|
106
153
|
tool_output = args[0]
|
|
107
|
-
if self.callback_state
|
|
108
|
-
self.callback_state[tool_run_id]
|
|
109
|
-
self.
|
|
154
|
+
if self.callback_state.get(tool_run_id):
|
|
155
|
+
status_widget = self.callback_state[tool_run_id]
|
|
156
|
+
self._safe_streamlit_call(status_widget.write, f"Tool output: {tool_output}")
|
|
157
|
+
self._safe_streamlit_call(
|
|
158
|
+
status_widget.update,
|
|
159
|
+
label=f"Completed {kwargs.get('name')}",
|
|
160
|
+
state="complete",
|
|
161
|
+
expanded=False
|
|
162
|
+
)
|
|
110
163
|
self.callback_state.pop(tool_run_id, None)
|
|
111
|
-
del self.callback_state[run_id]
|
|
112
164
|
|
|
113
165
|
def on_tool_error(self, *args, run_id: UUID, **kwargs):
|
|
114
166
|
""" Callback """
|
|
@@ -116,9 +168,19 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
|
|
|
116
168
|
log.info("on_tool_error(%s, %s)", args, kwargs)
|
|
117
169
|
tool_run_id = str(run_id)
|
|
118
170
|
tool_exception = args[0]
|
|
119
|
-
self.callback_state
|
|
120
|
-
|
|
121
|
-
|
|
171
|
+
if self.callback_state.get(tool_run_id):
|
|
172
|
+
status_widget = self.callback_state[tool_run_id]
|
|
173
|
+
self._safe_streamlit_call(
|
|
174
|
+
status_widget.write,
|
|
175
|
+
f"{traceback.format_exception(tool_exception)}"
|
|
176
|
+
)
|
|
177
|
+
self._safe_streamlit_call(
|
|
178
|
+
status_widget.update,
|
|
179
|
+
label=f"Error {kwargs.get('name')}",
|
|
180
|
+
state="error",
|
|
181
|
+
expanded=False
|
|
182
|
+
)
|
|
183
|
+
self.callback_state.pop(tool_run_id, None)
|
|
122
184
|
|
|
123
185
|
#
|
|
124
186
|
# Agent
|
|
@@ -156,8 +218,14 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
|
|
|
156
218
|
self.current_model_name = metadata.get('ls_model_name', self.current_model_name)
|
|
157
219
|
llm_run_id = str(run_id)
|
|
158
220
|
|
|
159
|
-
|
|
160
|
-
|
|
221
|
+
status_widget = self._safe_streamlit_call(
|
|
222
|
+
self.st.status,
|
|
223
|
+
f"Running LLM ...",
|
|
224
|
+
expanded=True
|
|
225
|
+
)
|
|
226
|
+
if status_widget:
|
|
227
|
+
self.callback_state[llm_run_id] = status_widget
|
|
228
|
+
self._safe_streamlit_call(status_widget.write, f"LLM inputs: {messages}")
|
|
161
229
|
|
|
162
230
|
def on_llm_start(self, *args, **kwargs):
|
|
163
231
|
""" Callback """
|
|
@@ -178,16 +246,27 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
|
|
|
178
246
|
content = None
|
|
179
247
|
if chunk:
|
|
180
248
|
content = chunk.text
|
|
181
|
-
|
|
249
|
+
|
|
250
|
+
llm_run_id = str(run_id)
|
|
251
|
+
if self.callback_state.get(llm_run_id):
|
|
252
|
+
status_widget = self.callback_state[llm_run_id]
|
|
253
|
+
self._safe_streamlit_call(status_widget.write, content)
|
|
182
254
|
|
|
183
255
|
def on_llm_error(self, *args, run_id: UUID, **kwargs):
|
|
184
256
|
""" Callback """
|
|
185
257
|
if self.debug:
|
|
186
258
|
log.error("on_llm_error(%s, %s)", args, kwargs)
|
|
187
259
|
llm_run_id = str(run_id)
|
|
188
|
-
self.callback_state
|
|
189
|
-
|
|
190
|
-
|
|
260
|
+
if self.callback_state.get(llm_run_id):
|
|
261
|
+
status_widget = self.callback_state[llm_run_id]
|
|
262
|
+
self._safe_streamlit_call(status_widget.write, f"on_llm_error({args}, {kwargs})")
|
|
263
|
+
self._safe_streamlit_call(
|
|
264
|
+
status_widget.update,
|
|
265
|
+
label=f"Error {kwargs.get('name')}",
|
|
266
|
+
state="error",
|
|
267
|
+
expanded=False
|
|
268
|
+
)
|
|
269
|
+
self.callback_state.pop(llm_run_id, None)
|
|
191
270
|
#
|
|
192
271
|
# exception = args[0]
|
|
193
272
|
# FIXME: should we emit an error here too?
|
|
@@ -205,5 +284,12 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
|
|
|
205
284
|
if self.debug:
|
|
206
285
|
log.debug("on_llm_end(%s, %s)", response, kwargs)
|
|
207
286
|
llm_run_id = str(run_id)
|
|
208
|
-
self.callback_state
|
|
209
|
-
|
|
287
|
+
if self.callback_state.get(llm_run_id):
|
|
288
|
+
status_widget = self.callback_state[llm_run_id]
|
|
289
|
+
self._safe_streamlit_call(
|
|
290
|
+
status_widget.update,
|
|
291
|
+
label=f"Completed LLM call",
|
|
292
|
+
state="complete",
|
|
293
|
+
expanded=False
|
|
294
|
+
)
|
|
295
|
+
self.callback_state.pop(llm_run_id, None)
|