alita-sdk 0.3.263__py3-none-any.whl → 0.3.499__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 (248) hide show
  1. alita_sdk/cli/__init__.py +10 -0
  2. alita_sdk/cli/__main__.py +17 -0
  3. alita_sdk/cli/agent/__init__.py +5 -0
  4. alita_sdk/cli/agent/default.py +258 -0
  5. alita_sdk/cli/agent_executor.py +155 -0
  6. alita_sdk/cli/agent_loader.py +215 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3601 -0
  9. alita_sdk/cli/callbacks.py +647 -0
  10. alita_sdk/cli/cli.py +168 -0
  11. alita_sdk/cli/config.py +306 -0
  12. alita_sdk/cli/context/__init__.py +30 -0
  13. alita_sdk/cli/context/cleanup.py +198 -0
  14. alita_sdk/cli/context/manager.py +731 -0
  15. alita_sdk/cli/context/message.py +285 -0
  16. alita_sdk/cli/context/strategies.py +289 -0
  17. alita_sdk/cli/context/token_estimation.py +127 -0
  18. alita_sdk/cli/formatting.py +182 -0
  19. alita_sdk/cli/input_handler.py +419 -0
  20. alita_sdk/cli/inventory.py +1256 -0
  21. alita_sdk/cli/mcp_loader.py +315 -0
  22. alita_sdk/cli/toolkit.py +327 -0
  23. alita_sdk/cli/toolkit_loader.py +85 -0
  24. alita_sdk/cli/tools/__init__.py +43 -0
  25. alita_sdk/cli/tools/approval.py +224 -0
  26. alita_sdk/cli/tools/filesystem.py +1751 -0
  27. alita_sdk/cli/tools/planning.py +389 -0
  28. alita_sdk/cli/tools/terminal.py +414 -0
  29. alita_sdk/community/__init__.py +64 -8
  30. alita_sdk/community/inventory/__init__.py +224 -0
  31. alita_sdk/community/inventory/config.py +257 -0
  32. alita_sdk/community/inventory/enrichment.py +2137 -0
  33. alita_sdk/community/inventory/extractors.py +1469 -0
  34. alita_sdk/community/inventory/ingestion.py +3172 -0
  35. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  36. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  37. alita_sdk/community/inventory/parsers/base.py +295 -0
  38. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  39. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  40. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  41. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  42. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  43. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  44. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  45. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  46. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  47. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  48. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  49. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  50. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  51. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  52. alita_sdk/community/inventory/patterns/loader.py +348 -0
  53. alita_sdk/community/inventory/patterns/registry.py +198 -0
  54. alita_sdk/community/inventory/presets.py +535 -0
  55. alita_sdk/community/inventory/retrieval.py +1403 -0
  56. alita_sdk/community/inventory/toolkit.py +173 -0
  57. alita_sdk/community/inventory/visualize.py +1370 -0
  58. alita_sdk/configurations/__init__.py +10 -0
  59. alita_sdk/configurations/ado.py +4 -2
  60. alita_sdk/configurations/azure_search.py +1 -1
  61. alita_sdk/configurations/bigquery.py +1 -1
  62. alita_sdk/configurations/bitbucket.py +94 -2
  63. alita_sdk/configurations/browser.py +18 -0
  64. alita_sdk/configurations/carrier.py +19 -0
  65. alita_sdk/configurations/confluence.py +96 -1
  66. alita_sdk/configurations/delta_lake.py +1 -1
  67. alita_sdk/configurations/figma.py +0 -5
  68. alita_sdk/configurations/github.py +65 -1
  69. alita_sdk/configurations/gitlab.py +79 -0
  70. alita_sdk/configurations/google_places.py +17 -0
  71. alita_sdk/configurations/jira.py +103 -0
  72. alita_sdk/configurations/postman.py +1 -1
  73. alita_sdk/configurations/qtest.py +1 -3
  74. alita_sdk/configurations/report_portal.py +19 -0
  75. alita_sdk/configurations/salesforce.py +19 -0
  76. alita_sdk/configurations/service_now.py +1 -12
  77. alita_sdk/configurations/sharepoint.py +19 -0
  78. alita_sdk/configurations/sonar.py +18 -0
  79. alita_sdk/configurations/sql.py +20 -0
  80. alita_sdk/configurations/testio.py +18 -0
  81. alita_sdk/configurations/testrail.py +88 -0
  82. alita_sdk/configurations/xray.py +94 -1
  83. alita_sdk/configurations/zephyr_enterprise.py +94 -1
  84. alita_sdk/configurations/zephyr_essential.py +95 -0
  85. alita_sdk/runtime/clients/artifact.py +12 -2
  86. alita_sdk/runtime/clients/client.py +235 -66
  87. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  88. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  89. alita_sdk/runtime/clients/sandbox_client.py +373 -0
  90. alita_sdk/runtime/langchain/assistant.py +123 -17
  91. alita_sdk/runtime/langchain/constants.py +8 -1
  92. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  93. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
  94. alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
  95. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +8 -2
  96. alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
  97. alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
  98. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
  99. alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
  100. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
  101. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
  102. alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
  103. alita_sdk/runtime/langchain/document_loaders/constants.py +187 -40
  104. alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
  105. alita_sdk/runtime/langchain/langraph_agent.py +406 -91
  106. alita_sdk/runtime/langchain/utils.py +51 -8
  107. alita_sdk/runtime/llms/preloaded.py +2 -6
  108. alita_sdk/runtime/models/mcp_models.py +61 -0
  109. alita_sdk/runtime/toolkits/__init__.py +26 -0
  110. alita_sdk/runtime/toolkits/application.py +9 -2
  111. alita_sdk/runtime/toolkits/artifact.py +19 -7
  112. alita_sdk/runtime/toolkits/datasource.py +13 -6
  113. alita_sdk/runtime/toolkits/mcp.py +780 -0
  114. alita_sdk/runtime/toolkits/planning.py +178 -0
  115. alita_sdk/runtime/toolkits/subgraph.py +11 -6
  116. alita_sdk/runtime/toolkits/tools.py +214 -60
  117. alita_sdk/runtime/toolkits/vectorstore.py +9 -4
  118. alita_sdk/runtime/tools/__init__.py +22 -0
  119. alita_sdk/runtime/tools/application.py +16 -4
  120. alita_sdk/runtime/tools/artifact.py +312 -19
  121. alita_sdk/runtime/tools/function.py +100 -4
  122. alita_sdk/runtime/tools/graph.py +81 -0
  123. alita_sdk/runtime/tools/image_generation.py +212 -0
  124. alita_sdk/runtime/tools/llm.py +539 -180
  125. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  126. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  127. alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
  128. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  129. alita_sdk/runtime/tools/planning/models.py +246 -0
  130. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  131. alita_sdk/runtime/tools/router.py +2 -1
  132. alita_sdk/runtime/tools/sandbox.py +375 -0
  133. alita_sdk/runtime/tools/vectorstore.py +62 -63
  134. alita_sdk/runtime/tools/vectorstore_base.py +156 -85
  135. alita_sdk/runtime/utils/AlitaCallback.py +106 -20
  136. alita_sdk/runtime/utils/mcp_client.py +465 -0
  137. alita_sdk/runtime/utils/mcp_oauth.py +244 -0
  138. alita_sdk/runtime/utils/mcp_sse_client.py +405 -0
  139. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  140. alita_sdk/runtime/utils/streamlit.py +41 -14
  141. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  142. alita_sdk/runtime/utils/utils.py +14 -0
  143. alita_sdk/tools/__init__.py +78 -35
  144. alita_sdk/tools/ado/__init__.py +0 -1
  145. alita_sdk/tools/ado/repos/__init__.py +10 -6
  146. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -11
  147. alita_sdk/tools/ado/test_plan/__init__.py +10 -7
  148. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -23
  149. alita_sdk/tools/ado/wiki/__init__.py +10 -11
  150. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -28
  151. alita_sdk/tools/ado/work_item/__init__.py +10 -11
  152. alita_sdk/tools/ado/work_item/ado_wrapper.py +63 -10
  153. alita_sdk/tools/advanced_jira_mining/__init__.py +10 -7
  154. alita_sdk/tools/aws/delta_lake/__init__.py +13 -11
  155. alita_sdk/tools/azure_ai/search/__init__.py +11 -7
  156. alita_sdk/tools/base_indexer_toolkit.py +392 -86
  157. alita_sdk/tools/bitbucket/__init__.py +18 -11
  158. alita_sdk/tools/bitbucket/api_wrapper.py +52 -9
  159. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  160. alita_sdk/tools/browser/__init__.py +40 -16
  161. alita_sdk/tools/browser/crawler.py +3 -1
  162. alita_sdk/tools/browser/utils.py +15 -6
  163. alita_sdk/tools/carrier/__init__.py +17 -17
  164. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  165. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  166. alita_sdk/tools/chunkers/__init__.py +3 -1
  167. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  168. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  169. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  170. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  171. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  172. alita_sdk/tools/cloud/aws/__init__.py +9 -6
  173. alita_sdk/tools/cloud/azure/__init__.py +9 -6
  174. alita_sdk/tools/cloud/gcp/__init__.py +9 -6
  175. alita_sdk/tools/cloud/k8s/__init__.py +9 -6
  176. alita_sdk/tools/code/linter/__init__.py +7 -7
  177. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  178. alita_sdk/tools/code/sonar/__init__.py +18 -12
  179. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  180. alita_sdk/tools/confluence/__init__.py +14 -11
  181. alita_sdk/tools/confluence/api_wrapper.py +198 -58
  182. alita_sdk/tools/confluence/loader.py +10 -0
  183. alita_sdk/tools/custom_open_api/__init__.py +9 -4
  184. alita_sdk/tools/elastic/__init__.py +8 -7
  185. alita_sdk/tools/elitea_base.py +543 -64
  186. alita_sdk/tools/figma/__init__.py +10 -8
  187. alita_sdk/tools/figma/api_wrapper.py +352 -153
  188. alita_sdk/tools/github/__init__.py +13 -11
  189. alita_sdk/tools/github/api_wrapper.py +9 -26
  190. alita_sdk/tools/github/github_client.py +75 -12
  191. alita_sdk/tools/github/schemas.py +2 -1
  192. alita_sdk/tools/gitlab/__init__.py +11 -10
  193. alita_sdk/tools/gitlab/api_wrapper.py +135 -45
  194. alita_sdk/tools/gitlab_org/__init__.py +11 -9
  195. alita_sdk/tools/google/bigquery/__init__.py +12 -13
  196. alita_sdk/tools/google_places/__init__.py +18 -10
  197. alita_sdk/tools/jira/__init__.py +14 -8
  198. alita_sdk/tools/jira/api_wrapper.py +315 -168
  199. alita_sdk/tools/keycloak/__init__.py +8 -7
  200. alita_sdk/tools/localgit/local_git.py +56 -54
  201. alita_sdk/tools/memory/__init__.py +27 -11
  202. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  203. alita_sdk/tools/ocr/__init__.py +8 -7
  204. alita_sdk/tools/openapi/__init__.py +10 -1
  205. alita_sdk/tools/pandas/__init__.py +8 -7
  206. alita_sdk/tools/pandas/api_wrapper.py +7 -25
  207. alita_sdk/tools/postman/__init__.py +8 -10
  208. alita_sdk/tools/postman/api_wrapper.py +19 -8
  209. alita_sdk/tools/postman/postman_analysis.py +8 -1
  210. alita_sdk/tools/pptx/__init__.py +8 -9
  211. alita_sdk/tools/qtest/__init__.py +19 -13
  212. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  213. alita_sdk/tools/rally/__init__.py +10 -9
  214. alita_sdk/tools/report_portal/__init__.py +20 -15
  215. alita_sdk/tools/salesforce/__init__.py +19 -15
  216. alita_sdk/tools/servicenow/__init__.py +14 -11
  217. alita_sdk/tools/sharepoint/__init__.py +14 -13
  218. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  219. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  220. alita_sdk/tools/sharepoint/utils.py +8 -2
  221. alita_sdk/tools/slack/__init__.py +10 -7
  222. alita_sdk/tools/sql/__init__.py +19 -18
  223. alita_sdk/tools/sql/api_wrapper.py +71 -23
  224. alita_sdk/tools/testio/__init__.py +18 -12
  225. alita_sdk/tools/testrail/__init__.py +10 -10
  226. alita_sdk/tools/testrail/api_wrapper.py +213 -45
  227. alita_sdk/tools/utils/__init__.py +28 -4
  228. alita_sdk/tools/utils/content_parser.py +181 -61
  229. alita_sdk/tools/utils/text_operations.py +254 -0
  230. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  231. alita_sdk/tools/xray/__init__.py +12 -7
  232. alita_sdk/tools/xray/api_wrapper.py +58 -113
  233. alita_sdk/tools/zephyr/__init__.py +9 -6
  234. alita_sdk/tools/zephyr_enterprise/__init__.py +13 -8
  235. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +17 -7
  236. alita_sdk/tools/zephyr_essential/__init__.py +13 -9
  237. alita_sdk/tools/zephyr_essential/api_wrapper.py +289 -47
  238. alita_sdk/tools/zephyr_essential/client.py +6 -4
  239. alita_sdk/tools/zephyr_scale/__init__.py +10 -7
  240. alita_sdk/tools/zephyr_scale/api_wrapper.py +6 -2
  241. alita_sdk/tools/zephyr_squad/__init__.py +9 -6
  242. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/METADATA +180 -33
  243. alita_sdk-0.3.499.dist-info/RECORD +433 -0
  244. alita_sdk-0.3.499.dist-info/entry_points.txt +2 -0
  245. alita_sdk-0.3.263.dist-info/RECORD +0 -342
  246. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/WHEEL +0 -0
  247. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/licenses/LICENSE +0 -0
  248. {alita_sdk-0.3.263.dist-info → alita_sdk-0.3.499.dist-info}/top_level.txt +0 -0
