alita-sdk 0.3.412.post1__py3-none-any.whl → 0.3.414__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.
@@ -1,5 +1,6 @@
1
1
  from typing import Optional
2
2
 
3
+ from atlassian import Bitbucket
3
4
  from pydantic import BaseModel, ConfigDict, Field, SecretStr
4
5
 
5
6
 
@@ -30,3 +31,97 @@ class BitbucketConfiguration(BaseModel):
30
31
  url: str = Field(description="Bitbucket URL")
31
32
  username: str = Field(description="Bitbucket Username")
32
33
  password: SecretStr = Field(description="Bitbucket Password/App Password")
34
+
35
+ @staticmethod
36
+ def check_connection(settings: dict) -> str | None:
37
+ """
38
+ Check the connection to Bitbucket.
39
+
40
+ Args:
41
+ settings: Dictionary containing Bitbucket configuration
42
+ - url: Bitbucket instance URL (required)
43
+ - username: Bitbucket username (required)
44
+ - password: Password or App Password (required)
45
+
46
+ Returns:
47
+ None if connection successful, error message string if failed
48
+ """
49
+ import requests
50
+ from requests.auth import HTTPBasicAuth
51
+
52
+ # Validate url
53
+ url = settings.get("url", "").strip()
54
+ if not url:
55
+ return "Bitbucket URL is required"
56
+
57
+ # Normalize URL - remove trailing slashes
58
+ url = url.rstrip("/")
59
+
60
+ # Basic URL validation
61
+ if not url.startswith(("http://", "https://")):
62
+ return "Bitbucket URL must start with http:// or https://"
63
+
64
+ # Validate username
65
+ username = settings.get("username", "").strip()
66
+ if not username:
67
+ return "Bitbucket username is required"
68
+
69
+ # Validate password
70
+ password = settings.get("password")
71
+ if not password:
72
+ return "Bitbucket password is required"
73
+
74
+ # Extract password value if it's a SecretStr
75
+ password_value = password.get_secret_value() if hasattr(password, 'get_secret_value') else password
76
+
77
+ if not password_value or not str(password_value).strip():
78
+ return "Bitbucket password cannot be empty"
79
+
80
+ # Detect if this is Bitbucket Cloud or Server/Data Center
81
+ is_cloud = "bitbucket.org" in url.lower() or "api.bitbucket.org" in url.lower()
82
+ is_correct_bitbucket_domain = "bitbucket" in url.lower()
83
+
84
+ if is_cloud:
85
+ # Bitbucket Cloud: Use API v2.0
86
+ # Endpoint: /2.0/user - returns current authenticated user
87
+ test_url = f"{url}/2.0/user"
88
+ else:
89
+ # Bitbucket Server/Data Center: Use API v1.0
90
+ # Endpoint: /rest/api/1.0/users/{username}
91
+ test_url = f"{url}/rest/api/1.0/users/{username}"
92
+
93
+ try:
94
+ response = requests.get(
95
+ test_url,
96
+ auth=HTTPBasicAuth(username, str(password_value).strip()),
97
+ timeout=10
98
+ )
99
+
100
+ # Check response status
101
+ if response.status_code == 200:
102
+ # Successfully connected and authenticated
103
+ return None
104
+ elif response.status_code == 401:
105
+ return "Authentication failed: invalid username or password"
106
+ elif response.status_code == 403:
107
+ return "Access forbidden: check user permissions"
108
+ elif response.status_code == 404:
109
+ if not is_correct_bitbucket_domain:
110
+ return f"Url you provided is incorrect. Please provide correct server or cloud bitbucket url."
111
+ if is_cloud:
112
+ return "Bitbucket API endpoint not found: please provide the correct bitbucket cloud URL"
113
+ else:
114
+ return "Bitbucket API endpoint not found: please provide the correct bitbucket server URL"
115
+ else:
116
+ return f"Bitbucket API returned status code {response.status_code}"
117
+
118
+ except requests.exceptions.SSLError as e:
119
+ return f"SSL certificate verification failed: {str(e)}"
120
+ except requests.exceptions.ConnectionError:
121
+ return f"Cannot connect to Bitbucket at {url if not is_cloud else 'api.bitbucket.org'}: connection refused"
122
+ except requests.exceptions.Timeout:
123
+ return f"Connection to Bitbucket at {url if not is_cloud else 'api.bitbucket.org'} timed out"
124
+ except requests.exceptions.RequestException as e:
125
+ return f"Error connecting to Bitbucket: {str(e)}"
126
+ except Exception as e:
127
+ return f"Unexpected error: {str(e)}"
@@ -34,4 +34,99 @@ class ConfluenceConfiguration(BaseModel):
34
34
  base_url: str = Field(description="Confluence URL")
35
35
  username: Optional[str] = Field(description="Confluence Username", default=None)
36
36
  api_key: Optional[SecretStr] = Field(description="Confluence API Key", default=None)
37
- token: Optional[SecretStr] = Field(description="Confluence Token", default=None)
37
+ token: Optional[SecretStr] = Field(description="Confluence Token", default=None)
38
+
39
+ @staticmethod
40
+ def check_connection(settings: dict) -> str | None:
41
+ """
42
+ Check the connection to Confluence.
43
+
44
+ Args:
45
+ settings: Dictionary containing Confluence configuration
46
+ - base_url: Confluence instance URL (required)
47
+ - username: Username for Basic Auth (optional)
48
+ - api_key: API key/password for Basic Auth (optional)
49
+ - token: Bearer token for authentication (optional)
50
+
51
+ Returns:
52
+ None if connection successful, error message string if failed
53
+ """
54
+ import requests
55
+ from requests.auth import HTTPBasicAuth
56
+
57
+ # Validate base_url
58
+ base_url = settings.get("base_url", "").strip()
59
+ if not base_url:
60
+ return "Confluence URL is required"
61
+
62
+ # Normalize URL - remove trailing slashes
63
+ base_url = base_url.rstrip("/")
64
+
65
+ # Basic URL validation
66
+ if not base_url.startswith(("http://", "https://")):
67
+ return "Confluence URL must start with http:// or https://"
68
+
69
+ # Check authentication credentials
70
+ username = settings.get("username")
71
+ api_key = settings.get("api_key")
72
+ token = settings.get("token")
73
+
74
+ # Validate authentication - at least one method must be provided
75
+ has_basic_auth = bool(username and api_key)
76
+ has_token = bool(token and str(token).strip())
77
+
78
+ # Determine authentication method
79
+ auth_headers = {}
80
+ auth = None
81
+
82
+ if has_token:
83
+ # Bearer token authentication
84
+ token_value = token.get_secret_value() if hasattr(token, 'get_secret_value') else token
85
+ auth_headers["Authorization"] = f"Bearer {token_value}"
86
+ elif has_basic_auth:
87
+ # Basic authentication
88
+ api_key_value = api_key.get_secret_value() if hasattr(api_key, 'get_secret_value') else api_key
89
+ auth = HTTPBasicAuth(username, api_key_value)
90
+ else:
91
+ return "Authentication required: provide either token or both username and api_key"
92
+
93
+ # Test connection using /rest/api/user/current endpoint
94
+ # This endpoint returns current user info and validates authentication
95
+ test_url = f"{base_url}/rest/api/user/current"
96
+
97
+ try:
98
+ response = requests.get(
99
+ test_url,
100
+ auth=auth,
101
+ headers=auth_headers,
102
+ timeout=10
103
+ )
104
+
105
+ # Check response status
106
+ if response.status_code == 200:
107
+ # Successfully connected and authenticated
108
+ return None
109
+ elif response.status_code == 401:
110
+ # Authentication failed
111
+ if has_token:
112
+ return "Authentication failed: Invalid token"
113
+ else:
114
+ return "Authentication failed: Invalid username or API key"
115
+ elif response.status_code == 403:
116
+ return """Access forbidden: check permissions and verify the credentials you provided.
117
+ Most probably you provided incorrect credentials (user name and api key or token)"""
118
+ elif response.status_code == 404:
119
+ return "Confluence API endpoint not found: verify the Confluence URL"
120
+ else:
121
+ return f"Confluence API returned status code {response.status_code}"
122
+
123
+ except requests.exceptions.SSLError as e:
124
+ return f"SSL certificate verification failed: {str(e)}"
125
+ except requests.exceptions.ConnectionError:
126
+ return f"Cannot connect to Confluence at {base_url}: connection refused"
127
+ except requests.exceptions.Timeout:
128
+ return f"Connection to Confluence at {base_url} timed out"
129
+ except requests.exceptions.RequestException as e:
130
+ return f"Error connecting to Confluence: {str(e)}"
131
+ except Exception as e:
132
+ return f"Unexpected error: {str(e)}"
@@ -29,3 +29,82 @@ class GitlabConfiguration(BaseModel):
29
29
  )
