alita-sdk 0.3.206__py3-none-any.whl → 0.3.207__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.
@@ -473,15 +473,15 @@ class AlitaClient:
473
473
  logger.warning(f"Error: Could not determine user ID for MCP tool: {e}")
474
474
  return None
475
475
 
476
- def predict_agent(self, client: Any, instructions: str = "You are a helpful assistant.",
476
+ def predict_agent(self, llm: ChatOpenAI, instructions: str = "You are a helpful assistant.",
477
477
  tools: Optional[list] = None, chat_history: Optional[List[Any]] = None,
478
478
  memory=None, runtime='langchain', variables: Optional[list] = None,
479
479
  store: Optional[BaseStore] = None):
480
480
  """
481
481
  Create a predict-type agent with minimal configuration.
482
-
482
+
483
483
  Args:
484
- client: The LLM client to use
484
+ llm: The LLM to use
485
485
  instructions: System instructions for the agent
486
486
  tools: Optional list of tools to provide to the agent
487
487
  chat_history: Optional chat history
@@ -489,7 +489,7 @@ class AlitaClient:
489
489
  runtime: Runtime type (default: 'langchain')
490
490
  variables: Optional list of variables for the agent
491
491
  store: Optional store for memory
492
-
492
+
493
493
  Returns:
494
494
  Runnable agent ready for execution
495
495
  """
@@ -499,7 +499,7 @@ class AlitaClient:
499
499
  chat_history = []
500
500
  if variables is None:
501
501
  variables = []
502
-
502
+
503
503
  # Create a minimal data structure for predict agent
504
504
  # All LLM settings are taken from the passed client instance
505
505
  agent_data = {
@@ -507,6 +507,269 @@ class AlitaClient:
507
507
  'tools': tools, # Tools are handled separately in predict agents
508
508
  'variables': variables
509
509
  }
510
- return LangChainAssistant(self, agent_data, client,
510
+ return LangChainAssistant(self, agent_data, llm,
511
511
  chat_history, "predict", memory=memory, store=store).runnable()
512
+
513
+ def test_toolkit_tool(self, toolkit_config: dict, tool_name: str, tool_params: dict = None,
514
+ runtime_config: dict = None, llm_model: str = None,
515
+ llm_config: dict = None) -> dict:
516
+ """
517
+ Test a single tool from a toolkit with given parameters and runtime callbacks.
518
+
519
+ This method initializes a toolkit, calls a specific tool, and supports runtime
520
+ callbacks for event dispatching, enabling tools to send custom events back to
521
+ the platform during execution.
522
+
523
+ Args:
524
+ toolkit_config: Configuration dictionary for the toolkit containing:
525
+ - toolkit_name: Name of the toolkit (e.g., 'github', 'jira')
526
+ - settings: Dictionary containing toolkit-specific settings
527
+ tool_name: Name of the specific tool to call
528
+ tool_params: Parameters to pass to the tool (default: empty dict)
529
+ runtime_config: Runtime configuration with callbacks for events, containing:
530
+ - callbacks: List of callback handlers for event processing
531
+ - configurable: Additional configuration parameters
532
+ - tags: Tags for the execution
533
+ llm_model: Name of the LLM model to use (default: 'gpt-4o-mini')
534
+ llm_config: Configuration for the LLM containing:
535
+ - max_tokens: Maximum tokens for response (default: 1000)
536
+ - temperature: Temperature for response generation (default: 0.1)
537
+ - top_p: Top-p value for response generation (default: 1.0)
538
+
539
+ Returns:
540
+ Dictionary containing:
541
+ - success: Boolean indicating if the operation was successful
542
+ - result: The actual result from the tool (if successful)
543
+ - error: Error message (if unsuccessful)
544
+ - tool_name: Name of the executed tool
545
+ - toolkit_config: Original toolkit configuration
546
+ - events_dispatched: List of custom events dispatched during execution
547
+ - llm_model: LLM model used for the test
548
+ - execution_time_seconds: Time taken to execute the tool in seconds
549
+
550
+ Example:
551
+ >>> from langchain_core.callbacks import BaseCallbackHandler
552
+ >>>
553
+ >>> class TestCallback(BaseCallbackHandler):
554
+ ... def __init__(self):
555
+ ... self.events = []
556
+ ... def on_custom_event(self, name, data, **kwargs):
557
+ ... self.events.append({'name': name, 'data': data})
558
+ >>>
559
+ >>> callback = TestCallback()
560
+ >>> runtime_config = {'callbacks': [callback]}
561
+ >>>
562
+ >>> config = {
563
+ ... 'toolkit_name': 'github',
564
+ ... 'settings': {'github_token': 'your_token'}
565
+ ... }
566
+ >>> result = client.test_toolkit_tool(
567
+ ... config, 'get_repository_info',
568
+ ... {'repo_name': 'alita'}, runtime_config,
569
+ ... llm_model='gpt-4o-mini',
570
+ ... llm_config={'temperature': 0.1}
571
+ ... )
572
+ """
573
+ if tool_params is None:
574
+ tool_params = {}
575
+ if llm_model is None:
576
+ llm_model = 'gpt-4o-mini'
577
+ if llm_config is None:
578
+ llm_config = {
579
+ 'max_tokens': 1024,
580
+ 'temperature': 0.1,
581
+ 'top_p': 1.0
582
+ }
583
+
584
+ try:
585
+ from ..utils.toolkit_utils import instantiate_toolkit_with_client
586
+ from langchain_core.runnables import RunnableConfig
587
+ import logging
588
+ import time
589
+
590
+ logger = logging.getLogger(__name__)
591
+ logger.info(f"Testing tool '{tool_name}' from toolkit '{toolkit_config.get('toolkit_name')}' with LLM '{llm_model}'")
592
+
593
+ # Create RunnableConfig for callback support
594
+ config = None
595
+ callbacks = []
596
+ events_dispatched = []
597
+
598
+ if runtime_config:
599
+ callbacks = runtime_config.get('callbacks', [])
600
+ if callbacks:
601
+ config = RunnableConfig(
602
+ callbacks=callbacks,
603
+ configurable=runtime_config.get('configurable', {}),
604
+ tags=runtime_config.get('tags', [])
605
+ )
606
+
607
+ # Create LLM instance using the client's get_llm method
608
+ try:
609
+ llm = self.get_llm(llm_model, llm_config)
610
+ logger.info(f"Created LLM instance: {llm_model} with config: {llm_config}")
611
+ except Exception as llm_error:
612
+ logger.error(f"Failed to create LLM instance: {str(llm_error)}")
613
+ return {
614
+ "success": False,
615
+ "error": f"Failed to create LLM instance '{llm_model}': {str(llm_error)}",
616
+ "tool_name": tool_name,
617
+ "toolkit_config": toolkit_config,
618
+ "llm_model": llm_model,
619
+ "events_dispatched": events_dispatched,
620
+ "execution_time_seconds": 0.0
621
+ }
622
+
623
+ # Instantiate the toolkit with client and LLM support
624
+ tools = instantiate_toolkit_with_client(toolkit_config, llm, self)
625
+
626
+ if not tools:
627
+ return {
628
+ "success": False,
629
+ "error": f"Failed to instantiate toolkit '{toolkit_config.get('toolkit_name')}' or no tools found",
630
+ "tool_name": tool_name,
631
+ "toolkit_config": toolkit_config,
632
+ "llm_model": llm_model,
633
+ "events_dispatched": events_dispatched,
634
+ "execution_time_seconds": 0.0
635
+ }
636
+
637
+ # Find the specific tool
638
+ target_tool = None
639
+ for tool in tools:
640
+ if hasattr(tool, 'name') and tool.name == tool_name:
641
+ target_tool = tool
642
+ break
643
+ elif hasattr(tool, 'func') and hasattr(tool.func, '__name__') and tool.func.__name__ == tool_name:
644
+ target_tool = tool
645
+ break
646
+
647
+ if target_tool is None:
648
+ available_tools = []
649
+ for tool in tools:
650
+ if hasattr(tool, 'name'):
651
+ available_tools.append(tool.name)
652
+ elif hasattr(tool, 'func') and hasattr(tool.func, '__name__'):
653
+ available_tools.append(tool.func.__name__)
654
+
655
+ return {
656
+ "success": False,
657
+ "error": f"Tool '{tool_name}' not found. Available tools: {available_tools}",
658
+ "tool_name": tool_name,
659
+ "toolkit_config": toolkit_config,
660
+ "llm_model": llm_model,
661
+ "events_dispatched": events_dispatched,
662
+ "execution_time_seconds": 0.0
663
+ }
664
+
665
+ # Execute the tool with callback support
666
+ try:
667
+ logger.info(f"Executing tool '{tool_name}' with parameters: {tool_params}")
668
+
669
+ # Start timing the tool execution
670
+ start_time = time.time()
671
+
672
+ # Different tools might have different invocation patterns
673
+ if hasattr(target_tool, 'invoke'):
674
+ # Use config for tools that support RunnableConfig
675
+ if config is not None:
676
+ result = target_tool.invoke(tool_params, config=config)
677
+ else:
678
+ result = target_tool.invoke(tool_params)
679
+ elif hasattr(target_tool, 'run'):
680
+ result = target_tool.run(tool_params)
681
+ elif callable(target_tool):
682
+ result = target_tool(**tool_params)
683
+ else:
684
+ execution_time = time.time() - start_time
685
+ return {
686
+ "success": False,
687
+ "error": f"Tool '{tool_name}' is not callable",
688
+ "tool_name": tool_name,
689
+ "toolkit_config": toolkit_config,
690
+ "llm_model": llm_model,
691
+ "events_dispatched": events_dispatched,
692
+ "execution_time_seconds": execution_time
693
+ }
694
+
695
+ # Calculate execution time
696
+ execution_time = time.time() - start_time
697
+
698
+ # Extract events from callbacks if they support it
699
+ for callback in callbacks:
700
+ if hasattr(callback, 'events'):
701
+ events_dispatched.extend(callback.events)
702
+ elif hasattr(callback, 'get_events'):
703
+ events_dispatched.extend(callback.get_events())
704
+ elif hasattr(callback, 'dispatched_events'):
705
+ events_dispatched.extend(callback.dispatched_events)
706
+
707
+ logger.info(f"Tool '{tool_name}' executed successfully in {execution_time:.3f} seconds")
708
+
709
+ return {
710
+ "success": True,
711
+ "result": result,
712
+ "tool_name": tool_name,
713
+ "toolkit_config": toolkit_config,
714
+ "llm_model": llm_model,
715
+ "events_dispatched": events_dispatched,
716
+ "execution_time_seconds": execution_time
717
+ }
718
+
719
+ except Exception as tool_error:
720
+ # Calculate execution time even for failed executions
721
+ execution_time = time.time() - start_time
722
+ logger.error(f"Error executing tool '{tool_name}' after {execution_time:.3f} seconds: {str(tool_error)}")
723
+
724
+ # Still collect events even if tool execution failed
725
+ for callback in callbacks:
726
+ if hasattr(callback, 'events'):
727
+ events_dispatched.extend(callback.events)
728
+ elif hasattr(callback, 'get_events'):
729
+ events_dispatched.extend(callback.get_events())
730
+ elif hasattr(callback, 'dispatched_events'):
731
+ events_dispatched.extend(callback.dispatched_events)
732
+
733
+ return {
734
+ "success": False,
735
+ "error": f"Tool execution failed: {str(tool_error)}",
736
+ "tool_name": tool_name,
737
+ "toolkit_config": toolkit_config,
738
+ "llm_model": llm_model,
739
+ "events_dispatched": events_dispatched,
740
+ "execution_time_seconds": execution_time
741
+ }
742
+
743
+ except Exception as e:
744
+ logger = logging.getLogger(__name__)
745
+ logger.error(f"Error in test_toolkit_tool: {str(e)}")
746
+ return {
747
+ "success": False,
748
+ "error": f"Method execution failed: {str(e)}",
749
+ "tool_name": tool_name,
750
+ "toolkit_config": toolkit_config,
751
+ "llm_model": llm_model if 'llm_model' in locals() else None,
752
+ "events_dispatched": [],
753
+ "execution_time_seconds": 0.0
754
+ }
755
+
756
+ def _get_real_user_id(self) -> str:
757
+ """Extract the real user ID from the auth token for MCP tool calls."""
758
+ try:
759
+ import base64
760
+ import json
761
+ # Assuming JWT token, extract user ID from payload
762
+ # This is a basic implementation - adjust based on your token format
763
+ token_parts = self.auth_token.split('.')
764
+ if len(token_parts) >= 2:
765
+ payload_part = token_parts[1]
766
+ # Add padding if needed
767
+ padding = len(payload_part) % 4
768
+ if padding:
769
+ payload_part += '=' * (4 - padding)
770
+ payload = json.loads(base64.b64decode(payload_part))
771
+ return payload.get('user_id') or payload.get('sub') or payload.get('uid')
772
+ except Exception as e:
773
+ logger.error(f"Error extracting user ID from token: {e}")
774
+ return None
512
775
 
@@ -500,7 +500,12 @@ def create_graph(
500
500
  }
501
501
 
502
502
  # Check if tools should be bound to this LLM node
503
- tool_names = node.get('tool_names', []) if isinstance(node.get('tool_names'), list) else []
503
+ connected_tools = node.get('tool_names', {})
504
+ tool_names = []
505
+ if isinstance(connected_tools, dict):
506
+ for toolkit, selected_tools in connected_tools.items():
507
+ for tool in selected_tools:
508
+ tool_names.append(f"{toolkit}___{tool}")
504
509
 
505
510
  # Filter tools if specific tool names are provided
506
511
  available_tools = []
@@ -3,9 +3,6 @@ import atexit
3
3
  import logging
4
4
  from urllib.parse import urlparse, unquote
5
5
 
6
- from psycopg import Connection
7
- from langgraph.store.postgres import PostgresStore
8
-
9
6
  logger = logging.getLogger(__name__)
10
7
 
11
8
  class StoreManager:
@@ -37,7 +34,10 @@ class StoreManager:
37
34
  "dbname": parsed.path.lstrip("/") if parsed.path else None
38
35
  }
39
36
 
40
- def get_store(self, conn_str: str) -> PostgresStore:
37
+ def get_store(self, conn_str: str):
38
+ from psycopg import Connection
39
+ from langgraph.store.postgres import PostgresStore
40
+
41
41
  store = self._stores.get(conn_str)
42
42
  if store is None:
43
43
  logger.info(f"Creating new PostgresStore for connection: {conn_str}")
@@ -14,18 +14,15 @@ from .vectorstore import VectorStoreToolkit
14
14
  from ..tools.mcp_server_tool import McpServerTool
15
15
  # Import community tools
16
16
  from ...community import get_toolkits as community_toolkits, get_tools as community_tools
17
- # from ...tools.memory import MemoryToolkit
17
+ from ...tools.memory import MemoryToolkit
18
18
 
19
19
  logger = logging.getLogger(__name__)
20
20
 
21
21
 
22
22
  def get_toolkits():
23
23
  core_toolkits = [
24
- # PromptToolkit.toolkit_config_schema(),
25
- # DatasourcesToolkit.toolkit_config_schema(),
26
- # ApplicationToolkit.toolkit_config_schema(),
27
24
  ArtifactToolkit.toolkit_config_schema(),
28
- # MemoryToolkit.toolkit_config_schema(),
25
+ MemoryToolkit.toolkit_config_schema(),
29
26
  VectorStoreToolkit.toolkit_config_schema()
30
27
  ]
31
28
 
@@ -37,12 +34,7 @@ def get_tools(tools_list: list, alita_client, llm, memory_store: BaseStore = Non
37
34
  tools = []
38
35
 
39
36
  for tool in tools_list:
40
- if tool['type'] == 'prompt':
41
- prompts.append([
42
- int(tool['settings']['prompt_id']),
43
- int(tool['settings']['prompt_version_id'])
44
- ])
45
- elif tool['type'] == 'datasource':
37
+ if tool['type'] == 'datasource':
46
38
  tools.extend(DatasourcesToolkit.get_toolkit(
47
39
  alita_client,
48
40
  datasource_ids=[int(tool['settings']['datasource_id'])],
@@ -66,15 +58,14 @@ def get_tools(tools_list: list, alita_client, llm, memory_store: BaseStore = Non
66
58
  selected_tools=[],
67
59
  llm=llm
68
60
  ))
69
- # move on tools level
70
- # elif tool['type'] == 'memory':
71
- # if memory_store is None:
72
- # raise ToolException(f"Memory store is not provided for memory tool: {tool['name']}")
73
- # tools += MemoryToolkit.get_toolkit(
74
- # namespace=tool['settings'].get('namespace', str(tool['id'])),
75
- # store=memory_store,
76
- # toolkit_name=tool.get('toolkit_name', '')
77
- # ).get_tools()
61
+ elif tool['type'] == 'memory':
62
+ if memory_store is None:
63
+ raise ToolException(f"Memory store is not provided for memory tool: {tool.get('name', tool.get('toolkit_name', 'unknown'))}")
64
+ tools += MemoryToolkit.get_toolkit(
65
+ namespace=tool['settings'].get('namespace', str(tool['id'])),
66
+ store=memory_store,
67
+ toolkit_name=tool.get('toolkit_name', '')
68
+ ).get_tools()
78
69
  elif tool['type'] == 'artifact':
79
70
  tools.extend(ArtifactToolkit.get_toolkit(
80
71
  client=alita_client,