@@ -20,8 +20,9 @@ from .prompt import AlitaPrompt
20
20
  from .datasource import AlitaDataSource
21
21
  from .artifact import Artifact
22
22
  from ..langchain.chat_message_template import Jinja2TemplatedChatMessagesTemplate
23
- from ..utils.utils import TOOLKIT_SPLITTER
23
+ from ..utils.mcp_oauth import McpAuthorizationRequired
24
24
  from ...tools import get_available_toolkit_models
25
+ from ...tools.base_indexer_toolkit import IndexTools
25
26
 
26
27
  logger = logging.getLogger(__name__)
27
28
 
@@ -68,7 +69,11 @@ class AlitaClient:
68
69
  self.bucket_url = f"{self.base_url}{self.api_path}/artifacts/buckets/{self.project_id}"
69
70
  self.configurations_url = f'{self.base_url}{self.api_path}/integrations/integrations/default/{self.project_id}?section=configurations&unsecret=true'
70
71
  self.ai_section_url = f'{self.base_url}{self.api_path}/integrations/integrations/default/{self.project_id}?section=ai'
72
+ self.models_url = f'{self.base_url}{self.api_path}/configurations/models/{self.project_id}?include_shared=true'
73
+ self.image_generation_url = f"{self.base_url}{self.llm_path}/images/generations"
71
74
  self.configurations: list = configurations or []