30
30
  url: str = Field(description="GitLab URL")
31
31
  private_token: SecretStr = Field(description="GitLab private token")
32
+
33
+ @staticmethod
34
+ def check_connection(settings: dict) -> str | None:
35
+ """
36
+ Check the connection to GitLab.
37
+
38
+ Args:
39
+ settings: Dictionary containing GitLab configuration
40
+ - url: GitLab instance URL (required)
41
+ - private_token: GitLab private token for authentication (required)
42
+
43
+ Returns:
44
+ None if connection successful, error message string if failed
45
+ """
46
+ import requests
47
+
48
+ # Validate url
49
+ url = settings.get("url", "").strip()
50
+ if not url:
51
+ return "GitLab URL is required"
52
+
53
+ # Normalize URL - remove trailing slashes
54
+ url = url.rstrip("/")
55
+
56
+ # Basic URL validation
57
+ if not url.startswith(("http://", "https://")):
58
+ return "GitLab URL must start with http:// or https://"
59
+
60
+ # Validate private_token
61
+ private_token = settings.get("private_token")
62
+ if not private_token:
63
+ return "GitLab private token is required"
64
+
65
+ # Extract token value if it's a SecretStr
66
+ token_value = private_token.get_secret_value() if hasattr(private_token, 'get_secret_value') else private_token
67
+
68
+ if not token_value or not str(token_value).strip():
69
+ return "GitLab private token cannot be empty"
70
+
71
+ # Test connection using /api/v4/user endpoint
72
+ # This endpoint returns current authenticated user info
73
+ test_url = f"{url}/api/v4/user"
74
+
75
+ # GitLab supports both PRIVATE-TOKEN header and Authorization Bearer
76
+ # Using PRIVATE-TOKEN is GitLab-specific and more explicit
77
+ headers = {
78
+ "PRIVATE-TOKEN": str(token_value).strip()
79
+ }
80
+
81
+ try:
82
+ response = requests.get(
83
+ test_url,
84
+ headers=headers,
85
+ timeout=10
86
+ )
87
+
88
+ # Check response status
89
+ if response.status_code == 200:
90
+ # Successfully connected and authenticated
91
+ return None
92
+ elif response.status_code == 401:
93
+ return "Authentication failed: invalid private token"
94
+ elif response.status_code == 403:
95
+ return "Access forbidden: token lacks required permissions"
96
+ elif response.status_code == 404:
97
+ return "GitLab API endpoint not found: verify the GitLab URL"
98
+ else:
99
+ return f"GitLab API returned status code {response.status_code}"
100
+
101
+ except requests.exceptions.SSLError as e:
102
+ return f"SSL certificate verification failed: {str(e)}"
103
+ except requests.exceptions.ConnectionError:
104
+ return f"Cannot connect to GitLab at {url}: connection refused"
105
+ except requests.exceptions.Timeout:
106
+ return f"Connection to GitLab at {url} timed out"
107
+ except requests.exceptions.RequestException as e:
108
+ return f"Error connecting to GitLab: {str(e)}"
109
+ except Exception as e:
110
+ return f"Unexpected error: {str(e)}"
@@ -35,3 +35,106 @@ class JiraConfiguration(BaseModel):
35
35
  username: Optional[str] = Field(description="Jira Username", default=None)
36
36
  api_key: Optional[SecretStr] = Field(description="Jira API Key", default=None)
37
37
  token: Optional[SecretStr] = Field(description="Jira Token", default=None)
38
+
39
+ @staticmethod
40
+ def check_connection(settings: dict) -> str | None:
41
+ """
42
+ Check Jira connection using provided settings.
43
+ Returns None if connection is successful, error message otherwise.
44
+
45
+ Tests authentication by calling the /rest/api/latest/myself endpoint,
46
+ which returns information about the currently authenticated user.
47
+ """
48
+ import requests
49
+ from requests.auth import HTTPBasicAuth
50
+
51
+ # Extract and validate settings
52
+ base_url = settings.get('base_url', '').rstrip('/')
53
+ username = settings.get('username')
54
+ api_key = settings.get('api_key')
55
+ token = settings.get('token')
56
+
57
+ # Validate base URL
58
+ if not base_url:
59
+ return "Base URL is required"
60
+
61
+ if not base_url.startswith(('http://', 'https://')):
62
+ return "Base URL must start with http:// or https://"
63
+
64
+ # Validate authentication - at least one method must be provided
65
+ has_basic_auth = bool(username and api_key)
66
+ has_token = bool(token and str(token).strip())
67
+
68
+ if not (has_basic_auth or has_token):
69
+ return "Authentication required: Provide either username + API key, or bearer token"
70
+
71
+ # Setup authentication headers
72
+ headers = {'Accept': 'application/json'}
73
+ auth = None
74
+
75
+ if has_token:
76
+ # Bearer token authentication
77
+ token_value = token.get_secret_value() if hasattr(token, 'get_secret_value') else token
78
+ headers['Authorization'] = f'Bearer {token_value}'
79
+ elif has_basic_auth:
80
+ # Basic authentication
81
+ api_key_value = api_key.get_secret_value() if hasattr(api_key, 'get_secret_value') else api_key
82
+ auth = HTTPBasicAuth(username, api_key_value)
83
+
84
+ # Build API endpoint - using 'latest' for version independence
85
+ api_endpoint = f"{base_url}/rest/api/latest/myself"
86
+
87
+ try:
88
+ # Make authenticated request to verify credentials
89
+ response = requests.get(
90
+ api_endpoint,
91
+ headers=headers,
92
+ auth=auth,
93
+ timeout=10
94
+ )
95
+
96
+ # Handle different response codes
97
+ if response.status_code == 200:
98
+ return None # Success - credentials are valid
99
+
100
+ elif response.status_code == 401:
101
+ # Authentication failed
102
+ if has_token:
103
+ return "Authentication failed: Invalid bearer token"
104
+ else:
105
+ return "Authentication failed: Invalid username or API key"
106
+
107
+ elif response.status_code == 403:
108
+ # Authenticated but insufficient permissions
109
+ return "Access forbidden: Your account has insufficient permissions to access Jira API"
110
+
111
+ elif response.status_code == 404:
112
+ # API endpoint not found - likely wrong URL
113
+ return "Jira API endpoint not found: Verify your base URL (e.g., 'https://yourinstance.atlassian.net')"
114
+
115
+ else:
116
+ # Other HTTP errors - try to extract Jira error messages
117
+ error_detail = ""
118
+ try:
119
+ error_json = response.json()
120
+ if 'errorMessages' in error_json and error_json['errorMessages']:
121
+ error_detail = ": " + ", ".join(error_json['errorMessages'])
122
+ elif 'message' in error_json:
123
+ error_detail = f": {error_json['message']}"
124
+ except:
125
+ pass
126
+
127
+ return f"Connection failed with status {response.status_code}{error_detail}"
128
+
129
+ except requests.exceptions.SSLError:
130
+ return "SSL certificate verification failed: Check your Jira URL or network settings"
131
+ except requests.exceptions.ConnectionError:
132
+ return "Connection error: Unable to reach Jira server - check URL and network connectivity"
133
+ except requests.exceptions.Timeout:
134
+ return "Connection timeout: Jira server did not respond within 10 seconds"
135
+ except requests.exceptions.MissingSchema:
136
+ return "Invalid URL format: URL must include protocol (http:// or https://)"
137
+ except requests.exceptions.InvalidURL:
138
+ return "Invalid URL format: Please check your Jira base URL"
139
+ except Exception as e:
140
+ return f"Unexpected error: {str(e)}"
@@ -19,3 +19,91 @@ class TestRailConfiguration(BaseModel):
19
19
  url: str = Field(description="Testrail URL")
