alita-sdk 0.3.206__py3-none-any.whl → 0.3.208__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.
Files changed (26) hide show
  1. alita_sdk/runtime/clients/client.py +369 -6
  2. alita_sdk/runtime/langchain/langraph_agent.py +6 -1
  3. alita_sdk/runtime/langchain/store_manager.py +4 -4
  4. alita_sdk/runtime/toolkits/tools.py +11 -20
  5. alita_sdk/runtime/utils/streamlit.py +472 -192
  6. alita_sdk/runtime/utils/toolkit_runtime.py +147 -0
  7. alita_sdk/runtime/utils/toolkit_utils.py +157 -0
  8. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +82 -11
  9. alita_sdk/tools/ado/wiki/ado_wrapper.py +62 -2
  10. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +2 -1
  11. alita_sdk/tools/memory/__init__.py +54 -10
  12. alita_sdk/tools/sharepoint/api_wrapper.py +13 -4
  13. {alita_sdk-0.3.206.dist-info → alita_sdk-0.3.208.dist-info}/METADATA +1 -1
  14. {alita_sdk-0.3.206.dist-info → alita_sdk-0.3.208.dist-info}/RECORD +17 -24
  15. alita_sdk/community/analysis/__init__.py +0 -0
  16. alita_sdk/community/analysis/ado_analyse/__init__.py +0 -103
  17. alita_sdk/community/analysis/ado_analyse/api_wrapper.py +0 -261
  18. alita_sdk/community/analysis/github_analyse/__init__.py +0 -98
  19. alita_sdk/community/analysis/github_analyse/api_wrapper.py +0 -166
  20. alita_sdk/community/analysis/gitlab_analyse/__init__.py +0 -110
  21. alita_sdk/community/analysis/gitlab_analyse/api_wrapper.py +0 -172
  22. alita_sdk/community/analysis/jira_analyse/__init__.py +0 -141
  23. alita_sdk/community/analysis/jira_analyse/api_wrapper.py +0 -252
  24. {alita_sdk-0.3.206.dist-info → alita_sdk-0.3.208.dist-info}/WHEEL +0 -0
  25. {alita_sdk-0.3.206.dist-info → alita_sdk-0.3.208.dist-info}/licenses/LICENSE +0 -0
  26. {alita_sdk-0.3.206.dist-info → alita_sdk-0.3.208.dist-info}/top_level.txt +0 -0
@@ -256,45 +256,55 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
256
256
 
257
257
  return config
258
258
 
259
- def instantiate_toolkit(toolkit_name, toolkit_config, llm_client):
260
- """Helper function to instantiate a toolkit based on its configuration"""
259
+ def instantiate_toolkit(toolkit_config):
260
+ """
261
+ Helper function to instantiate a toolkit based on its configuration.
262
+ This function now delegates to the toolkit_utils module for the actual implementation.
263
+ """
261
264
  try:
262
- # Log the configuration being used
263
- logger.info(f"Instantiating toolkit {toolkit_name} with config: {json.dumps(toolkit_config, indent=2)}")
265
+ from .toolkit_utils import instantiate_toolkit_with_client
264
266
 
265
- # Validate LLM client
266
- if not llm_client:
267
- raise ValueError("LLM client is required but not provided")
267
+ # Extract toolkit name and settings from the old format
268
+ toolkit_name = toolkit_config.get('toolkit_name')
269
+ settings = toolkit_config.get('settings', {})
268
270
 
269
- # Find the toolkit schema
270
- toolkit_schema = None
271
- for config in st.session_state.tooklit_configs:
272
- if config['title'] == toolkit_name:
273
- toolkit_schema = config
274
- break
275
-
276
- if not toolkit_schema:
277
- raise ValueError(f"Toolkit {toolkit_name} not found")
271
+ # Inject project secrets into configuration
272
+ enhanced_settings = inject_project_secrets(settings)
278
273
 