75
+ self.model_timeout = kwargs.get('model_timeout', 120)
76
+ self.model_image_generation = kwargs.get('model_image_generation')
72
77
 
73
78
  def get_mcp_toolkits(self):
74
79
  if user_id := self._get_real_user_id():
@@ -81,8 +86,23 @@ class AlitaClient:
81
86
  def mcp_tool_call(self, params: dict[str, Any]):
82
87
  if user_id := self._get_real_user_id():
83
88
  url = f"{self.mcp_tools_call}/{user_id}"
84
- data = requests.post(url, headers=self.headers, json=params, verify=False).json()
85
- return data
89
+ #
90
+ # This loop iterates over each key-value pair in the arguments dictionary,
91
+ # and if a value is a Pydantic object, it replaces it with its dictionary representation using .dict().
92
+ for arg_name, arg_value in params.get('params', {}).get('arguments', {}).items():
93
+ if isinstance(arg_value, list):
94
+ params['params']['arguments'][arg_name] = [
95
+ item.dict() if hasattr(item, "dict") and callable(item.dict) else item
96
+ for item in arg_value
97
+ ]
98
+ elif hasattr(arg_value, "dict") and callable(arg_value.dict):
99
+ params['params']['arguments'][arg_name] = arg_value.dict()
100
+ #
101
+ response = requests.post(url, headers=self.headers, json=params, verify=False)
102
+ try:
103
+ return response.json()
104
+ except (ValueError, TypeError):
105
+ return response.text
86
106
  else:
87
107
  return f"Error: Could not determine user ID for MCP tool call"
88
108
 
@@ -157,6 +177,20 @@ class AlitaClient:
157
177
  return resp.json()
158
178
  return []
159
179
 
180
+ def get_available_models(self):
181
+ """Get list of available models from the configurations API.
182
+
183
+ Returns:
184
+ List of model dictionaries with 'name' and other properties,
185
+ or empty list if request fails.
186
+ """
187
+ resp = requests.get(self.models_url, headers=self.headers, verify=False)
188
+ if resp.ok:
189
+ data = resp.json()
190
+ # API returns {"items": [...], ...}
191
+ return data.get('items', [])
192
+ return []
193
+
160
194
  def get_embeddings(self, embedding_model: str) -> OpenAIEmbeddings:
161
195
  """
162
196
  Get an instance of OpenAIEmbeddings configured with the project ID and auth token.
@@ -169,6 +203,7 @@ class AlitaClient:
169
203
  model=embedding_model,
170
204
  api_key=self.auth_token,
171
205
  openai_organization=str(self.project_id),
206
+ request_timeout=self.model_timeout
172
207
  )
173
208
 
174
209
  def get_llm(self, model_name: str, model_config: dict) -> ChatOpenAI:
@@ -187,20 +222,99 @@ class AlitaClient:
187
222
 
188
223
  logger.info(f"Creating ChatOpenAI model: {model_name} with config: {model_config}")
189
224
 
190
- return ChatOpenAI(
191
- base_url=f"{self.base_url}{self.llm_path}",
192
- model=model_name,
193
- api_key=self.auth_token,
194
- streaming=model_config.get("streaming", True),
195
- stream_usage=model_config.get("stream_usage", True),
196
- max_tokens=model_config.get("max_tokens", None),
197
- top_p=model_config.get("top_p"),
198
- temperature=model_config.get("temperature"),
199
- max_retries=model_config.get("max_retries", 3),
200
- seed=model_config.get("seed", None),
201
- openai_organization=str(self.project_id),
202
- )
225
+ try:
226
+ from tools import this # pylint: disable=E0401,C0415
227
+ worker_config = this.for_module("indexer_worker").descriptor.config
228
+ except: # pylint: disable=W0702
229
+ worker_config = {}
230
+
231
+ use_responses_api = False
232
+
233
+ if worker_config and isinstance(worker_config, dict):
234
+ for target_name_tag in worker_config.get("use_responses_api_for", []):
235
+ if target_name_tag in model_name:
236
+ use_responses_api = True
237
+ break
238
+
239
+ # handle case when max_tokens are auto-configurable == -1
240
+ llm_max_tokens = model_config.get("max_tokens", None)
241
+ if llm_max_tokens and llm_max_tokens == -1:
242
+ logger.warning(f'User selected `MAX COMPLETION TOKENS` as `auto`')
243
+ # default nuber for a case when auto is selected for an agent
244
+ llm_max_tokens = 4000
245
+
246
+ target_kwargs = {
247
+ "base_url": f"{self.base_url}{self.llm_path}",
248
+ "model": model_name,
249
+ "api_key": self.auth_token,
250
+ "streaming": model_config.get("streaming", True),
251
+ "stream_usage": model_config.get("stream_usage", True),
252
+ "max_tokens": llm_max_tokens,
253
+ "temperature": model_config.get("temperature"),
254
+ "reasoning_effort": model_config.get("reasoning_effort"),
255
+ "max_retries": model_config.get("max_retries", 3),
256
+ "seed": model_config.get("seed", None),
257
+ "openai_organization": str(self.project_id),
258
+ }
259
+
260
+ if use_responses_api:
261
+ target_kwargs["use_responses_api"] = True
262
+
263
+ return ChatOpenAI(**target_kwargs)
264
+
265
+ def generate_image(self,
266
+ prompt: str,
267
+ n: int = 1,
268
+ size: str = "auto",
269
+ quality: str = "auto",
270
+ response_format: str = "b64_json",
271
+ style: Optional[str] = None) -> dict:
272
+
273
+ if not self.model_image_generation:
274
+ raise ValueError("Image generation model is not configured for this client")
275
+
276
+ image_generation_data = {
277
+ "prompt": prompt,
278
+ "model": self.model_image_generation,
279
+ "n": n,
280
+ "response_format": response_format,
281
+ }
282
+
283
+ # Only add optional parameters if they have meaningful values
284
+ if size and size.lower() != "auto":
285
+ image_generation_data["size"] = size
286
+
287
+ if quality and quality.lower() != "auto":
288
+ image_generation_data["quality"] = quality
289
+
290
+ if style:
291
+ image_generation_data["style"] = style
292
+
293
+ # Standard headers for image generation
294
+ image_headers = self.headers.copy()
295
+ image_headers.update({
296
+ "Content-Type": "application/json",
297
+ })
203
298
 
299
+ logger.info(f"Generating image with model: {self.model_image_generation}, prompt: {prompt[:50]}...")
300
+
301
+ try:
302
+ response = requests.post(
303
+ self.image_generation_url,
304
+ headers=image_headers,
305
+ json=image_generation_data,
306
+ verify=False,
307
+ timeout=self.model_timeout
308
+ )
309
+ response.raise_for_status()
310
+ return response.json()
311
+
312
+ except requests.exceptions.HTTPError as e:
313
+ logger.error(f"Image generation failed: {e.response.status_code} - {e.response.text}")
314
+ raise
315
+ except requests.exceptions.RequestException as e:
316
+ logger.error(f"Image generation request failed: {e}")
317
+ raise
204
318
 
205
319
  def get_app_version_details(self, application_id: int, application_version_id: int) -> dict:
206
320
  url = f"{self.application_versions}/{application_id}/{application_version_id}"
@@ -232,7 +346,8 @@ class AlitaClient:
232
346
  app_type=None, memory=None, runtime='langchain',
233
347
  application_variables: Optional[dict] = None,
234
348
  version_details: Optional[dict] = None, store: Optional[BaseStore] = None,
235
- llm: Optional[ChatOpenAI] = None):
349
+ llm: Optional[ChatOpenAI] = None, mcp_tokens: Optional[dict] = None,
350
+ conversation_id: Optional[str] = None):
236
351
  if tools is None:
237
352
  tools = []
238
353
  if chat_history is None:
@@ -252,11 +367,15 @@ class AlitaClient:
252
367
  if var['name'] in application_variables:
253
368
  var.update(application_variables[var['name']])
254
369
  if llm is None:
370
+ max_tokens = data['llm_settings'].get('max_tokens', 4000)
371
+ if max_tokens == -1:
372
+ # default nuber for case when auto is selected for agent
373
+ max_tokens = 4000
255
374
  llm = self.get_llm(
256
375
  model_name=data['llm_settings']['model_name'],
257
376
  model_config={
258
- "max_tokens": data['llm_settings']['max_tokens'],
259
- "top_p": data['llm_settings']['top_p'],
377
+ "max_tokens": max_tokens,
378
+ "reasoning_effort": data['llm_settings'].get('reasoning_effort'),
260
379
  "temperature": data['llm_settings']['temperature'],
261
380
  "model_project_id": data['llm_settings'].get('model_project_id'),
262
381
  }
@@ -271,13 +390,18 @@ class AlitaClient:
271
390
  app_type = "react"
272
391
  elif app_type == 'autogen':
273
392
  app_type = "react"
393
+
394
+ # LangChainAssistant constructor calls get_tools() which may raise McpAuthorizationRequired
395
+ # The exception will propagate naturally to the indexer worker's outer handler
274
396
  if runtime == 'nonrunnable':
275
397
  return LangChainAssistant(self, data, llm, chat_history, app_type,
276
- tools=tools, memory=memory, store=store)
398
+ tools=tools, memory=memory, store=store, mcp_tokens=mcp_tokens,
399
+ conversation_id=conversation_id)
277
400
  if runtime == 'langchain':
278
401
  return LangChainAssistant(self, data, llm,
279
402
  chat_history, app_type,
280
- tools=tools, memory=memory, store=store).runnable()
403
+ tools=tools, memory=memory, store=store, mcp_tokens=mcp_tokens,
404
+ conversation_id=conversation_id).runnable()
281
405
  elif runtime == 'llama':
282
406
  raise NotImplementedError("LLama runtime is not supported")
283
407
 
@@ -339,16 +463,50 @@ class AlitaClient:
339
463
  return self._process_requst(resp)
340
464
 
341
465
  def list_artifacts(self, bucket_name: str):
342
- url = f'{self.artifacts_url}/{bucket_name}'
466
+ # Ensure bucket name is lowercase as required by the API
467
+ url = f'{self.artifacts_url}/{bucket_name.lower()}'
343
468
  data = requests.get(url, headers=self.headers, verify=False)
344
469
  return self._process_requst(data)
345
470
 
346
471
  def create_artifact(self, bucket_name, artifact_name, artifact_data):
472
+ # Sanitize filename to prevent regex errors during indexing
473
+ sanitized_name, was_modified = self._sanitize_artifact_name(artifact_name)
474
+ if was_modified:
475
+ logger.warning(f"Artifact filename sanitized: '{artifact_name}' -> '{sanitized_name}'")
476
+
347
477
  url = f'{self.artifacts_url}/{bucket_name.lower()}'
348
478
  data = requests.post(url, headers=self.headers, files={
349
- 'file': (artifact_name, artifact_data)
479
+ 'file': (sanitized_name, artifact_data)
350
480
  }, verify=False)
351
481
  return self._process_requst(data)
482
+
483
+ @staticmethod
484
+ def _sanitize_artifact_name(filename: str) -> tuple:
485
+ """Sanitize filename for safe storage and regex pattern matching."""
486
+ import re
487
+ from pathlib import Path
488
+
489
+ if not filename or not filename.strip():
490
+ return "unnamed_file", True
491
+
492
+ original = filename
493
+ path_obj = Path(filename)
494
+ name = path_obj.stem
495
+ extension = path_obj.suffix
496
+
497
+ # Whitelist: alphanumeric, underscore, hyphen, space, Unicode letters/digits
498
+ sanitized_name = re.sub(r'[^\w\s-]', '', name, flags=re.UNICODE)
499
+ sanitized_name = re.sub(r'[-\s]+', '-', sanitized_name)
500
+ sanitized_name = sanitized_name.strip('-').strip()
501
+
502
+ if not sanitized_name:
503
+ sanitized_name = "file"
504
+
505
+ if extension:
506
+ extension = re.sub(r'[^\w.-]', '', extension, flags=re.UNICODE)
507
+
508
+ sanitized = sanitized_name + extension
509
+ return sanitized, (sanitized != original)
352
510
 
353
511
  def download_artifact(self, bucket_name, artifact_name):
354
512
  url = f'{self.artifact_url}/{bucket_name.lower()}/{artifact_name}'
@@ -490,25 +648,29 @@ class AlitaClient:
490
648
  monitoring_meta = tasknode_task.meta.get("monitoring", {})
491
649
  return monitoring_meta["user_id"]
492
650
  except Exception as e:
493
- logger.warning(f"Error: Could not determine user ID for MCP tool: {e}")
651
+ logger.debug(f"Error: Could not determine user ID for MCP tool: {e}")
494
652
  return None
495
653
 
496
654
  def predict_agent(self, llm: ChatOpenAI, instructions: str = "You are a helpful assistant.",
497
655
  tools: Optional[list] = None, chat_history: Optional[List[Any]] = None,
498
656
  memory=None, runtime='langchain', variables: Optional[list] = None,
499
- store: Optional[BaseStore] = None):
657
+ store: Optional[BaseStore] = None, debug_mode: Optional[bool] = False,
658
+ mcp_tokens: Optional[dict] = None, conversation_id: Optional[str] = None):
500
659
  """