20
20
  email: str = Field(description="TestRail Email")
21
21
  password: SecretStr = Field(description="TestRail Password")
22
+
23
+ @staticmethod
24
+ def check_connection(settings: dict) -> str | None:
25
+ """
26
+ Check the connection to TestRail.
27
+
28
+ Args:
29
+ settings: Dictionary containing TestRail configuration
30
+ - url: TestRail instance URL (required)
31
+ - email: User email for authentication (required)
32
+ - password: Password or API key for authentication (required)
33
+
34
+ Returns:
35
+ None if connection successful, error message string if failed
36
+ """
37
+ import requests
38
+ from requests.auth import HTTPBasicAuth
39
+
40
+ # Validate url
41
+ url = settings.get("url", "").strip()
42
+ if not url:
43
+ return "TestRail URL is required"
44
+
45
+ # Normalize URL - remove trailing slashes
46
+ url = url.rstrip("/")
47
+
48
+ # Basic URL validation
49
+ if not url.startswith(("http://", "https://")):
50
+ return "TestRail URL must start with http:// or https://"
51
+
52
+ # Validate email
53
+ email = settings.get("email", "").strip()
54
+ if not email:
55
+ return "TestRail email is required"
56
+
57
+ # Validate password
58
+ password = settings.get("password")
59
+ if not password:
60
+ return "TestRail password is required"
61
+
62
+ # Extract password value if it's a SecretStr
63
+ password_value = password.get_secret_value() if hasattr(password, 'get_secret_value') else password
64
+
65
+ if not password_value or not password_value.strip():
66
+ return "TestRail password cannot be empty"
67
+
68
+ # Test connection using /index.php?/api/v2/get_user_by_email endpoint
69
+ # This endpoint returns user info and validates authentication
70
+ test_url = f"{url}/index.php?/api/v2/get_user_by_email&email={email}"
71
+
72
+ try:
73
+ response = requests.get(
74
+ test_url,
75
+ auth=HTTPBasicAuth(email, password_value),
76
+ timeout=10
77
+ )
78
+
79
+ # Check response status
80
+ if response.status_code == 200:
81
+ # Successfully connected and authenticated
82
+ return None
83
+ elif response.status_code == 401:
84
+ return "Authentication failed: invalid email or password"
85
+ elif response.status_code == 403:
86
+ return "Access forbidden: check user permissions"
87
+ elif response.status_code == 404:
88
+ return "TestRail API endpoint not found: verify the TestRail URL"
89
+ elif response.status_code == 400:
90
+ # Could be invalid email format or other bad request
91
+ try:
92
+ error_data = response.json()
93
+ error_msg = error_data.get("error", "Bad request")
94
+ return f"Bad request: {error_msg}"
95
+ except:
96
+ return "Bad request: check email format and URL"
97
+ else:
98
+ return f"TestRail API returned status code {response.status_code}"
99
+
100
+ except requests.exceptions.SSLError as e:
101
+ return f"SSL certificate verification failed: {str(e)}"
102
+ except requests.exceptions.ConnectionError:
103
+ return f"Cannot connect to TestRail at {url}: connection refused"
104
+ except requests.exceptions.Timeout:
105
+ return f"Connection to TestRail at {url} timed out"
106
+ except requests.exceptions.RequestException as e:
107
+ return f"Error connecting to TestRail: {str(e)}"
108
+ except Exception as e:
109
+ return f"Unexpected error: {str(e)}"
@@ -30,3 +30,96 @@ class XrayConfiguration(BaseModel):
30
30
  base_url: str = Field(description="Xray URL")
31
31
  client_id: Optional[str] = Field(description="Client ID")
32
32
  client_secret: Optional[SecretStr] = Field(description="Client secret")
33
+
34
+ @staticmethod
35
+ def check_connection(settings: dict) -> str | None:
36
+ """
37
+ Check the connection to Xray Cloud.
38
+
39
+ Args:
40
+ settings: Dictionary containing Xray configuration
41
+ - base_url: Xray Cloud URL (required)
42
+ - client_id: OAuth2 Client ID (required)
43
+ - client_secret: OAuth2 Client Secret (required)
44
+
45
+ Returns:
46
+ None if connection successful, error message string if failed
47
+ """
48
+ import requests
49
+
50
+ # Validate base_url
51
+ base_url = settings.get("base_url", "").strip()
52
+ if not base_url:
53
+ return "Xray URL is required"
54
+
55
+ # Normalize URL - remove trailing slashes
56
+ base_url = base_url.rstrip("/")
57
+
58
+ # Basic URL validation
59
+ if not base_url.startswith(("http://", "https://")):
60
+ return "Xray URL must start with http:// or https://"
61
+
62
+ # Validate client_id
63
+ client_id = settings.get("client_id", "").strip() if settings.get("client_id") else ""
64
+ if not client_id:
65
+ return "Xray client ID is required"
66
+
67
+ # Validate client_secret
68
+ client_secret = settings.get("client_secret")
69
+ if not client_secret:
70
+ return "Xray client secret is required"
71
+
72
+ # Extract client_secret value if it's a SecretStr
73
+ client_secret_value = client_secret.get_secret_value() if hasattr(client_secret, 'get_secret_value') else client_secret
74
+
75
+ if not client_secret_value or not str(client_secret_value).strip():
76
+ return "Xray client secret cannot be empty"
77
+
78
+ # Test connection using /api/v2/authenticate endpoint
79
+ # This is the OAuth2 token generation endpoint for Xray Cloud
80
+ auth_url = f"{base_url}/api/v2/authenticate"
81
+
82
+ auth_payload = {
83
+ "client_id": client_id,
84
+ "client_secret": str(client_secret_value).strip()
85
+ }
86
+
87
+ try:
88
+ response = requests.post(
89
+ auth_url,
90
+ json=auth_payload,
91
+ headers={"Content-Type": "application/json"},
92
+ timeout=10
93
+ )
94
+
95
+ # Check response status
96
+ if response.status_code == 200:
97
+ # Successfully authenticated and got token
98
+ return None
99
+ elif response.status_code == 401:
100
+ return "Authentication failed: invalid client ID or secret"
101
+ elif response.status_code == 403:
102
+ return "Access forbidden: check client credentials"
103
+ elif response.status_code == 400:
104
+ # Bad request - could be invalid format
105
+ try:
106
+ error_data = response.json()
107
+ error_msg = error_data.get("error", "Bad request")
108
+ return f"Bad request: {error_msg}"
109
+ except:
110
+ return "Bad request: check client ID and secret format"
111
+ elif response.status_code == 404:
112
+ return "Xray API endpoint not found: verify the Xray URL"
113
+ else:
114
+ return f"Xray API returned status code {response.status_code}"
115
+
116
+ except requests.exceptions.SSLError as e:
117
+ return f"SSL certificate verification failed: {str(e)}"
118
+ except requests.exceptions.ConnectionError:
119
+ return f"Cannot connect to Xray at {base_url}: connection refused"
120
+ except requests.exceptions.Timeout:
121
+ return f"Connection to Xray at {base_url} timed out"
122
+ except requests.exceptions.RequestException as e:
123
+ return f"Error connecting to Xray: {str(e)}"
124
+ except Exception as e:
125
+ return f"Unexpected error: {str(e)}"
@@ -18,3 +18,96 @@ class ZephyrEnterpriseConfiguration(BaseModel):
18
18
  )