279
- # Inject project secrets into configuration
280
- enhanced_config = inject_project_secrets(toolkit_config)
281
- logger.info(f"Enhanced configuration for {toolkit_name}: {json.dumps(enhanced_config, indent=2)}")
282
- # Use the get_tools function from toolkits to instantiate the toolkit
283
- # Create a tool configuration dict with required ID field
284
- tool_config = {
285
- 'id': random.randint(1, 1000000), # Required random integer ID
286
- 'type': toolkit_name.lower(),
287
- 'settings': enhanced_config,
288
- 'toolkit_name': toolkit_name
274
+ # Create the new format configuration
275
+ new_config = {
276
+ 'toolkit_name': toolkit_name,
277
+ 'settings': enhanced_settings
289
278
  }
290
279
 
291
- # Import get_tools dynamically
292
- tools = get_tools([tool_config], llm_client.client, llm_client)
280
+ # Create a basic LLM client for toolkit instantiation
281
+ try:
282
+ if not st.session_state.client:
283
+ raise ValueError("Alita client not available")
284
+
285
+ llm_client = st.session_state.client.get_llm(
286
+ model_name="gpt-4o-mini",
287
+ model_config={
288
+ "temperature": 0.1,
289
+ "max_tokens": 1000,
290
+ "top_p": 1.0
291
+ }
292
+ )
293
+ except Exception as e:
294
+ logger.warning(f"Failed to create LLM client: {str(e)}. Falling back to basic toolkit instantiation.")
295
+ # Fallback to basic instantiation
296
+ from .toolkit_utils import instantiate_toolkit as fallback_instantiate
297
+ return fallback_instantiate(new_config)
293
298
 
294
- return tools
299
+ # Use the enhanced implementation with client support
300
+ return instantiate_toolkit_with_client(
301
+ new_config,
302
+ llm_client,
303
+ st.session_state.client
304
+ )
295
305
 
296
306
  except Exception as e:
297
- logger.error(f"Error instantiating toolkit {toolkit_name}: {str(e)}")
307
+ logger.error(f"Error instantiating toolkit {toolkit_config.get('toolkit_name')}: {str(e)}")
298
308
  raise
299
309
 
300
310
  st.set_page_config(
@@ -905,12 +915,61 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
905
915
  elif default_value:
906
916
  array_value = str(default_value)
907
917
 
908
- array_input = st.text_area(
909
- f"{label} (one per line)",
910
- value=array_value,
911
- help=f"{field_description} - Enter one item per line",
912
- key=f"config_{field_name}_{selected_toolkit_idx}"
913
- )
918
+ # Auto-populate selected_tools with all available tools
919
+ if field_name == 'selected_tools':
920
+ # Get available tools from the schema's json_schema_extra
921
+ args_schemas = field_schema.get('json_schema_extra', {}).get('args_schemas', {})
922
+ if args_schemas:
923
+ available_tools = list(args_schemas.keys())
924
+
925
+ # Create a session state key for this toolkit's auto-population
926
+ auto_populate_key = f"auto_populate_tools_{toolkit_schema['title']}_{selected_toolkit_idx}"
927
+
928
+ # Auto-populate if field is empty and not already auto-populated
929
+ if not array_value and auto_populate_key not in st.session_state:
930
+ array_value = '\n'.join(available_tools)
931
+ st.session_state[auto_populate_key] = True
932
+ st.success(f"🔧 **Auto-populated {len(available_tools)} tools:** {', '.join(available_tools)}")
933
+ elif array_value and auto_populate_key in st.session_state:
934
+ # Show info about existing auto-population
935
+ current_tools = [line.strip() for line in array_value.split('\n') if line.strip()]
936
+ st.info(f"📋 **{len(current_tools)} tools configured** (auto-populated: {len(available_tools)} available)")
937
+
938
+ # Add a button to reset to all tools
939
+ col1, col2 = st.columns([3, 1])
940
+ with col2:
941
+ if st.button("📋 Load All Tools", help="Auto-populate with all available tools", key=f"load_all_tools_{selected_toolkit_idx}"):
942
+ # Update the session state to trigger rerun with populated tools
943
+ st.session_state[f"tools_loaded_{selected_toolkit_idx}"] = '\n'.join(available_tools)
944
+ st.success(f"✅ Loaded {len(available_tools)} tools")
945
+ st.rerun()
946
+
947
+ # Check if tools were just loaded via button
948
+ if f"tools_loaded_{selected_toolkit_idx}" in st.session_state:
949
+ array_value = st.session_state[f"tools_loaded_{selected_toolkit_idx}"]
950
+ del st.session_state[f"tools_loaded_{selected_toolkit_idx}"] # Clean up
951
+
952
+ with col1:
953
+ array_input = st.text_area(
954
+ f"{label} (one per line)",
955
+ value=array_value,
956
+ help=f"{field_description} - Enter one item per line. Available tools: {', '.join(available_tools)}",
957
+ key=f"config_{field_name}_{selected_toolkit_idx}"
958
+ )
959
+ else:
960
+ array_input = st.text_area(
961
+ f"{label} (one per line)",
962
+ value=array_value,
963
+ help=f"{field_description} - Enter one item per line",
964
+ key=f"config_{field_name}_{selected_toolkit_idx}"
965
+ )
966
+ else:
967
+ array_input = st.text_area(
968
+ f"{label} (one per line)",
969
+ value=array_value,
970
+ help=f"{field_description} - Enter one item per line",
971
+ key=f"config_{field_name}_{selected_toolkit_idx}"
972
+ )
914
973
  toolkit_config_values[field_name] = [line.strip() for line in array_input.split('\n') if line.strip()]
915
974
  else:
916
975
  st.info("This toolkit doesn't require additional configuration.")
@@ -988,7 +1047,11 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
988
1047
  toolkit_name = toolkit_schema['title']
989
1048
 
990
1049
  # Test with current config
991
- tools = instantiate_toolkit(toolkit_name, toolkit_config_values, st.session_state.llm)
1050
+ toolkit_test_config = {
1051
+ 'toolkit_name': toolkit_name,
1052
+ 'settings': toolkit_config_values
1053
+ }
1054
+ tools = instantiate_toolkit(toolkit_test_config)
992
1055
  st.success("✅ Connection test successful!")
993
1056
 
994
1057
  except Exception as e:
@@ -1050,6 +1113,41 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
1050
1113
  # Toolkit Testing Main View
1051
1114
  st.title("🚀 Toolkit Testing Interface")
1052
1115
 
1116
+ # Add info about the new testing capabilities
1117
+ st.info("""
1118
+ 🔥 **Enhanced Testing Features:**
1119
+ - **Event Tracking**: Monitor custom events dispatched during tool execution
1120
+ - **Callback Support**: Full runtime callback support for real-time monitoring
1121
+ - **Error Handling**: Detailed error reporting with execution context
1122
+ - **Client Integration**: Uses the same method available in the API client
1123
+ """)
1124
+
1125
+ # Sidebar with testing information
1126
+ with st.sidebar:
1127
+ st.markdown("### 🔧 Testing Information")
1128
+ st.markdown("""
1129
+ **Current Method**: `client.test_toolkit_tool()`
1130
+
1131
+ **Features**:
1132
+ - ✅ Runtime callbacks
1133
+ - ✅ Event dispatching
1134
+ - ✅ Error handling
1135
+ - ✅ Configuration validation
1136
+
1137
+ **API Usage**:
1138
+ ```python
1139
+ result = client.test_toolkit_tool(
1140
+ toolkit_config={
1141
+ 'toolkit_name': 'github',
1142
+ 'settings': {'token': '...'}
1143
+ },
1144
+ tool_name='get_repo',
1145
+ tool_params={'repo': 'alita'},
1146
+ runtime_config={'callbacks': [cb]}
1147
+ )
1148
+ ```
1149
+ """)
1150
+
1053
1151
  toolkit_config = st.session_state.configured_toolkit
1054
1152
 
1055
1153
  # Header with toolkit info and navigation
@@ -1091,185 +1189,367 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
1091
1189
  st.write("**Original Agent Configuration:**")
1092
1190
  st.json(agent_context['original_agent_config'])
1093
1191
 
1094
- # Test mode selection in main view
1192
+ # Test mode selection in main view - simplified to function mode only
1095
1193
  st.markdown("---")
1096
- st.subheader("📋 Step 2: Choose Testing Mode")
1194
+ st.subheader("📋 Step 2: Function Testing Mode")
1097
1195
 
1098
- test_mode = st.radio(
1099
- "**Select your testing approach:**",
1100
- ["🤖 With LLM (Tool Mode)", "⚡ Without LLM (Function Mode)"],
1101
- help="Tool Mode: Let AI decide which functions to call. Function Mode: Call specific functions directly.",
1102
- horizontal=True
1103
- )
1196
+ # Force to function mode only to avoid client dependency issues
1197
+ test_mode = " Without LLM (Function Mode)"
1198
+ st.info("🔧 **Function Mode:** Call specific toolkit functions directly with custom parameters.")
1104
1199
 
1105
1200
  st.markdown("---")
1106
1201
 
1107
- if test_mode == "🤖 With LLM (Tool Mode)":
1108
- st.markdown("### 🤖 AI-Assisted Toolkit Testing")
1109
- st.info("💡 **Tip:** Describe what you want to accomplish, and the AI will use the appropriate toolkit functions to help you.")
1110
-
1111
- test_prompt = st.text_area(
1112
- "Enter your test prompt:",
1113
- placeholder="Example: 'Get a list of all projects' or 'Create a new test case with title \"Login Test\"'",
1114
- height=120,
1115
- key="llm_test_prompt"
1116
- )
1117
-
1118
- col1, col2 = st.columns([3, 1])
1119
- with col1:
1120
- run_llm = st.button("🚀 Run with LLM", type="primary", key="run_llm_main")
1121
- with col2:
1122
- clear_prompt = st.button("🗑️ Clear", key="clear_llm_main")
1123
- if clear_prompt:
1124
- st.rerun()
1125
-
1126
- if run_llm:
1127
- if not test_prompt.strip():
1128
- st.warning("⚠️ Please enter a test prompt.")
1129
- else:
1130
- with st.spinner("🔄 AI is working with your toolkit..."):
1131
- try:
1132
- tools = instantiate_toolkit(
1133
- toolkit_config['name'],
1134
- toolkit_config['config'],
1135
- st.session_state.llm
1136
- )
1137
-
1138
- # Create a simple agent with the tools
1139
- try:
1140
- # Try to use OpenAI functions agent if available
1141
- from langchain.agents import create_openai_functions_agent, AgentExecutor
1142
- from langchain.prompts import ChatPromptTemplate
1143
-
1144
- # Create a prompt for the toolkit testing
1145
- prompt = ChatPromptTemplate.from_messages([
1146
- ("system", f"You are a helpful assistant with access to {toolkit_config['name']} toolkit functions. Use the available tools to help the user accomplish their task. Always explain what you're doing and provide clear results."),
1147
- ("human", "{input}"),
1148
- ("placeholder", "{agent_scratchpad}"),
1149
- ])
1150
-
1151
- agent = create_openai_functions_agent(st.session_state.llm, tools, prompt)
1152
- agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
1153
-
1154
- result = agent_executor.invoke({"input": test_prompt})
1155
-
1156
- except ImportError:
1157
- # Fallback to direct LLM invocation with tool descriptions
1158
- tool_descriptions = "\n".join([f"- {tool.name}: {tool.description}" for tool in tools])
1159
- enhanced_prompt = f"""You have access to the following {toolkit_config['name']} toolkit functions:
1160
- {tool_descriptions}
1161
-
1162
- User request: {test_prompt}
1163
-
1164
- Please explain how you would use these tools to help the user, even though I cannot directly execute them in this fallback mode."""
1165
-
1166
- result = {"output": st.session_state.llm.invoke(enhanced_prompt).content}
1167
-
1168
- st.markdown("### 🎯 AI Response:")
1169
- st.success(result["output"])
1170
-
1171
- # Show tool usage details if available
1172
- if "intermediate_steps" in result:
1173
- with st.expander("📋 Execution Details"):
1174
- st.json(result)
1175
-
1176
- except Exception as e:
1177
- st.error(f"❌ Error running toolkit with LLM: {str(e)}")
1178
- with st.expander("🔍 Error Details"):
1179
- st.code(str(e))
1202
+ # Directly proceed to Function Mode (no LLM option)
1203
+ st.markdown("### Direct Function Testing")
1204
+
1205
+ # Information about the new testing method
1206
+ col1, col2 = st.columns([3, 1])
1207
+ with col1:
1208
+ st.info("💡 **Enhanced Testing:** Using `AlitaClient.test_toolkit_tool()` method with event capture and runtime callbacks.")
1209
+ with col2:
1210
+ st.markdown("**🔧 Method:** `test_toolkit_tool`")
1180
1211
 
1181
- else: # Without LLM (Function Mode)
1182
- st.markdown("### ⚡ Direct Function Testing")
1183
- st.info("💡 **Tip:** This mode lets you call specific toolkit functions directly with custom parameters.")
1212
+ # Show available functions
1213
+ try:
1214
+ # Use the client to get toolkit tools for display
1215
+ # We'll call the toolkit utilities directly to get tools
1216
+ from .toolkit_utils import instantiate_toolkit_with_client
1184
1217
 
1185
- # Show available functions
1218
+ # Create a simple LLM client for tool instantiation
1186
1219
  try:
1187
- tools = instantiate_toolkit(
1188
- toolkit_config['name'],
1189
- toolkit_config['config'],
1190
- st.session_state.llm
1220
+ if not st.session_state.client:
1221
+ raise ValueError("Alita client not available")
1222
+
1223
+ llm_client = st.session_state.client.get_llm(
1224
+ model_name="gpt-4o-mini",
1225
+ model_config={
1226
+ "temperature": 0.1,
1227
+ "max_tokens": 1000,
1228
+ "top_p": 1.0
1229
+ }
1191
1230
  )
1231
+ except Exception as e:
1232
+ logger.warning(f"Failed to create LLM client for toolkit instantiation: {str(e)}. Falling back to basic mode.")
1233
+ # Fallback to basic instantiation
1234
+ from .toolkit_utils import instantiate_toolkit as fallback_instantiate
1235
+ toolkit_test_config = {
1236
+ 'toolkit_name': toolkit_config['name'],
1237
+ 'settings': toolkit_config['config']
1238
+ }
1239
+ tools = fallback_instantiate(toolkit_test_config)
1240
+ else:
1241
+ toolkit_test_config = {
1242
+ 'toolkit_name': toolkit_config['name'],
1243
+ 'settings': toolkit_config['config']
1244
+ }
1245
+ tools = instantiate_toolkit_with_client(
1246
+ toolkit_test_config,
1247
+ llm_client,
1248
+ st.session_state.client
1249
+ )
1250
+
1251
+ if tools:
1252
+ st.markdown("### 📚 Available Functions:")
1253
+ st.info("🔧 **Auto-Population Enabled:** All available tools are automatically selected when you configure a toolkit. You can modify the selection below.")
1254
+ function_names = [tool.name for tool in tools]
1192
1255
 
1193
- if tools:
1194
- st.markdown("### 📚 Available Functions:")
1195
- function_names = [tool.name for tool in tools]
1196
-
1197
- # Create function selection with details
1256
+ # Auto-populate selected tools with all available tools
1257
+ if f"selected_tools_{toolkit_config['name']}" not in st.session_state:
1258
+ st.session_state[f"selected_tools_{toolkit_config['name']}"] = function_names
1259
+
1260
+ # Add controls for tool selection
1261
+ col1, col2, col3 = st.columns([3, 1, 1])
1262
+ with col1:
1263
+ st.markdown("**Tool Selection:**")
1264
+ with col2:
1265
+ if st.button("✅ Select All", help="Select all available tools", key=f"select_all_{toolkit_config['name']}"):
1266
+ st.session_state[f"selected_tools_{toolkit_config['name']}"] = function_names
1267
+ st.rerun()
1268
+ with col3:
1269
+ if st.button("❌ Clear All", help="Clear all selected tools", key=f"clear_all_{toolkit_config['name']}"):
1270
+ st.session_state[f"selected_tools_{toolkit_config['name']}"] = []
1271
+ st.rerun()
1272
+
1273
+ # Create multi-select for tools with auto-population
1274
+ selected_tools = st.multiselect(
1275
+ "Select tools to test:",
1276
+ function_names,
1277
+ default=st.session_state[f"selected_tools_{toolkit_config['name']}"],
1278
+ help="Choose the tools you want to test. All tools are selected by default.",
1279
+ key=f"tools_multiselect_{toolkit_config['name']}"
1280
+ )
1281
+
1282
+ # Update session state when selection changes
1283
+ st.session_state[f"selected_tools_{toolkit_config['name']}"] = selected_tools
1284
+
1285
+ # Show selection summary
1286
+ if selected_tools:
1287
+ st.success(f"✅ **{len(selected_tools)} of {len(function_names)} tools selected**")
1288
+ else:
1289
+ st.warning("⚠️ **No tools selected** - Please select at least one tool to proceed.")
1290
+
1291
+ # Create function selection dropdown from selected tools
1292
+ if selected_tools:
1198
1293
  selected_function = st.selectbox(
1199
- "Select a function:",
1200
- function_names,
1201
- help="Choose the function you want to test",
1294
+ "Select a function to configure and run:",
1295
+ selected_tools,
1296
+ help="Choose the specific function you want to configure and execute",
1202
1297
  key="function_selector_main"
1203
1298
  )
1299
+ else:
1300
+ st.warning("Please select at least one tool to proceed.")
1301
+ selected_function = None
1302
+
1303
+ if selected_function:
1304
+ selected_tool = next(tool for tool in tools if tool.name == selected_function)
1305
+
1306
+ # Function details
1307
+ col1, col2 = st.columns([2, 1])
1308
+ with col1:
1309
+ st.markdown(f"**📖 Description:** {selected_tool.description}")
1310
+ with col2:
1311
+ st.markdown(f"**🏷️ Function:** `{selected_function}`")
1312
+
1313
+ # Show function schema if available
1314
+ if hasattr(selected_tool, 'args_schema') and selected_tool.args_schema:
1315
+ with st.expander("📋 Function Schema", expanded=False):
1316
+ try:
1317
+ schema = selected_tool.args_schema.schema()
1318
+ st.json(schema)
1319
+ except:
1320
+ st.write("Schema not available")
1204
1321
 
1205
- if selected_function:
1206
- selected_tool = next(tool for tool in tools if tool.name == selected_function)
1322
+ # Function parameter form (instead of JSON input)
1323
+ st.markdown("---")
1324
+ with st.form("function_params_form", clear_on_submit=False):
1325
+ parameters = render_function_parameters_form(selected_tool, f"func_{selected_function}")
1207
1326
 
1208
- # Function details
1209
- col1, col2 = st.columns([2, 1])
1327
+ # LLM Configuration Section
1328
+ st.markdown("### 🤖 LLM Configuration")
1329
+ st.markdown("Configure the LLM settings for tools that require AI capabilities:")
1330
+
1331
+ col1, col2 = st.columns(2)
1210
1332
  with col1:
1211
- st.markdown(f"**📖 Description:** {selected_tool.description}")
1333
+ llm_model = st.selectbox(
1334
+ "LLM Model:",
1335
+ options=['gpt-4o-mini', 'gpt-4o', 'gpt-4', 'gpt-3.5-turbo', 'claude-3-haiku', 'claude-3-sonnet'],
1336
+ index=0,
1337
+ help="Select the LLM model to use for tools that require AI capabilities"
1338
+ )
1339
+
1340
+ temperature = st.slider(
1341
+ "Temperature:",
1342
+ min_value=0.0,
1343
+ max_value=1.0,
1344
+ value=0.1,
1345
+ step=0.1,
1346
+ help="Controls randomness in AI responses. Lower values are more deterministic."
1347
+ )
1348
+
1212
1349
  with col2:
1213
- st.markdown(f"**🏷️ Function:** `{selected_function}`")
1350
+ max_tokens = st.number_input(
1351
+ "Max Tokens:",
1352
+ min_value=100,
1353
+ max_value=4000,
1354
+ value=1000,
1355
+ step=100,
1356
+ help="Maximum number of tokens in the AI response"
1357
+ )
1358
+
1359
+ top_p = st.slider(
1360
+ "Top-p:",
1361
+ min_value=0.1,
1362
+ max_value=1.0,
1363
+ value=1.0,
1364
+ step=0.1,
1365
+ help="Controls diversity via nucleus sampling"
1366
+ )
1214
1367
 
1215
- # Show function schema if available
1216
- if hasattr(selected_tool, 'args_schema') and selected_tool.args_schema:
1217
- with st.expander("📋 Function Schema", expanded=False):
1218
- try:
1219
- schema = selected_tool.args_schema.schema()
1220
- st.json(schema)
1221
- except:
1222
- st.write("Schema not available")
1368
+ # Create LLM config
1369
+ llm_config = {
1370
+ 'max_tokens': max_tokens,
1371
+ 'temperature': temperature,
1372
+ 'top_p': top_p
1373
+ }
1223
1374
 
1224
- # Function parameter form (instead of JSON input)
1225
- st.markdown("---")
1226
- with st.form("function_params_form", clear_on_submit=False):
1227
- parameters = render_function_parameters_form(selected_tool, f"func_{selected_function}")
1228
-
1229
- col1, col2 = st.columns([3, 1])
1230
- with col1:
1231
- run_function = st.form_submit_button("⚡ Run Function", type="primary")
1232
- with col2:
1233
- clear_params = st.form_submit_button("🗑️ Clear Form")
1234
- if clear_params:
1235
- st.rerun()
1236
-
1237
- if run_function and parameters is not None:
1238
- with st.spinner("⚡ Executing function..."):
1239
- try:
1240
- result = selected_tool.invoke(parameters)
1241
-
1242
- st.markdown("### 🎯 Function Result:")
1375
+ col1, col2 = st.columns([3, 1])
1376
+ with col1:
1377
+ run_function = st.form_submit_button("⚡ Run Function", type="primary")
1378
+ with col2:
1379
+ clear_params = st.form_submit_button("🗑️ Clear Form")
1380
+ if clear_params:
1381
+ st.rerun()
1382
+
1383
+ if run_function and parameters is not None:
1384
+ with st.spinner(" Executing function..."):
1385
+ try:
1386
+ # Use the client's test_toolkit_tool method
1387
+ # Create callback to capture events
1388
+ from langchain_core.callbacks import BaseCallbackHandler
1389
+
1390
+ class StreamlitEventCallback(BaseCallbackHandler):
1391
+ """Callback handler for capturing custom events in Streamlit."""
1392
+ def __init__(self):
1393
+ self.events = []
1394
+ self.steps = []
1395
+
1396
+ def on_custom_event(self, name, data, **kwargs):
1397
+ """Handle custom events dispatched by tools."""
1398
+ import datetime
1399
+ event = {
1400
+ 'name': name,
1401
+ 'data': data,
1402
+ 'timestamp': datetime.datetime.now().isoformat(),
1403
+ **kwargs
1404
+ }
1405
+ self.events.append(event)
1406
+
1407
+ # Update progress in real-time for certain events
1408
+ if name == "progress" and isinstance(data, dict):
1409
+ message = data.get('message', 'Processing...')
1410
+ step = data.get('step', None)
1411
+ total_steps = data.get('total_steps', None)
1412
+
1413
+ if step and total_steps:
1414
+ progress = step / total_steps
1415
+ st.progress(progress, text=f"{message} ({step}/{total_steps})")
1416
+ else:
1417
+ st.info(f"📊 {message}")
1418
+
1419
+ callback = StreamlitEventCallback()
1420
+ runtime_config = {
1421
+ 'callbacks': [callback],
1422
+ 'configurable': {'streamlit_session': True},
1423
+ 'tags': ['streamlit_testing', toolkit_config['name']]
1424
+ }
1425
+
1426
+ # Call the client's test method with LLM configuration
1427
+ result = st.session_state.client.test_toolkit_tool(
1428
+ toolkit_config={
1429
+ 'toolkit_name': toolkit_config['name'],
1430
+ 'settings': toolkit_config['config']
1431
+ },
1432
+ tool_name=selected_function,
1433
+ tool_params=parameters,
1434
+ runtime_config=runtime_config,
1435
+ llm_model=llm_model,
1436
+ llm_config=llm_config
1437
+ )
1438
+
1439
+ st.markdown("### 🎯 Function Result:")
1440
+
1441
+ if result['success']:
1442
+ execution_time = result.get('execution_time_seconds', 0.0)
1443
+ # Display success status with timing and LLM info
1444
+ col1, col2, col3, col4 = st.columns([2, 1, 1, 1])
1445
+ with col1:
1446
+ st.success("✅ Function executed successfully!")
1447
+ with col2:
1448
+ st.metric("⏱️ Time", f"{execution_time:.3f}s")
1449
+ with col3:
1450
+ st.metric("📡 Events", len(result.get('events_dispatched', [])))
1451
+ with col4:
1452
+ st.metric("🔧 Tool", result['tool_name'])
1453
+ llm_used = result.get('llm_model', 'N/A')
1454
+ st.metric("LLM", llm_used)
1243
1455
 
1244
- # Try to format result nicely
1245
- if isinstance(result, (dict, list)):
1246
- st.json(result)
1247
- elif isinstance(result, str):
1248
- if result.startswith('{') or result.startswith('['):
1249
- try:
1250
- parsed_result = json.loads(result)
1251
- st.json(parsed_result)
1252
- except:
1253
- st.code(result)
1456
+ # Display the actual result
1457
+ with st.container():
1458
+ st.markdown("**📊 Function Output:**")
1459
+ tool_result = result['result']
1460
+ if isinstance(tool_result, (dict, list)):
1461
+ st.json(tool_result)
1462
+ elif isinstance(tool_result, str):
1463
+ if tool_result.startswith('{') or tool_result.startswith('['):
1464
+ try:
1465
+ parsed_result = json.loads(tool_result)
1466
+ st.json(parsed_result)
1467
+ except:
1468
+ st.code(tool_result, language="text")
1469
+ else:
1470
+ if len(tool_result) > 1000:
1471
+ with st.expander("📄 View Full Output", expanded=False):
1472
+ st.code(tool_result, language="text")
1473
+ st.info(f"Output truncated. Full length: {len(tool_result)} characters.")
1474
+ else:
1475
+ st.code(tool_result, language="text")
1254
1476
  else:
1255
- st.code(result)
1256
- else:
1257
- st.code(str(result))
1477
+ st.code(str(tool_result), language="text")
1258
1478
 
1259
- # Success message
1260
- st.success("✅ Function executed successfully!")
1479
+ # Show events if any were dispatched with better formatting
1480
+ events = result.get('events_dispatched', [])
1481
+ if events:
1482
+ with st.expander(f"📡 Events Dispatched ({len(events)})", expanded=True):
1483
+ for i, event in enumerate(events):
1484
+ with st.container():
1485
+ col1, col2 = st.columns([1, 4])
1486
+ with col1:
1487
+ event_type = event.get('name', 'unknown')
1488
+ if event_type == 'progress':
1489
+ st.markdown("🔄 **Progress**")
1490
+ elif event_type == 'info':
1491
+ st.markdown("ℹ️ **Info**")
1492
+ elif event_type == 'warning':
1493
+ st.markdown("⚠️ **Warning**")
1494
+ elif event_type == 'error':
1495
+ st.markdown("❌ **Error**")
1496
+ else:
1497
+ st.markdown(f"📋 **{event_type.title()}**")
1498
+ with col2:
1499
+ event_data = event.get('data', {})
1500
+ if isinstance(event_data, dict) and 'message' in event_data:
1501
+ st.write(event_data['message'])
1502
+ if len(event_data) > 1:
1503
+ with st.expander("Event Details"):
1504
+ st.json({k: v for k, v in event_data.items() if k != 'message'})
1505
+ else:
1506
+ st.json(event_data)
1507
+ if i < len(events) - 1:
1508
+ st.divider()
1261
1509
 
1262
- except Exception as e:
1263
- st.error(f" Error running function: {str(e)}")
1510
+ # Show execution metadata
1511
+ with st.expander("🔍 Execution Details", expanded=False):
1512
+ execution_time = result.get('execution_time_seconds', 0.0)
1513
+ metadata = {
1514
+ 'tool_name': result['tool_name'],
1515
+ 'toolkit_name': result['toolkit_config'].get('toolkit_name'),
1516
+ 'llm_model': result.get('llm_model'),
1517
+ 'llm_config': llm_config,
1518
+ 'success': result['success'],
1519
+ 'execution_time_seconds': execution_time,
1520
+ 'execution_time_formatted': f"{execution_time:.3f}s",
1521
+ 'events_count': len(result.get('events_dispatched', [])),
1522
+ 'parameters_used': parameters
1523
+ }
1524
+ st.json(metadata)
1525
+ else:
1526
+ # Display error from the client method
1527
+ execution_time = result.get('execution_time_seconds', 0.0)
1528
+ st.error(f"❌ {result['error']} (after {execution_time:.3f}s)")
1264
1529
  with st.expander("🔍 Error Details"):
1265
- st.code(str(e))
1266
- else:
1267
- st.warning("⚠️ No functions available in this toolkit.")
1268
-
1269
- except Exception as e:
1270
- st.error(f"❌ Error loading toolkit functions: {str(e)}")
1271
- with st.expander("🔍 Error Details"):
1272
- st.code(str(e))
1530
+ error_details = {
1531
+ 'error': result['error'],
1532
+ 'tool_name': result['tool_name'],
1533
+ 'toolkit_config': result['toolkit_config'],
1534
+ 'llm_model': result.get('llm_model'),
1535
+ 'llm_config': llm_config,
1536
+ 'execution_time_seconds': execution_time,
1537
+ 'execution_time_formatted': f"{execution_time:.3f}s",
1538
+ 'events_dispatched': result.get('events_dispatched', [])
1539
+ }
1540
+ st.json(error_details)
1541
+
1542
+ except Exception as e:
1543
+ st.error(f"❌ Error executing function: {str(e)}")
1544
+ with st.expander("🔍 Error Details"):
1545
+ st.code(str(e))
1546
+ else:
1547
+ st.warning("⚠️ No functions available in this toolkit.")
1548
+
1549
+ except Exception as e:
1550
+ st.error(f"❌ Error loading toolkit functions: {str(e)}")
1551
+ with st.expander("🔍 Error Details"):
1552
+ st.code(str(e))
1273
1553
 
1274
1554
  # Display current configuration
1275
1555
  st.markdown("---")