501
660
  Create a predict-type agent with minimal configuration.
502
661
 
503
662
  Args:
504
663
  llm: The LLM to use
505
664
  instructions: System instructions for the agent
506
- tools: Optional list of tools to provide to the agent
665
+ tools: Optional list of tool configurations (not tool instances) to provide to the agent.
666
+ Tool configs will be processed through get_tools() to create tool instances.
667
+ Each tool config should have 'type', 'settings', etc.
507
668
  chat_history: Optional chat history
508
669
  memory: Optional memory/checkpointer
509
670
  runtime: Runtime type (default: 'langchain')
510
671
  variables: Optional list of variables for the agent
511
672
  store: Optional store for memory
673
+ debug_mode: Enable debug mode for cases when assistant can be initialized without tools
512
674
 
513
675
  Returns:
514
676
  Runnable agent ready for execution
@@ -522,17 +684,32 @@ class AlitaClient:
522
684
 
523
685
  # Create a minimal data structure for predict agent
524
686
  # All LLM settings are taken from the passed client instance
687
+ # Note: 'tools' here are tool CONFIGURATIONS, not tool instances
688
+ # They will be converted to tool instances by LangChainAssistant via get_tools()
525
689
  agent_data = {
526
690
  'instructions': instructions,
527
- 'tools': tools, # Tools are handled separately in predict agents
691
+ 'tools': tools, # Tool configs that will be processed by get_tools()
528
692
  'variables': variables
529
693
  }