19
19
  base_url: str = Field(description="Zephyr base URL")
20
20
  token: Optional[SecretStr] = Field(description="API token")
21
+
22
+ @staticmethod
23
+ def check_connection(settings: dict) -> str | None:
24
+ """
25
+ Check the connection to Zephyr Enterprise.
26
+
27
+ Args:
28
+ settings: Dictionary containing Zephyr Enterprise configuration
29
+ - base_url: Zephyr Enterprise instance URL (required)
30
+ - token: API token for authentication (optional, anonymous access possible)
31
+
32
+ Returns:
33
+ None if connection successful, error message string if failed
34
+ """
35
+ import requests
36
+
37
+ # Validate base_url
38
+ base_url = settings.get("base_url", "").strip()
39
+ if not base_url:
40
+ return "Zephyr Enterprise URL is required"
41
+
42
+ # Normalize URL - remove trailing slashes
43
+ base_url = base_url.rstrip("/")
44
+
45
+ # Basic URL validation
46
+ if not base_url.startswith(("http://", "https://")):
47
+ return "Zephyr Enterprise URL must start with http:// or https://"
48
+
49
+ # Get token (optional)
50
+ token = settings.get("token")
51
+
52
+ # Prepare headers
53
+ headers = {}
54
+ has_token = False
55
+ if token:
56
+ # Extract token value if it's a SecretStr
57
+ token_value = token.get_secret_value() if hasattr(token, 'get_secret_value') else token
58
+ if token_value and str(token_value).strip():
59
+ headers["Authorization"] = f"Bearer {str(token_value).strip()}"
60
+ has_token = True
61
+
62
+ # Use different endpoints based on whether authentication is provided
63
+ # Note: /healthcheck may allow anonymous access, so we use authenticated endpoints when token is provided
64
+ if has_token:
65
+ # Test with an endpoint that requires authentication: /flex/services/rest/latest/project
66
+ # This endpoint lists projects and requires proper authentication
67
+ test_url = f"{base_url}/flex/services/rest/latest/user/current"
68
+ else:
69
+ # Without token, test basic connectivity with healthcheck
70
+ test_url = f"{base_url}/flex/services/rest/latest/healthcheck"
71
+
72
+ try:
73
+ response = requests.get(
74
+ test_url,
75
+ headers=headers,
76
+ timeout=10
77
+ )
78
+
79
+ # Check response status
80
+ if response.status_code == 200:
81
+ # Successfully connected
82
+ return None
83
+ elif response.status_code == 401:
84
+ if has_token:
85
+ return "Authentication failed: invalid API token"
86
+ else:
87
+ return "Authentication required: provide API token"
88
+ elif response.status_code == 403:
89
+ return "Access forbidden: check token permissions"
90
+ elif response.status_code == 404:
91
+ # If user endpoint not found, try healthcheck as fallback
92
+ if has_token:
93
+ try:
94
+ fallback_url = f"{base_url}/flex/services/rest/latest/healthcheck"
95
+ fallback_response = requests.get(fallback_url, headers=headers, timeout=10)
96
+ if fallback_response.status_code == 200:
97
+ return None
98
+ except:
99
+ pass
100
+ return "Zephyr Enterprise API endpoint not found: verify the Zephyr URL"
101
+ else:
102
+ return f"Zephyr Enterprise API returned status code {response.status_code}"
103
+
104
+ except requests.exceptions.SSLError as e:
105
+ return f"SSL certificate verification failed: {str(e)}"
106
+ except requests.exceptions.ConnectionError:
107
+ return f"Cannot connect to Zephyr Enterprise at {base_url}: connection refused"
108
+ except requests.exceptions.Timeout:
109
+ return f"Connection to Zephyr Enterprise at {base_url} timed out"
110
+ except requests.exceptions.RequestException as e:
111
+ return f"Error connecting to Zephyr Enterprise: {str(e)}"
112
+ except Exception as e:
113
+ return f"Unexpected error: {str(e)}"
@@ -18,3 +18,78 @@ class ZephyrEssentialConfiguration(BaseModel):
18
18
  )
19
19
  base_url: Optional[str] = Field(description="Zephyr Essential API Base URL", default=None)
20
20
  token: SecretStr = Field(description="Zephyr Essential API Token")
21
+
22
+ @staticmethod
23
+ def check_connection(settings: dict) -> str | None:
24
+ """
25
+ Check the connection to Zephyr Essential (Zephyr Scale).
26
+
27
+ Args:
28
+ settings: Dictionary containing Zephyr Essential configuration
29
+ - base_url: Zephyr Essential API Base URL (optional, defaults to Zephyr Scale Cloud API)
30
+ - token: Zephyr Essential API Token (required)
31
+
32
+ Returns:
33
+ None if connection successful, error message string if failed
34
+ """
35
+ import requests
36
+
37
+ # Get base_url or use default
38
+ base_url = settings.get("base_url")
39
+ if base_url:
40
+ base_url = base_url.strip().rstrip("/")
41
+ # Validate URL format if provided
42
+ if not base_url.startswith(("http://", "https://")):
43
+ return "Zephyr Essential URL must start with http:// or https://"
44
+ else:
45
+ # Default to Zephyr Scale Cloud API
46
+ base_url = "https://api.zephyrscale.smartbear.com/v2"
47
+
48
+ # Validate token
49
+ token = settings.get("token")
50
+ if not token:
51
+ return "Zephyr Essential API token is required"
52
+
53
+ # Extract token value if it's a SecretStr
54
+ token_value = token.get_secret_value() if hasattr(token, 'get_secret_value') else token
55
+
56
+ if not token_value or not str(token_value).strip():
57
+ return "Zephyr Essential API token cannot be empty"
58
+
59
+ # Test connection using /projects endpoint (requires authentication)
60
+ test_url = f"{base_url}/projects"
61
+
62
+ headers = {
63
+ "Authorization": f"Bearer {str(token_value).strip()}"
64
+ }
65
+
66
+ try:
67
+ response = requests.get(
68
+ test_url,
69
+ headers=headers,
70
+ timeout=10
71
+ )
72
+
73
+ # Check response status
74
+ if response.status_code == 200:
75
+ # Successfully connected and authenticated
76
+ return None
77
+ elif response.status_code == 401:
78
+ return "Authentication failed: invalid API token"
79
+ elif response.status_code == 403:
80
+ return "Access forbidden: token lacks required permissions"
81
+ elif response.status_code == 404:
82
+ return "Zephyr Essential API endpoint not found: verify the API URL"
83
+ else:
84
+ return f"Zephyr Essential API returned status code {response.status_code}"
85
+
86
+ except requests.exceptions.SSLError as e:
87
+ return f"SSL certificate verification failed: {str(e)}"
88
+ except requests.exceptions.ConnectionError:
89
+ return f"Cannot connect to Zephyr Essential at {base_url}: connection refused"
90
+ except requests.exceptions.Timeout:
91
+ return f"Connection to Zephyr Essential at {base_url} timed out"
92
+ except requests.exceptions.RequestException as e:
93
+ return f"Error connecting to Zephyr Essential: {str(e)}"
94
+ except Exception as e:
95
+ return f"Unexpected error: {str(e)}"
@@ -80,3 +80,5 @@ DEFAULT_MULTIMODAL_PROMPT = """
80
80
  - Maintain a structured and logical flow in the output to enhance understanding and usability.
81
81
  - Avoid presenting the entire prompt for user.
82
82
  """