530
- return LangChainAssistant(self, agent_data, llm,
531
- chat_history, "predict", memory=memory, store=store).runnable()
694
+
695
+ # LangChainAssistant constructor calls get_tools() which may raise McpAuthorizationRequired
696
+ # The exception will propagate naturally to the indexer worker's outer handler
697
+ return LangChainAssistant(
698
+ self,
699
+ agent_data,
700
+ llm,
701
+ chat_history,
702
+ "predict",
703
+ memory=memory,
704
+ store=store,
705
+ debug_mode=debug_mode,
706
+ mcp_tokens=mcp_tokens,
707
+ conversation_id=conversation_id
708
+ ).runnable()
532
709
 
533
710
  def test_toolkit_tool(self, toolkit_config: dict, tool_name: str, tool_params: dict = None,
534
711
  runtime_config: dict = None, llm_model: str = None,
535
- llm_config: dict = None) -> dict:
712
+ llm_config: dict = None, mcp_tokens: dict = None) -> dict:
536
713
  """
537
714
  Test a single tool from a toolkit with given parameters and runtime callbacks.
538
715
 
@@ -551,6 +728,7 @@ class AlitaClient:
551
728
  - configurable: Additional configuration parameters
552
729
  - tags: Tags for the execution
553
730
  llm_model: Name of the LLM model to use (default: 'gpt-4o-mini')
731
+ mcp_tokens: Optional dictionary of MCP OAuth tokens by server URL
554
732
  llm_config: Configuration for the LLM containing:
555
733
  - max_tokens: Maximum tokens for response (default: 1000)
556
734
  - temperature: Temperature for response generation (default: 0.1)
@@ -598,7 +776,6 @@ class AlitaClient:
598
776
  llm_config = {
599
777
  'max_tokens': 1024,
600
778
  'temperature': 0.1,
601
- 'top_p': 1.0
602
779
  }
603
780
  import logging
604
781
  logger = logging.getLogger(__name__)
@@ -669,7 +846,23 @@ class AlitaClient:
669
846
  }
670
847
 
671
848
  # Instantiate the toolkit with client and LLM support
672
- tools = instantiate_toolkit_with_client(toolkit_config, llm, self)
849
+ try:
850
+ tools = instantiate_toolkit_with_client(toolkit_config, llm, self, mcp_tokens=mcp_tokens, use_prefix=False)
851
+ except McpAuthorizationRequired:
852
+ # Re-raise McpAuthorizationRequired to allow proper handling upstream
853
+ logger.info(f"McpAuthorizationRequired detected, re-raising")
854
+ raise
855
+ except Exception as toolkit_error:
856
+ # For other errors, return error response
857
+ return {
858
+ "success": False,
859
+ "error": f"Failed to instantiate toolkit '{toolkit_config.get('toolkit_name')}': {str(toolkit_error)}",
860
+ "tool_name": tool_name,
861
+ "toolkit_config": toolkit_config_parsed_json,
862
+ "llm_model": llm_model,
863
+ "events_dispatched": events_dispatched,
864
+ "execution_time_seconds": 0.0
865
+ }
673
866
 
674
867
  if not tools:
675
868
  return {
@@ -745,7 +938,6 @@ class AlitaClient:
745
938
  if target_tool is None:
746
939
  available_tools = []
747
940
  base_available_tools = []
748
- full_available_tools = []
749
941
 
750
942
  for tool in tools:
751
943
  tool_name_attr = None
@@ -762,16 +954,14 @@ class AlitaClient:
762
954
  if base_name not in base_available_tools:
763
955
  base_available_tools.append(base_name)
764
956
 
765
- # Track full names separately
766
- if TOOLKIT_SPLITTER in tool_name_attr:
767
- full_available_tools.append(tool_name_attr)
768
-
769
957
  # Create comprehensive error message
770
- error_msg = f"Tool '{tool_name}' not found in toolkit '{toolkit_config.get('toolkit_name')}'."
958
+ error_msg = f"Tool '{tool_name}' not found in toolkit '{toolkit_config.get('toolkit_name')}'.\n"
959
+
960
+ # Custom error for index tools
961
+ if toolkit_name in [tool.value for tool in IndexTools]:
962
+ error_msg += f" Please make sure proper PGVector configuration and embedding model are set in the platform.\n"
771
963
 
772
- if base_available_tools and full_available_tools:
773
- error_msg += f" Available tools: {base_available_tools} (base names) or {full_available_tools} (full names)"
774
- elif base_available_tools:
964
+ if base_available_tools:
775
965
  error_msg += f" Available tools: {base_available_tools}"
776
966
  elif available_tools:
777
967
  error_msg += f" Available tools: {available_tools}"
@@ -780,10 +970,7 @@ class AlitaClient:
780
970
 
781
971
  # Add helpful hint about naming conventions
782
972
  if '___' in tool_name:
783
- error_msg += f" Note: You provided a full name '{tool_name}'. Try using just the base name '{extract_base_tool_name(tool_name)}'."
784
- elif full_available_tools:
785
- possible_full_name = create_full_tool_name(tool_name, toolkit_name)
786
- error_msg += f" Note: You provided a base name '{tool_name}'. The full name might be '{possible_full_name}'."
973
+ error_msg += f" Note: Tool names no longer use '___' prefixes. Try using just the base name '{extract_base_tool_name(tool_name)}'."
787
974
 
788
975
  return {
789
976
  "success": False,
@@ -889,6 +1076,9 @@ class AlitaClient:
889
1076
  }
890
1077
 
891
1078
  except Exception as e:
1079
+ # Re-raise McpAuthorizationRequired to allow proper handling upstream
1080
+ if isinstance(e, McpAuthorizationRequired):
1081
+ raise
892
1082
  logger = logging.getLogger(__name__)
893
1083
  logger.error(f"Error in test_toolkit_tool: {str(e)}")
894
1084
  return {
@@ -900,24 +1090,3 @@ class AlitaClient:
900
1090
  "events_dispatched": [],
901
1091
  "execution_time_seconds": 0.0
902
1092
  }
903
-
904
- def _get_real_user_id(self) -> str:
905
- """Extract the real user ID from the auth token for MCP tool calls."""
906
- try:
907
- import base64
908
- import json
909
- # Assuming JWT token, extract user ID from payload
910
- # This is a basic implementation - adjust based on your token format
911
- token_parts = self.auth_token.split('.')
912
- if len(token_parts) >= 2:
913
- payload_part = token_parts[1]
914
- # Add padding if needed
915
- padding = len(payload_part) % 4
916
- if padding:
917
- payload_part += '=' * (4 - padding)
918
- payload = json.loads(base64.b64decode(payload_part))
919
- return payload.get('user_id') or payload.get('sub') or payload.get('uid')
920
- except Exception as e:
921
- logger.error(f"Error extracting user ID from token: {e}")
922
- return None
923
-