83
+
84
+ ELITEA_RS = "elitea_response"
@@ -467,10 +467,11 @@ def create_graph(
467
467
  input_params = node.get('input', ['messages'])
468
468
  input_mapping = node.get('input_mapping',
469
469
  {'messages': {'type': 'variable', 'value': 'messages'}})
470
+ output_vars = node.get('output', [])
470
471
  lg_builder.add_node(node_id, FunctionTool(
471
472
  client=client, tool=tool,
472
473
  name=node_id, return_type='str',
473
- output_variables=node.get('output', []),
474
+ output_variables=output_vars + ['messages'] if 'messages' not in output_vars else output_vars,
474
475
  input_variables=input_params,
475
476
  input_mapping= input_mapping
476
477
  ))
@@ -500,15 +501,6 @@ def create_graph(
500
501
  structured_output=node.get('structured_output', False),
501
502
  task=node.get('task')
502
503
  ))
503
- # TODO: decide on struct output for agent nodes
504
- # elif node_type == 'agent':
505
- # lg_builder.add_node(node_id, AgentNode(
506
- # client=client, tool=tool,
507
- # name=node['id'], return_type='dict',
508
- # output_variables=node.get('output', []),
509
- # input_variables=node.get('input', ['messages']),
510
- # task=node.get('task')
511
- # ))
512
504
  elif node_type == 'loop':
513
505
  lg_builder.add_node(node_id, LoopNode(
514
506
  client=client, tool=tool,
@@ -5,8 +5,9 @@ import re
5
5
  from pydantic import create_model, Field
6
6
  from typing import Tuple, TypedDict, Any, Optional, Annotated
7
7
  from langchain_core.messages import AnyMessage
8
- from langchain_core.prompts import PromptTemplate
9
- from langgraph.graph import MessagesState, add_messages
8
+ from langgraph.graph import add_messages
9
+
10
+ from ...runtime.langchain.constants import ELITEA_RS
10
11
 
11
12
  logger = logging.getLogger(__name__)
12
13
 
@@ -130,7 +131,7 @@ def parse_type(type_str):
130
131
 
131
132
 
132
133
  def create_state(data: Optional[dict] = None):
133
- state_dict = {'input': str, 'router_output': str} # Always include router_output
134
+ state_dict = {'input': str, 'router_output': str, ELITEA_RS: str} # Always include router_output
134
135
  types_dict = {}
135
136
  if not data:
136
137
  data = {'messages': 'list[str]'}
@@ -116,14 +116,21 @@ class FunctionTool(BaseTool):
116
116
  if not self.output_variables:
117
117
  return {"messages": [{"role": "assistant", "content": dumps(tool_result)}]}
118
118
  else:
119
- if self.output_variables[0] == "messages":
120
- return {
119
+ if "messages" in self.output_variables:
120
+ messages_dict = {
121
121
  "messages": [{
122
122
  "role": "assistant",
123
- "content": dumps(tool_result) if not isinstance(tool_result, ToolException) else str(
124
- tool_result)
123
+ "content": dumps(tool_result) if not isinstance(tool_result, ToolException)
124
+ else str(tool_result)
125
125
  }]
126
126
  }
127
+ for var in self.output_variables:
128
+ if var != "messages":
129
+ if isinstance(tool_result, dict) and var in tool_result:
130
+ messages_dict[var] = tool_result[var]
131
+ else:
132
+ messages_dict[var] = tool_result
133
+ return messages_dict
127
134
  else:
128
135
  return { self.output_variables[0]: tool_result }
129
136
  except ValidationError:
@@ -7,6 +7,7 @@ from langchain_core.runnables import RunnableConfig
7
7
  from langchain_core.tools import BaseTool, ToolException
8
8
  from pydantic import Field
9
9
 
10
+ from ..langchain.constants import ELITEA_RS
10
11
  from ..langchain.utils import create_pydantic_model, propagate_the_input_mapping
11
12
 
12
13
  logger = logging.getLogger(__name__)
@@ -122,6 +123,8 @@ class LLMNode(BaseTool):
122
123
  }
123
124
  for key, value in (self.structured_output_dict or {}).items()
124
125
  }
126
+ # Add default output field for proper response to user
127
+ struct_params['elitea_response'] = {'description': 'final output to user', 'type': 'str'}
125
128
  struct_model = create_pydantic_model(f"LLMOutput", struct_params)
126
129
  completion = llm_client.invoke(messages, config=config)
127
130
  if hasattr(completion, 'tool_calls') and completion.tool_calls:
@@ -137,6 +140,8 @@ class LLMNode(BaseTool):
137
140
  # Ensure messages are properly formatted
138
141
  if result.get('messages') and isinstance(result['messages'], list):
139
142
  result['messages'] = [{'role': 'assistant', 'content': '\n'.join(result['messages'])}]
143
+ else:
144
+ result['messages'] = messages + [AIMessage(content=result.get(ELITEA_RS, ''))]
140
145
 
141
146
  return result
142
147
  else:
@@ -64,10 +64,36 @@ def _is_deno_available() -> bool:
64
64
 
65
65
 
66
66
  def _setup_pyodide_cache_env() -> None:
67
- """Setup Pyodide caching environment variables for performance optimization [NO-OP]"""
67
+ """Setup Pyodide caching environment variables for performance optimization"""
68
68
  try:
69
- for key in ["SANDBOX_BASE", "DENO_DIR"]:
70
- logger.info("Sandbox env: %s -> %s", key, os.environ.get(key, "n/a"))
69
+ # Check if cache environment file exists and source it
70
+ cache_env_file = os.path.expanduser("~/.pyodide_cache_env")
71
+ if os.path.exists(cache_env_file):
72
+ with open(cache_env_file, 'r') as f:
73
+ for line in f:
74
+ line = line.strip()
75
+ if line.startswith('export ') and '=' in line:
76
+ # Parse export VAR=value format
77
+ var_assignment = line[7:] # Remove 'export '
78
+ if '=' in var_assignment:
79
+ key, value = var_assignment.split('=', 1)
80
+ # Remove quotes if present
81
+ value = value.strip('"').strip("'")
82
+ os.environ[key] = value
83
+ logger.debug(f"Set Pyodide cache env: {key}={value}")
84
+
85
+ # Set default caching environment variables if not already set
86
+ cache_defaults = {
87
+ 'PYODIDE_PACKAGES_PATH': os.path.expanduser('~/.cache/pyodide'),
88
+ 'DENO_DIR': os.path.expanduser('~/.cache/deno'),
89
+ 'PYODIDE_CACHE_DIR': os.path.expanduser('~/.cache/pyodide'),
90
+ }
91
+
92
+ for key, default_value in cache_defaults.items():
93
+ if key not in os.environ:
94
+ os.environ[key] = default_value
95
+ logger.debug(f"Set default Pyodide env: {key}={default_value}")
96
+
71
97
  except Exception as e:
72
98
  logger.warning(f"Could not setup Pyodide cache environment: {e}")
73
99
 
@@ -116,7 +142,7 @@ class PyodideSandboxTool(BaseTool):
116
142
  def _prepare_pyodide_input(self, code: str) -> str:
117
143
  """Prepare input for PyodideSandboxTool by injecting state and alita_client into the code block."""
118
144
  pyodide_predata = ""
119
-
145
+
120
146
  # Add alita_client if available
121
147
  if self.alita_client:
122
148
  try:
@@ -132,7 +158,7 @@ class PyodideSandboxTool(BaseTool):
132
158
  f"auth_token='{self.alita_client.auth_token}')\n")
133
159
  except FileNotFoundError:
134
160
  logger.error(f"sandbox_client.py not found. Ensure the file exists.")
135
-
161
+
136
162
  return f"#elitea simplified client\n{pyodide_predata}{code}"
137
163
 
138
164
  def _initialize_sandbox(self) -> None:
@@ -149,19 +175,9 @@ class PyodideSandboxTool(BaseTool):
149
175
 
150
176
  from langchain_sandbox import PyodideSandbox
151
177
 
152
- # Air-gapped settings
153
- sandbox_base = os.environ.get("SANDBOX_BASE", os.path.expanduser('~/.cache/pyodide'))
154
- sandbox_tmp = os.path.join(sandbox_base, "tmp")
155
- deno_cache = os.environ.get("DENO_DIR", os.path.expanduser('~/.cache/deno'))
156
-
157
178
  # Configure sandbox with performance optimizations
158
179
  self._sandbox = PyodideSandbox(
159
180
  stateful=self.stateful,
160
- #
161
- allow_env=["SANDBOX_BASE"],
162
- allow_read=[sandbox_base, sandbox_tmp, deno_cache],
163
- allow_write=[sandbox_tmp, deno_cache],
164
- #
165
181
  allow_net=self.allow_net,
166
182
  # Use auto node_modules_dir for better caching
167
183
  node_modules_dir="auto"
@@ -8,6 +8,7 @@ from office365.runtime.auth.client_credential import ClientCredential
8
8
  from office365.sharepoint.client_context import ClientContext
9
9
  from pydantic import Field, PrivateAttr, create_model, model_validator, SecretStr
10
10
 
11
+ from .utils import decode_sharepoint_string
11
12
  from ..non_code_indexer_toolkit import NonCodeIndexerToolkit
12
13
  from ..utils.content_parser import parse_file_content
13
14
  from ...runtime.utils.utils import IndexerKeywords
@@ -105,26 +106,34 @@ class SharepointApiWrapper(NonCodeIndexerToolkit):
105
106
  def get_files_list(self, folder_name: str = None, limit_files: int = 100):
106
107
  """ If folder name is specified, lists all files in this folder under Shared Documents path. If folder name is empty, lists all files under root catalog (Shared Documents). Number of files is limited by limit_files (default is 100)."""
107
108
  try:
109
+ # exclude default system libraries like 'Form Templates', 'Site Assets', 'Style Library'
110
+ all_libraries = self._client.web.lists.filter("BaseTemplate eq 101 and Title ne 'Form Templates' and Title ne 'Site Assets' and Title ne 'Style Library'").get().execute_query()
108
111
  result = []
109
112
  if not limit_files:
110
113
  limit_files = 100
111
- target_folder_url = f"Shared Documents/{folder_name}" if folder_name else "Shared Documents"
112
- files = (self._client.web.get_folder_by_server_relative_path(target_folder_url)
113
- .get_files(True)
114
- .execute_query())
115
-
116
- for file in files:
117
- if len(result) >= limit_files:
118
- break
119
- temp_props = {
120
- 'Name': file.properties['Name'],
121
- 'Path': file.properties['ServerRelativeUrl'],
122
- 'Created': file.properties['TimeCreated'],
123
- 'Modified': file.properties['TimeLastModified'],
124
- 'Link': file.properties['LinkingUrl'],
125
- 'id': file.properties['UniqueId']
126
- }
127
- result.append(temp_props)
114
+ #
115
+ for lib in all_libraries:
116
+ library_type = decode_sharepoint_string(lib.properties["EntityTypeName"])
117
+ target_folder_url = f"{library_type}/{folder_name}" if folder_name else library_type
118
+ files = (self._client.web.get_folder_by_server_relative_path(target_folder_url)
119
+ .get_files(True)
120
+ .execute_query())
121
+ #
122
+ for file in files:
123
+ if f"{library_type}/Forms" in file.properties['ServerRelativeUrl']:
124
+ # skip files from system folder "Forms"
125
+ continue
126
+ if len(result) >= limit_files:
127
+ break
128
+ temp_props = {
129
+ 'Name': file.properties['Name'],
130
+ 'Path': file.properties['ServerRelativeUrl'],
131
+ 'Created': file.properties['TimeCreated'],
132
+ 'Modified': file.properties['TimeLastModified'],
133
+ 'Link': file.properties['LinkingUrl'],
134
+ 'id': file.properties['UniqueId']
135
+ }
136
+ result.append(temp_props)
128
137
  return result if result else ToolException("Can not get files or folder is empty. Please, double check folder name and read permissions.")
129
138
  except Exception as e:
130
139
  # attempt to get via graph api
@@ -1,5 +1,7 @@
1
- from docx import Document
1
+ import re
2
2
  from io import BytesIO
3
+ from docx import Document
4
+
3
5
 
4
6
  def read_docx_from_bytes(file_content):
5
7
  """Read and return content from a .docx file using a byte stream."""
@@ -11,4 +13,8 @@ def read_docx_from_bytes(file_content):
11
13
  return '\n'.join(text)
12
14
  except Exception as e:
13
15
  print(f"Error reading .docx from bytes: {e}")
14
- return ""
16
+ return ""
17
+
18
+
19
+ def decode_sharepoint_string(s):
20
+ return re.sub(r'_x([0-9A-Fa-f]{4})_', lambda m: chr(int(m.group(1), 16)), s)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alita_sdk
3
- Version: 0.3.412.post1
3
+ Version: 0.3.414
4
4
  Summary: SDK for building langchain agents using resources from Alita
5
5
  Author-email: Artem Rozumenko <artyom.rozumenko@gmail.com>, Mikalai Biazruchka <mikalai_biazruchka@epam.com>, Roman Mitusov <roman_mitusov@epam.com>, Ivan Krakhmaliuk <lifedj27@gmail.com>, Artem Dubrovskiy <ad13box@gmail.com>
6
6
  License-Expression: Apache-2.0
@@ -5,17 +5,17 @@ alita_sdk/configurations/__init__.py,sha256=3_MlzyzAi1dog6MFQD_ICOZvaPfXf2UfhSZw
5
5
  alita_sdk/configurations/ado.py,sha256=Djw1_8QmssETVpjpk2LeFN--Wg5-D1_L1NdwH8dtbGM,1274
6
6
  alita_sdk/configurations/azure_search.py,sha256=WDBc38uSmRBA_ypezb04wob2qUMqcw275FI39SWvrPo,808
7
7
  alita_sdk/configurations/bigquery.py,sha256=kuJ9AUAHynnyM4wkaQbiW3x5VmrJkjR0RGmsyAec194,917
8
- alita_sdk/configurations/bitbucket.py,sha256=D_RNwFHm3wEnAK-wMdJbsVy2RNLlH52xHiKsuDfehDg,1157
8
+ alita_sdk/configurations/bitbucket.py,sha256=I7LrgLxI28F9Dge5h5gdf9Fp6rdHdl7daocVaZVebfk,5249
9
9
  alita_sdk/configurations/browser.py,sha256=2ZMUrqk8M9oZi3HX4ouGp9b-qdYgpGN-hVin--zgzpc,670
10
10
  alita_sdk/configurations/carrier.py,sha256=QPgXyNXki8ECQAwBB1I64vsltV5patZUnZrd5m8obyY,693
11
- alita_sdk/configurations/confluence.py,sha256=mAW2fgSEOg-BAV768Sc6b_EuRA3H5UL9xfqQ12Zh_w4,1421
11
+ alita_sdk/configurations/confluence.py,sha256=bGGw8R_JtcH1m3NCIuE-W52GDQXELgB9JoxrNmjj974,5469
12
12
  alita_sdk/configurations/delta_lake.py,sha256=sCNDClaO6-b1Qv5WDxOMHjQUrHnr6S6EPQuS9o6q8Z8,1130
13
13
  alita_sdk/configurations/embedding.py,sha256=8GSC8Feh8CH7bT_6cQhNqlS6raE91S2YRAtb2N9bUA8,552
14
14
  alita_sdk/configurations/figma.py,sha256=91P-R5hqTwpXQvuAcUMXztzqvSvmtPkguR88Zw830Og,1025
15
15
  alita_sdk/configurations/github.py,sha256=6F7P9BPu9zE54q8m8IPjIct8cEX3WODYzZvn9WtxOXI,6283
16
- alita_sdk/configurations/gitlab.py,sha256=0W35igIlue6QxOnPgw65ToLf4HSdPVuRyObdwQuEld8,1053
16
+ alita_sdk/configurations/gitlab.py,sha256=ZDe3uGYp7Or01YxIPAHUfvNQNw5jbQHpHakx3XJRO4M,4177
17
17
  alita_sdk/configurations/google_places.py,sha256=jXhlPXywkDhHNrgB4KoVWogf3CVi1bY3wFLRzDqMr-E,589
18
- alita_sdk/configurations/jira.py,sha256=ASh8I2iVXzOOtwjRX7kYNllXpCXyAIxFMP_YD4Q0PTI,1379
18
+ alita_sdk/configurations/jira.py,sha256=isZPChfvzVNaoWLcVq4jWVDUcDGszieWdLg8gqxAU84,5914
19
19
  alita_sdk/configurations/pgvector.py,sha256=P-Q07ocIg4CXN_7hUBDM6r9gN62XS1N2jyP79tM9Tig,500
20
20
  alita_sdk/configurations/postman.py,sha256=A4swgV_0eEJ3LXYWzUUIDD5zFu9rS_vndqp1xd4YHnM,1114
21
21
  alita_sdk/configurations/qtest.py,sha256=i-09LUFDqcBKmzSiKd8C3NEKcRDAH6wwKsajC_TbBnM,642
@@ -28,11 +28,11 @@ alita_sdk/configurations/slack.py,sha256=ppwfV7YMpkq-qU6YREK7EH8VmYBZ0EN_9WIwz3E
28
28
  alita_sdk/configurations/sonar.py,sha256=b5Mh-s8C6Wts0KF0VX6AN8KmdXwVSu1UXJOf2AVGQbs,676
29
29
  alita_sdk/configurations/sql.py,sha256=0n2q2Tmc19_xUol6EN1LO6H1tZgJuxm7PvK3bFklGnM,754
30
30
  alita_sdk/configurations/testio.py,sha256=yoGWTuwcjFoBxbsxHLGVbcQBBZAFDQwuVcTijyUrW_U,612
31
- alita_sdk/configurations/testrail.py,sha256=k0fPmHBIrWAfEKhrDdB9Rdirw-UFHFoXkRePyrsqcWI,725
32
- alita_sdk/configurations/xray.py,sha256=_XCW5eGEAyhDuRvXCtTAZ24BGsIwzvIaeAZT10xoW6M,1147
31
+ alita_sdk/configurations/testrail.py,sha256=URm7nT3inwdwnD-_BVMlL5QSIPbKwtZGfxoeaXyyAm8,4355
32
+ alita_sdk/configurations/xray.py,sha256=mWic9HMJlYaxMWu0Hs6wpBvRAcJTUnrXMJjAgSzSinM,4968
33
33
  alita_sdk/configurations/zephyr.py,sha256=ndqGYFy5OFxjoXB7DzC71rd5W6qGBGAlKMWoqT8TuNk,1653
34
- alita_sdk/configurations/zephyr_enterprise.py,sha256=UaBk3qWcT2-bCzko5HEPvgxArw1ZpvOvwXwFYrIHZjM,710
35
- alita_sdk/configurations/zephyr_essential.py,sha256=tUIrh-PRNvdrLBj6rJXqlF-h6oaMXUQI1wgit07kFBw,752
34
+ alita_sdk/configurations/zephyr_enterprise.py,sha256=x2ZNvR2tYJySzEmBY3fovUe7B-_QawCMZPWl8GzmfkU,4830
35
+ alita_sdk/configurations/zephyr_essential.py,sha256=TiZedsBlfIDroflipvoqxjJeEWPonQzeT7e1bYns98s,3869
36
36
  alita_sdk/runtime/__init__.py,sha256=4W0UF-nl3QF2bvET5lnah4o24CoTwSoKXhuN0YnwvEE,828
37
37
  alita_sdk/runtime/clients/__init__.py,sha256=BdehU5GBztN1Qi1Wul0cqlU46FxUfMnI6Vq2Zd_oq1M,296
38
38
  alita_sdk/runtime/clients/artifact.py,sha256=b7hVuGRROt6qUcT11uAZqzJqslzmlgW-Y6oGsiwNmjI,4029
@@ -43,13 +43,13 @@ alita_sdk/runtime/clients/sandbox_client.py,sha256=OhEasE0MxBBDw4o76xkxVCpNpr3xJ
43
43
  alita_sdk/runtime/langchain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
44
44
  alita_sdk/runtime/langchain/assistant.py,sha256=qKoEjbGuUnX-OZDHmSaK3plb1jON9unzEwAjxBT9DY8,16044
45
45
  alita_sdk/runtime/langchain/chat_message_template.py,sha256=kPz8W2BG6IMyITFDA5oeb5BxVRkHEVZhuiGl4MBZKdc,2176
46
- alita_sdk/runtime/langchain/constants.py,sha256=eHVJ_beJNTf1WJo4yq7KMK64fxsRvs3lKc34QCXSbpk,3319
46
+ alita_sdk/runtime/langchain/constants.py,sha256=DhxYZgIVcyP7V9MnHQy5kD0N4Do4YxntnQbH1Pmw4Dg,3349
47
47
  alita_sdk/runtime/langchain/indexer.py,sha256=0ENHy5EOhThnAiYFc7QAsaTNp9rr8hDV_hTK8ahbatk,37592
48
- alita_sdk/runtime/langchain/langraph_agent.py,sha256=SoA7il7_Q9OyJbCDVubVMVNkL1NI0OzIU7FR33R7onI,50185
48
+ alita_sdk/runtime/langchain/langraph_agent.py,sha256=9ezh0iDTQwyE4PBH96k9g31Mz2Y1Rap1iw3mj7laQBQ,49731
49
49
  alita_sdk/runtime/langchain/mixedAgentParser.py,sha256=M256lvtsL3YtYflBCEp-rWKrKtcY1dJIyRGVv7KW9ME,2611
50
50
  alita_sdk/runtime/langchain/mixedAgentRenderes.py,sha256=asBtKqm88QhZRILditjYICwFVKF5KfO38hu2O-WrSWE,5964
51
51
  alita_sdk/runtime/langchain/store_manager.py,sha256=i8Fl11IXJhrBXq1F1ukEVln57B1IBe-tqSUvfUmBV4A,2218
52
- alita_sdk/runtime/langchain/utils.py,sha256=rLh2POmA7mzO9vR7yTlEIYWOCp9ANuBDJe4sl1AjA5A,7574
52
+ alita_sdk/runtime/langchain/utils.py,sha256=GxqG1xhdUd6BC01ZZFkdc_0Q27LNgc-mr6t_1LD7GRU,7579
53
53
  alita_sdk/runtime/langchain/agents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
54
  alita_sdk/runtime/langchain/agents/xml_chat.py,sha256=Mx7PK5T97_GrFCwHHZ3JZP42S7MwtUzV0W-_8j6Amt8,6212
55
55
  alita_sdk/runtime/langchain/document_loaders/AlitaBDDScenariosLoader.py,sha256=4kFU1ijrM1Jw7cywQv8mUiBHlE6w-uqfzSZP4hUV5P4,3771
@@ -110,18 +110,18 @@ alita_sdk/runtime/tools/application.py,sha256=RCGe-mRfj8372gTFkEX2xBvcYhw7IKdU1t
110
110
  alita_sdk/runtime/tools/artifact.py,sha256=u3szFwZqguHrPZ3tZJ7S_TiZl7cxlT3oHYd6zbdpRDE,13842
111
111
  alita_sdk/runtime/tools/datasource.py,sha256=pvbaSfI-ThQQnjHG-QhYNSTYRnZB0rYtZFpjCfpzxYI,2443
112
112
  alita_sdk/runtime/tools/echo.py,sha256=spw9eCweXzixJqHnZofHE1yWiSUa04L4VKycf3KCEaM,486
113
- alita_sdk/runtime/tools/function.py,sha256=jk_JrtuYByR9Df5EFOGFheB9HktNPJcOwf4jsTzV73w,6144
113
+ alita_sdk/runtime/tools/function.py,sha256=VOgcCjsDfyH2kBbX4k3DtwpsW7aX0JETahC26dwYsuA,6540
114
114
  alita_sdk/runtime/tools/graph.py,sha256=7jImBBSEdP5Mjnn2keOiyUwdGDFhEXLUrgUiugO3mgA,3503
115
115
  alita_sdk/runtime/tools/image_generation.py,sha256=Kls9D_ke_SK7xmVr7I9SlQcAEBJc86gf66haN0qIj9k,7469
116
116
  alita_sdk/runtime/tools/indexer_tool.py,sha256=whSLPevB4WD6dhh2JDXEivDmTvbjiMV1MrPl9cz5eLA,4375
117
- alita_sdk/runtime/tools/llm.py,sha256=OEhf4_YlZIihIpkuRKbbWJ_Lfk-V_rJHpy2NRm5xuCg,15533
117
+ alita_sdk/runtime/tools/llm.py,sha256=Oq0IH1mVEgtkw-8p5y-_xTCz-OcWtfvtR5xl19Anrhc,15875
118
118
  alita_sdk/runtime/tools/loop.py,sha256=uds0WhZvwMxDVFI6MZHrcmMle637cQfBNg682iLxoJA,8335
119
119
  alita_sdk/runtime/tools/loop_output.py,sha256=U4hO9PCQgWlXwOq6jdmCGbegtAxGAPXObSxZQ3z38uk,8069
120
120
  alita_sdk/runtime/tools/mcp_server_tool.py,sha256=MhLxZJ44LYrB_0GrojmkyqKoDRaqIHkEQAsg718ipog,4277
121
121
  alita_sdk/runtime/tools/pgvector_search.py,sha256=NN2BGAnq4SsDHIhUcFZ8d_dbEOM8QwB0UwpsWCYruXU,11692
122
122
  alita_sdk/runtime/tools/prompt.py,sha256=nJafb_e5aOM1Rr3qGFCR-SKziU9uCsiP2okIMs9PppM,741
123
123
  alita_sdk/runtime/tools/router.py,sha256=p7e0tX6YAWw2M2Nq0A_xqw1E2P-Xz1DaJvhUstfoZn4,1584
124
- alita_sdk/runtime/tools/sandbox.py,sha256=LTCHC9xnuYThWX30PCTVEtjeg50FX7w2c29KU5E68W0,15251
124
+ alita_sdk/runtime/tools/sandbox.py,sha256=7KpTAE4Fs1dvQIwAH__jwrxXG4QUa9GhcOhlkE8EKro,16049
125
125
  alita_sdk/runtime/tools/tool.py,sha256=lE1hGi6qOAXG7qxtqxarD_XMQqTghdywf261DZawwno,5631
126
126
  alita_sdk/runtime/tools/vectorstore.py,sha256=0SzfY1dYrGr7YUapJzXY01JFyzLv36dPjwHzs1XZIM4,34392
127
127
  alita_sdk/runtime/tools/vectorstore_base.py,sha256=k_6LAhhBJEs5SXCQJI3bBvJLQli6_3pHjqF6SCQGJGc,28312
@@ -317,9 +317,9 @@ alita_sdk/tools/servicenow/__init__.py,sha256=ziEt2juPrGFyB98ZXbGf25v6gZo4UJTHsz
317
317
  alita_sdk/tools/servicenow/api_wrapper.py,sha256=WpH-bBLGFdhehs4g-K-WAkNuaD1CSrwsDpdgB3RG53s,6120
318
318
  alita_sdk/tools/servicenow/servicenow_client.py,sha256=Rdqfu-ll-qbnclMzChLZBsfXRDzgoX_FdeI2WLApWxc,3269
319
319
  alita_sdk/tools/sharepoint/__init__.py,sha256=5z2iSmm-0kbHKf70wN6OOgS4Px7tOzwkIpHXz0Vrbj4,4045
320
- alita_sdk/tools/sharepoint/api_wrapper.py,sha256=onWKNO-pC3MEvv54JyRj26RaDu2PEg7vwnQcqN3UIiQ,14675
320
+ alita_sdk/tools/sharepoint/api_wrapper.py,sha256=QcOtgc8L10YLdSK8LwACV_vRBF7T-u7T9-W05TbuAQI,15409
321
321
  alita_sdk/tools/sharepoint/authorization_helper.py,sha256=QvxWFBjYZfhI1h_KkSrDbRh8D5BlFX8xWDLmlIoO4mo,9569
322
- alita_sdk/tools/sharepoint/utils.py,sha256=fZ1YzAu5CTjKSZeslowpOPH974902S8vCp1Wu7L44LM,446
322
+ alita_sdk/tools/sharepoint/utils.py,sha256=CO1PNRC5CpQaVo2Gdenj_jqm2bReSqUT92Bk5s37d8M,573
323
323
  alita_sdk/tools/slack/__init__.py,sha256=YiPAoRc6y6uVpfHl0K1Qi-flcyPlWFIMVcVbhicGWXY,3990
324
324
  alita_sdk/tools/slack/api_wrapper.py,sha256=5VrV7iSGno8ZcDzEHdGPNhInhtODGPPvAzoZ9W9iQWE,14009
325
325
  alita_sdk/tools/sql/__init__.py,sha256=F3eewPEKqVh3gZdNTUvcoFPOgG9Mn11qKoadtCmhQ4Q,3484
@@ -353,8 +353,8 @@ alita_sdk/tools/zephyr_scale/api_wrapper.py,sha256=kT0TbmMvuKhDUZc0i7KO18O38JM9S
353
353
  alita_sdk/tools/zephyr_squad/__init__.py,sha256=0ne8XLJEQSLOWfzd2HdnqOYmQlUliKHbBED5kW_Vias,2895
354
354
  alita_sdk/tools/zephyr_squad/api_wrapper.py,sha256=kmw_xol8YIYFplBLWTqP_VKPRhL_1ItDD0_vXTe_UuI,14906
355
355
  alita_sdk/tools/zephyr_squad/zephyr_squad_cloud_client.py,sha256=R371waHsms4sllHCbijKYs90C-9Yu0sSR3N4SUfQOgU,5066
356
- alita_sdk-0.3.412.post1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
357
- alita_sdk-0.3.412.post1.dist-info/METADATA,sha256=sau81SODpRBYAI4JH0vc014TBOE1hHJo9QkI7QsaPXU,19077
358
- alita_sdk-0.3.412.post1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
359
- alita_sdk-0.3.412.post1.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
360
- alita_sdk-0.3.412.post1.dist-info/RECORD,,
356
+ alita_sdk-0.3.414.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
357
+ alita_sdk-0.3.414.dist-info/METADATA,sha256=qLODyJVtp0z8r9KLfWTSl84P-PykpgbyZZT_wCelv9Y,19071
358
+ alita_sdk-0.3.414.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
359
+ alita_sdk-0.3.414.dist-info/top_level.txt,sha256=0vJYy5p_jK6AwVb1aqXr7Kgqgk3WDtQ6t5C-XI9zkmg,10
360
+ alita_sdk-0.3.414.dist-info/RECORD,,