alita-sdk 0.3.379__py3-none-any.whl → 0.3.627__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 (278) 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 +156 -0
  6. alita_sdk/cli/agent_loader.py +245 -0
  7. alita_sdk/cli/agent_ui.py +228 -0
  8. alita_sdk/cli/agents.py +3113 -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 +1073 -0
  21. alita_sdk/cli/mcp_loader.py +315 -0
  22. alita_sdk/cli/testcases/__init__.py +94 -0
  23. alita_sdk/cli/testcases/data_generation.py +119 -0
  24. alita_sdk/cli/testcases/discovery.py +96 -0
  25. alita_sdk/cli/testcases/executor.py +84 -0
  26. alita_sdk/cli/testcases/logger.py +85 -0
  27. alita_sdk/cli/testcases/parser.py +172 -0
  28. alita_sdk/cli/testcases/prompts.py +91 -0
  29. alita_sdk/cli/testcases/reporting.py +125 -0
  30. alita_sdk/cli/testcases/setup.py +108 -0
  31. alita_sdk/cli/testcases/test_runner.py +282 -0
  32. alita_sdk/cli/testcases/utils.py +39 -0
  33. alita_sdk/cli/testcases/validation.py +90 -0
  34. alita_sdk/cli/testcases/workflow.py +196 -0
  35. alita_sdk/cli/toolkit.py +327 -0
  36. alita_sdk/cli/toolkit_loader.py +85 -0
  37. alita_sdk/cli/tools/__init__.py +43 -0
  38. alita_sdk/cli/tools/approval.py +224 -0
  39. alita_sdk/cli/tools/filesystem.py +1751 -0
  40. alita_sdk/cli/tools/planning.py +389 -0
  41. alita_sdk/cli/tools/terminal.py +414 -0
  42. alita_sdk/community/__init__.py +72 -12
  43. alita_sdk/community/inventory/__init__.py +236 -0
  44. alita_sdk/community/inventory/config.py +257 -0
  45. alita_sdk/community/inventory/enrichment.py +2137 -0
  46. alita_sdk/community/inventory/extractors.py +1469 -0
  47. alita_sdk/community/inventory/ingestion.py +3172 -0
  48. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  49. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  50. alita_sdk/community/inventory/parsers/base.py +295 -0
  51. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  52. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  53. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  54. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  55. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  56. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  57. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  58. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  59. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  60. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  61. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  62. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  63. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  64. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  65. alita_sdk/community/inventory/patterns/loader.py +348 -0
  66. alita_sdk/community/inventory/patterns/registry.py +198 -0
  67. alita_sdk/community/inventory/presets.py +535 -0
  68. alita_sdk/community/inventory/retrieval.py +1403 -0
  69. alita_sdk/community/inventory/toolkit.py +173 -0
  70. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  71. alita_sdk/community/inventory/visualize.py +1370 -0
  72. alita_sdk/configurations/__init__.py +1 -1
  73. alita_sdk/configurations/ado.py +141 -20
  74. alita_sdk/configurations/bitbucket.py +94 -2
  75. alita_sdk/configurations/confluence.py +130 -1
  76. alita_sdk/configurations/figma.py +76 -0
  77. alita_sdk/configurations/gitlab.py +91 -0
  78. alita_sdk/configurations/jira.py +103 -0
  79. alita_sdk/configurations/openapi.py +329 -0
  80. alita_sdk/configurations/qtest.py +72 -1
  81. alita_sdk/configurations/report_portal.py +96 -0
  82. alita_sdk/configurations/sharepoint.py +148 -0
  83. alita_sdk/configurations/testio.py +83 -0
  84. alita_sdk/configurations/testrail.py +88 -0
  85. alita_sdk/configurations/xray.py +93 -0
  86. alita_sdk/configurations/zephyr_enterprise.py +93 -0
  87. alita_sdk/configurations/zephyr_essential.py +75 -0
  88. alita_sdk/runtime/clients/artifact.py +3 -3
  89. alita_sdk/runtime/clients/client.py +388 -46
  90. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  91. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  92. alita_sdk/runtime/clients/sandbox_client.py +8 -21
  93. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  94. alita_sdk/runtime/langchain/assistant.py +157 -39
  95. alita_sdk/runtime/langchain/constants.py +647 -1
  96. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  97. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -4
  100. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  101. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  102. alita_sdk/runtime/langchain/document_loaders/constants.py +40 -19
  103. alita_sdk/runtime/langchain/langraph_agent.py +405 -84
  104. alita_sdk/runtime/langchain/utils.py +106 -7
  105. alita_sdk/runtime/llms/preloaded.py +2 -6
  106. alita_sdk/runtime/models/mcp_models.py +61 -0
  107. alita_sdk/runtime/skills/__init__.py +91 -0
  108. alita_sdk/runtime/skills/callbacks.py +498 -0
  109. alita_sdk/runtime/skills/discovery.py +540 -0
  110. alita_sdk/runtime/skills/executor.py +610 -0
  111. alita_sdk/runtime/skills/input_builder.py +371 -0
  112. alita_sdk/runtime/skills/models.py +330 -0
  113. alita_sdk/runtime/skills/registry.py +355 -0
  114. alita_sdk/runtime/skills/skill_runner.py +330 -0
  115. alita_sdk/runtime/toolkits/__init__.py +31 -0
  116. alita_sdk/runtime/toolkits/application.py +29 -10
  117. alita_sdk/runtime/toolkits/artifact.py +20 -11
  118. alita_sdk/runtime/toolkits/datasource.py +13 -6
  119. alita_sdk/runtime/toolkits/mcp.py +783 -0
  120. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  121. alita_sdk/runtime/toolkits/planning.py +178 -0
  122. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  123. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  124. alita_sdk/runtime/toolkits/tools.py +356 -69
  125. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  126. alita_sdk/runtime/tools/__init__.py +10 -3
  127. alita_sdk/runtime/tools/application.py +27 -6
  128. alita_sdk/runtime/tools/artifact.py +511 -28
  129. alita_sdk/runtime/tools/data_analysis.py +183 -0
  130. alita_sdk/runtime/tools/function.py +67 -35
  131. alita_sdk/runtime/tools/graph.py +10 -4
  132. alita_sdk/runtime/tools/image_generation.py +148 -46
  133. alita_sdk/runtime/tools/llm.py +1003 -128
  134. alita_sdk/runtime/tools/loop.py +3 -1
  135. alita_sdk/runtime/tools/loop_output.py +3 -1
  136. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  137. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  138. alita_sdk/runtime/tools/mcp_server_tool.py +8 -5
  139. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  140. alita_sdk/runtime/tools/planning/models.py +246 -0
  141. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  142. alita_sdk/runtime/tools/router.py +2 -4
  143. alita_sdk/runtime/tools/sandbox.py +65 -48
  144. alita_sdk/runtime/tools/skill_router.py +776 -0
  145. alita_sdk/runtime/tools/tool.py +3 -1
  146. alita_sdk/runtime/tools/vectorstore.py +9 -3
  147. alita_sdk/runtime/tools/vectorstore_base.py +70 -14
  148. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  149. alita_sdk/runtime/utils/constants.py +5 -1
  150. alita_sdk/runtime/utils/mcp_client.py +492 -0
  151. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  152. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  153. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  154. alita_sdk/runtime/utils/serialization.py +155 -0
  155. alita_sdk/runtime/utils/streamlit.py +40 -13
  156. alita_sdk/runtime/utils/toolkit_utils.py +30 -9
  157. alita_sdk/runtime/utils/utils.py +36 -0
  158. alita_sdk/tools/__init__.py +134 -35
  159. alita_sdk/tools/ado/repos/__init__.py +51 -32
  160. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  161. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  162. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  163. alita_sdk/tools/ado/utils.py +1 -18
  164. alita_sdk/tools/ado/wiki/__init__.py +25 -12
  165. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  166. alita_sdk/tools/ado/work_item/__init__.py +26 -13
  167. alita_sdk/tools/ado/work_item/ado_wrapper.py +73 -11
  168. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  169. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  170. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  171. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  172. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  173. alita_sdk/tools/base/tool.py +5 -1
  174. alita_sdk/tools/base_indexer_toolkit.py +271 -84
  175. alita_sdk/tools/bitbucket/__init__.py +17 -11
  176. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  177. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  178. alita_sdk/tools/browser/__init__.py +5 -4
  179. alita_sdk/tools/carrier/__init__.py +5 -6
  180. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  181. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  182. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  183. alita_sdk/tools/chunkers/__init__.py +3 -1
  184. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  185. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  186. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  187. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  188. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  189. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  190. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  191. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  192. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  193. alita_sdk/tools/code/linter/__init__.py +10 -8
  194. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  195. alita_sdk/tools/code/sonar/__init__.py +11 -8
  196. alita_sdk/tools/code_indexer_toolkit.py +82 -22
  197. alita_sdk/tools/confluence/__init__.py +22 -16
  198. alita_sdk/tools/confluence/api_wrapper.py +107 -30
  199. alita_sdk/tools/confluence/loader.py +14 -2
  200. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  201. alita_sdk/tools/elastic/__init__.py +11 -8
  202. alita_sdk/tools/elitea_base.py +493 -30
  203. alita_sdk/tools/figma/__init__.py +58 -11
  204. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  205. alita_sdk/tools/figma/figma_client.py +73 -0
  206. alita_sdk/tools/figma/toon_tools.py +2748 -0
  207. alita_sdk/tools/github/__init__.py +14 -15
  208. alita_sdk/tools/github/github_client.py +224 -100
  209. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  210. alita_sdk/tools/github/schemas.py +14 -5
  211. alita_sdk/tools/github/tool.py +5 -1
  212. alita_sdk/tools/github/tool_prompts.py +9 -22
  213. alita_sdk/tools/gitlab/__init__.py +16 -11
  214. alita_sdk/tools/gitlab/api_wrapper.py +218 -48
  215. alita_sdk/tools/gitlab_org/__init__.py +10 -9
  216. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  217. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  218. alita_sdk/tools/google/bigquery/tool.py +5 -1
  219. alita_sdk/tools/google_places/__init__.py +11 -8
  220. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  221. alita_sdk/tools/jira/__init__.py +17 -10
  222. alita_sdk/tools/jira/api_wrapper.py +92 -41
  223. alita_sdk/tools/keycloak/__init__.py +11 -8
  224. alita_sdk/tools/localgit/__init__.py +9 -3
  225. alita_sdk/tools/localgit/local_git.py +62 -54
  226. alita_sdk/tools/localgit/tool.py +5 -1
  227. alita_sdk/tools/memory/__init__.py +12 -4
  228. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  229. alita_sdk/tools/ocr/__init__.py +11 -8
  230. alita_sdk/tools/openapi/__init__.py +491 -106
  231. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  232. alita_sdk/tools/openapi/tool.py +20 -0
  233. alita_sdk/tools/pandas/__init__.py +20 -12
  234. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  235. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  236. alita_sdk/tools/postman/__init__.py +10 -9
  237. alita_sdk/tools/pptx/__init__.py +11 -10
  238. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  239. alita_sdk/tools/qtest/__init__.py +31 -11
  240. alita_sdk/tools/qtest/api_wrapper.py +2135 -86
  241. alita_sdk/tools/rally/__init__.py +10 -9
  242. alita_sdk/tools/rally/api_wrapper.py +1 -1
  243. alita_sdk/tools/report_portal/__init__.py +12 -8
  244. alita_sdk/tools/salesforce/__init__.py +10 -8
  245. alita_sdk/tools/servicenow/__init__.py +17 -15
  246. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  247. alita_sdk/tools/sharepoint/__init__.py +10 -7
  248. alita_sdk/tools/sharepoint/api_wrapper.py +129 -38
  249. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  250. alita_sdk/tools/sharepoint/utils.py +8 -2
  251. alita_sdk/tools/slack/__init__.py +10 -7
  252. alita_sdk/tools/slack/api_wrapper.py +2 -2
  253. alita_sdk/tools/sql/__init__.py +12 -9
  254. alita_sdk/tools/testio/__init__.py +10 -7
  255. alita_sdk/tools/testrail/__init__.py +11 -10
  256. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  257. alita_sdk/tools/utils/__init__.py +9 -4
  258. alita_sdk/tools/utils/content_parser.py +103 -18
  259. alita_sdk/tools/utils/text_operations.py +410 -0
  260. alita_sdk/tools/utils/tool_prompts.py +79 -0
  261. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +30 -13
  262. alita_sdk/tools/xray/__init__.py +13 -9
  263. alita_sdk/tools/yagmail/__init__.py +9 -3
  264. alita_sdk/tools/zephyr/__init__.py +10 -7
  265. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -7
  266. alita_sdk/tools/zephyr_essential/__init__.py +10 -7
  267. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  268. alita_sdk/tools/zephyr_essential/client.py +2 -2
  269. alita_sdk/tools/zephyr_scale/__init__.py +11 -8
  270. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  271. alita_sdk/tools/zephyr_squad/__init__.py +10 -7
  272. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +154 -8
  273. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  274. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  275. alita_sdk-0.3.379.dist-info/RECORD +0 -360
  276. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  277. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  278. {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.627.dist-info}/top_level.txt +0 -0
@@ -86,7 +86,9 @@ Answer must be JSON only extractable by JSON.LOADS."""
86
86
  else:
87
87
  input_[-1].content += self.unstructured_output
88
88
  completion = self.client.invoke(input_, config=config)
89
- result = _extract_json(completion.content.strip())
89
+ from ..langchain.utils import extract_text_from_completion
90
+ content_text = extract_text_from_completion(completion)
91
+ result = _extract_json(content_text.strip())
90
92
  logger.info(f"ToolNode tool params: {result}")
91
93
  try:
92
94
  # handler for application added as a tool
@@ -12,9 +12,11 @@ from alita_sdk.tools.vector_adapters.VectorStoreAdapter import VectorStoreAdapte
12
12
  from logging import getLogger
13
13
 
14
14
  from ..utils.logging import dispatch_custom_event
15
+ from ..langchain.utils import extract_text_from_completion
15
16
 
16
17
  logger = getLogger(__name__)
17
18
 
19
+
18
20
  class IndexDocumentsModel(BaseModel):
19
21
  documents: Any = Field(description="Generator of documents to index")
20
22
 
@@ -414,7 +416,8 @@ class VectorStoreWrapper(BaseToolApiWrapper):
414
416
  return {"status": "error", "message": f"Error: {format_exc()}"}
415
417
  if _documents:
416
418
  add_documents(vectorstore=self.vectorstore, documents=_documents)
417
- return {"status": "ok", "message": f"successfully indexed {documents_count} documents"}
419
+ return {"status": "ok", "message": f"successfully indexed {documents_count} documents" if documents_count > 0
420
+ else "No new documents to index."}
418
421
 
419
422
  def search_documents(self, query:str, doctype: str = 'code',
420
423
  filter:dict|str={}, cut_off: float=0.5,
@@ -683,8 +686,10 @@ class VectorStoreWrapper(BaseToolApiWrapper):
683
686
  ]
684
687
  )
685
688
  ])
689
+ # Extract text content safely (handles both string and list content from thinking models)
690
+ search_query = extract_text_from_completion(result)
686
691
  search_results = self.search_documents(
687
- result.content, doctype, filter, cut_off, search_top,
692
+ search_query, doctype, filter, cut_off, search_top,
688
693
  full_text_search=full_text_search,
689
694
  reranking_config=reranking_config,
690
695
  extended_search=extended_search
@@ -713,7 +718,8 @@ class VectorStoreWrapper(BaseToolApiWrapper):
713
718
  ]
714
719
  )
715
720
  ])
716
- return result.content
721
+ # Extract text content safely (handles both string and list content from thinking models)
722
+ return extract_text_from_completion(result)
717
723
 
718
724
  def _log_data(self, message: str, tool_name: str = "index_data"):
719
725
  """Log data and dispatch custom event for indexing progress"""
@@ -1,9 +1,9 @@
1
1
  import json
2
- import math
3
2
  from collections import OrderedDict
4
3
  from logging import getLogger
5
4
  from typing import Any, Optional, List, Dict, Generator
6
5
 
6
+ import math
7
7
  from langchain_core.documents import Document
8
8
  from langchain_core.messages import HumanMessage
9
9
  from langchain_core.tools import ToolException
@@ -12,10 +12,12 @@ from pydantic import BaseModel, model_validator, Field
12
12
 
13
13
  from alita_sdk.tools.elitea_base import BaseToolApiWrapper
14
14
  from alita_sdk.tools.vector_adapters.VectorStoreAdapter import VectorStoreAdapterFactory
15
- from ..utils.logging import dispatch_custom_event
15
+ from ...runtime.utils.utils import IndexerKeywords
16
+ from ...runtime.langchain.utils import extract_text_from_completion
16
17
 
17
18
  logger = getLogger(__name__)
18
19
 
20
+
19
21
  class IndexDocumentsModel(BaseModel):
20
22
  documents: Any = Field(description="Generator of documents to index")
21
23
 
@@ -155,15 +157,45 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
155
157
  if values.get('alita') and values.get('embedding_model'):
156
158
  values['embeddings'] = values.get('alita').get_embeddings(values.get('embedding_model'))
157
159
 
158
- if values.get('vectorstore_type') and values.get('vectorstore_params') and values.get('embedding_model'):
159
- values['vectorstore'] = get_vectorstore(values['vectorstore_type'], values['vectorstore_params'], embedding_func=values['embeddings'])
160
- # Initialize the new vector adapter
161
- values['vector_adapter'] = VectorStoreAdapterFactory.create_adapter(values['vectorstore_type'])
162
- logger.debug(f"Vectorstore wrapper initialized: {values}")
160
+ # Lazy initialization: vectorstore and vector_adapter are initialized on-demand
161
+ # This prevents errors when using non-index tools with broken/missing vector DB
163
162
  return values
164
163
 
164
+ def _ensure_vectorstore_initialized(self):
165
+ """Lazily initialize vectorstore and vector_adapter when needed for index operations."""
166
+ if self.vectorstore is None:
167
+ if not self.vectorstore_type or not self.vectorstore_params:
168
+ raise ToolException(
169
+ "Vector store is not configured. "
170
+ "Please ensure embedding_model and pgvector_configuration are provided."
171
+ )
172
+
173
+ from ..langchain.interfaces.llm_processor import get_vectorstore
174
+ try:
175
+ self.vectorstore = get_vectorstore(
176
+ self.vectorstore_type,
177
+ self.vectorstore_params,
178
+ embedding_func=self.embeddings
179
+ )
180
+ logger.debug(f"Vectorstore initialized: {self.vectorstore_type}")
181
+ except Exception as e:
182
+ raise ToolException(
183
+ f"Failed to initialize vector store: {str(e)}. "
184
+ "Check your vector database configuration and connection."
185
+ )
186
+
187
+ if self.vector_adapter is None:
188
+ try:
189
+ self.vector_adapter = VectorStoreAdapterFactory.create_adapter(self.vectorstore_type)
190
+ logger.debug(f"Vector adapter initialized: {self.vectorstore_type}")
191
+ except Exception as e:
192
+ raise ToolException(
193
+ f"Failed to initialize vector adapter: {str(e)}"
194
+ )
195
+
165
196
  def _init_pg_helper(self, language='english'):
166
197
  """Initialize PGVector helper if needed and not already initialized"""
198
+ self._ensure_vectorstore_initialized()
167
199
  if self.pg_helper is None and hasattr(self.vectorstore, 'connection_string') and hasattr(self.vectorstore, 'collection_name'):
168
200
  try:
169
201
  from .pgvector_search import PGVectorSearch
@@ -192,6 +224,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
192
224
  Raises:
193
225
  ToolException: When DataException occurs or other search errors
194
226
  """
227
+ self._ensure_vectorstore_initialized()
195
228
  try:
196
229
  return self.vectorstore.similarity_search_with_score(
197
230
  query, filter=filter, k=k
@@ -210,27 +243,45 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
210
243
 
211
244
  def list_collections(self) -> List[str]:
212
245
  """List all collections in the vectorstore."""
213
-
246
+ self._ensure_vectorstore_initialized()
214
247
  collections = self.vector_adapter.list_collections(self)
215
248
  if not collections:
216
249
  return "No indexed collections"
217
250
  return collections
218
251
 
219
252
  def get_index_meta(self, index_name: str):
253
+ self._ensure_vectorstore_initialized()
220
254
  index_metas = self.vector_adapter.get_index_meta(self, index_name)
221
255
  if len(index_metas) > 1:
222
256
  raise RuntimeError(f"Multiple index_meta documents found: {index_metas}")
223
257
  return index_metas[0] if index_metas else None
224
258
 
225
- def _clean_collection(self, index_name: str = ''):
259
+ def get_indexed_count(self, index_name: str) -> int:
260
+ self._ensure_vectorstore_initialized()
261
+ from sqlalchemy.orm import Session
262
+ from sqlalchemy import func, or_
263
+
264
+ with Session(self.vectorstore.session_maker.bind) as session:
265
+ return session.query(
266
+ self.vectorstore.EmbeddingStore.id,
267
+ ).filter(
268
+ func.jsonb_extract_path_text(self.vectorstore.EmbeddingStore.cmetadata, 'collection') == index_name,
269
+ or_(
270
+ func.jsonb_extract_path_text(self.vectorstore.EmbeddingStore.cmetadata, 'type').is_(None),
271
+ func.jsonb_extract_path_text(self.vectorstore.EmbeddingStore.cmetadata, 'type') != IndexerKeywords.INDEX_META_TYPE.value
272
+ )
273
+ ).count()
274
+
275
+ def _clean_collection(self, index_name: str = '', including_index_meta: bool = False):
226
276
  """
227
277
  Clean the vectorstore collection by deleting all indexed data.
228
278
  """
279
+ self._ensure_vectorstore_initialized()
229
280
  self._log_tool_event(
230
281
  f"Cleaning collection '{self.dataset}'",
231
282
  tool_name="_clean_collection"
232
283
  )
233
- self.vector_adapter.clean_collection(self, index_name)
284
+ self.vector_adapter.clean_collection(self, index_name, including_index_meta)
234
285
  self._log_tool_event(
235
286
  f"Collection '{self.dataset}' has been cleaned. ",
236
287
  tool_name="_clean_collection"
@@ -244,6 +295,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
244
295
  progress_step (int): Step for progress reporting, default is 20.
245
296
  clean_index (bool): If True, clean the index before re-indexing all documents.
246
297
  """
298
+ self._ensure_vectorstore_initialized()
247
299
  if clean_index:
248
300
  self._clean_index(index_name)
249
301
 
@@ -253,7 +305,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
253
305
  logger.info("Cleaning index before re-indexing all documents.")
254
306
  self._log_tool_event("Cleaning index before re-indexing all documents. Previous index will be removed", tool_name="index_documents")
255
307
  try:
256
- self._clean_collection(index_name)
308
+ self._clean_collection(index_name, including_index_meta=False)
257
309
  self._log_tool_event("Previous index has been removed",
258
310
  tool_name="index_documents")
259
311
  except Exception as e:
@@ -308,7 +360,8 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
308
360
  return {"status": "error", "message": f"Error: {format_exc()}"}
309
361
  if _documents:
310
362
  add_documents(vectorstore=self.vectorstore, documents=_documents)
311
- return {"status": "ok", "message": f"successfully indexed {documents_count} documents"}
363
+ return {"status": "ok", "message": f"successfully indexed {documents_count} documents" if documents_count > 0
364
+ else "no documents to index"}
312
365
 
313
366
  def search_documents(self, query:str, doctype: str = 'code',
314
367
  filter:dict|str={}, cut_off: float=0.5,
@@ -574,8 +627,10 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
574
627
  ]
575
628
  )
576
629
  ])
630
+ # Extract text content safely (handles both string and list content from thinking models)
631
+ search_query = extract_text_from_completion(result)
577
632
  search_results = self.search_documents(
578
- result.content, doctype, filter, cut_off, search_top,
633
+ search_query, doctype, filter, cut_off, search_top,
579
634
  full_text_search=full_text_search,
580
635
  reranking_config=reranking_config,
581
636
  extended_search=extended_search
@@ -604,7 +659,8 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
604
659
  ]
605
660
  )
606
661
  ])
607
- return result.content
662
+ # Extract text content safely (handles both string and list content from thinking models)
663
+ return extract_text_from_completion(result)
608
664
 
609
665
  def get_available_tools(self):
610
666
  return [
@@ -23,9 +23,45 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
23
23
  self.tokens_out = 0
24
24
  self.pending_llm_requests = defaultdict(int)
25
25
  self.current_model_name = 'gpt-4'
26
+ self._event_queue = [] # Queue for events when context is unavailable
26
27
  #
27
28
  super().__init__()
28
29
 
30
+ def _has_streamlit_context(self) -> bool:
31
+ """Check if Streamlit context is available in the current thread."""
32
+ try:
33
+ # Try to import streamlit runtime context checker
34
+ from streamlit.runtime.scriptrunner import get_script_run_ctx
35
+ ctx = get_script_run_ctx()
36
+ return ctx is not None
37
+ except (ImportError, Exception) as e:
38
+ if self.debug:
39
+ log.debug(f"Streamlit context check failed: {e}")
40
+ return False
41
+
42
+ def _safe_streamlit_call(self, func, *args, **kwargs):
43
+ """Safely execute a Streamlit UI operation, handling missing context gracefully."""
44
+ if not self._has_streamlit_context():
45
+ func_name = getattr(func, '__name__', str(func))
46
+ if self.debug:
47
+ log.warning(f"Streamlit context not available for {func_name}, queueing event")
48
+ # Store the event for potential replay when context is available
49
+ self._event_queue.append({
50
+ 'func': func_name,
51
+ 'args': args,
52
+ 'kwargs': kwargs,
53
+ 'timestamp': datetime.now(tz=timezone.utc)
54
+ })
55
+ return None
56
+
57
+ try:
58
+ return func(*args, **kwargs)
59
+ except Exception as e:
60
+ func_name = getattr(func, '__name__', str(func))
61
+ # Handle any Streamlit-specific exceptions gracefully
62
+ log.warning(f"Streamlit operation {func_name} failed: {e}")
63
+ return None
64
+
29
65
  #
30
66
  # Chain
31
67
  #
@@ -76,10 +112,14 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
76
112
  json.dumps(payload, ensure_ascii=False, default=lambda o: str(o))
77
113
  )
78
114
 
79
- self.callback_state[str(run_id)] = self.st.status(
80
- f"Running {payload.get('tool_name')}...", expanded=True
115
+ status_widget = self._safe_streamlit_call(
116
+ self.st.status,
117
+ f"Running {payload.get('tool_name')}...",
118
+ expanded=True
81
119
  )
82
- self.callback_state[str(run_id)].write(f"Tool inputs: {payload}")
120
+ if status_widget:
121
+ self.callback_state[str(run_id)] = status_widget
122
+ self._safe_streamlit_call(status_widget.write, f"Tool inputs: {payload}")
83
123
 
84
124
  def on_tool_start(self, *args, run_id: UUID, **kwargs):
85
125
  """ Callback """
@@ -88,15 +128,51 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
88
128
 
89
129
  tool_name = args[0].get("name")
90
130
  tool_run_id = str(run_id)
131
+
132
+ # Extract metadata from tool if available (from BaseAction.metadata)
133
+ # Try multiple sources for metadata with toolkit_name
134
+ tool_meta = args[0].copy()
135
+
136
+ # Source 1: kwargs['serialized']['metadata'] - LangChain's full tool serialization
137
+ if 'serialized' in kwargs and 'metadata' in kwargs['serialized']:
138
+ tool_meta['metadata'] = kwargs['serialized']['metadata']
139
+ log.info(f"[METADATA] Extracted from serialized: {kwargs['serialized']['metadata']}")
140
+ # Source 2: Check if metadata is directly in args[0] (some LangChain versions)
141
+ elif 'metadata' in args[0]:
142
+ tool_meta['metadata'] = args[0]['metadata']
143
+ log.info(f"[METADATA] Extracted from args[0]: {args[0]['metadata']}")
144
+ else:
145
+ log.info(f"[METADATA] No metadata found. args[0] keys: {list(args[0].keys())}, kwargs keys: {list(kwargs.keys())}")
146
+ # Fallback: Try to extract toolkit_name from description
147
+ description = args[0].get('description', '')
148
+ if description:
149
+ import re
150
+ # Try pattern 1: [Toolkit: name]
151
+ match = re.search(r'\[Toolkit:\s*([^\]]+)\]', description)
152
+ if not match:
153
+ # Try pattern 2: Toolkit: name at start or end
154
+ match = re.search(r'(?:^|\n)Toolkit:\s*([^\n]+)', description)
155
+ if match:
156
+ toolkit_name = match.group(1).strip()
157
+ tool_meta['metadata'] = {'toolkit_name': toolkit_name}
158
+ log.info(f"[METADATA] Extracted toolkit_name from description: {toolkit_name}")
159
+
91
160
  payload = {
92
161
  "tool_name": tool_name,
93
162
  "tool_run_id": tool_run_id,
94
- "tool_meta": args[0],
163
+ "tool_meta": tool_meta,
95
164
  "tool_inputs": kwargs.get('inputs')
96
165
  }
97
166
  payload = json.loads(json.dumps(payload, ensure_ascii=False, default=lambda o: str(o)))
98
- self.callback_state[tool_run_id] = self.st.status(f"Running {tool_name}...", expanded=True)
99
- self.callback_state[tool_run_id].write(f"Tool inputs: {kwargs.get('inputs')}")
167
+
168
+ status_widget = self._safe_streamlit_call(
169
+ self.st.status,
170
+ f"Running {tool_name}...",
171
+ expanded=True
172
+ )
173
+ if status_widget:
174
+ self.callback_state[tool_run_id] = status_widget
175
+ self._safe_streamlit_call(status_widget.write, f"Tool inputs: {kwargs.get('inputs')}")
100
176
 
101
177
  def on_tool_end(self, *args, run_id: UUID, **kwargs):
102
178
  """ Callback """
@@ -104,11 +180,16 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
104
180
  log.info("on_tool_end(%s, %s)", args, kwargs)
105
181
  tool_run_id = str(run_id)
106
182
  tool_output = args[0]
107
- if self.callback_state[tool_run_id]:
108
- self.callback_state[tool_run_id].write(f"Tool output: {tool_output}")
109
- self.callback_state[tool_run_id].update(label=f"Completed {kwargs.get('name')}", state="complete", expanded=False)
183
+ if self.callback_state.get(tool_run_id):
184
+ status_widget = self.callback_state[tool_run_id]
185
+ self._safe_streamlit_call(status_widget.write, f"Tool output: {tool_output}")
186
+ self._safe_streamlit_call(
187
+ status_widget.update,
188
+ label=f"Completed {kwargs.get('name')}",
189
+ state="complete",
190
+ expanded=False
191
+ )
110
192
  self.callback_state.pop(tool_run_id, None)
111
- del self.callback_state[run_id]
112
193
 
113
194
  def on_tool_error(self, *args, run_id: UUID, **kwargs):
114
195
  """ Callback """
@@ -116,9 +197,19 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
116
197
  log.info("on_tool_error(%s, %s)", args, kwargs)
117
198
  tool_run_id = str(run_id)
118
199
  tool_exception = args[0]
119
- self.callback_state[tool_run_id].write(f"{traceback.format_exception(tool_exception)}")
120
- self.callback_state[tool_run_id].update(label=f"Error {kwargs.get('name')}", state="error", expanded=False)
121
- self.callback_state.pop(tool_run_id, None)
200
+ if self.callback_state.get(tool_run_id):
201
+ status_widget = self.callback_state[tool_run_id]
202
+ self._safe_streamlit_call(
203
+ status_widget.write,
204
+ f"{traceback.format_exception(tool_exception)}"
205
+ )
206
+ self._safe_streamlit_call(
207
+ status_widget.update,
208
+ label=f"Error {kwargs.get('name')}",
209
+ state="error",
210
+ expanded=False
211
+ )
212
+ self.callback_state.pop(tool_run_id, None)
122
213
 
123
214
  #
124
215
  # Agent
@@ -156,8 +247,14 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
156
247
  self.current_model_name = metadata.get('ls_model_name', self.current_model_name)
157
248
  llm_run_id = str(run_id)
158
249
 
159
- self.callback_state[llm_run_id] = self.st.status(f"Running LLM ...", expanded=True)
160
- self.callback_state[llm_run_id].write(f"LLM inputs: {messages}")
250
+ status_widget = self._safe_streamlit_call(
251
+ self.st.status,
252
+ f"Running LLM ...",
253
+ expanded=True
254
+ )
255
+ if status_widget:
256
+ self.callback_state[llm_run_id] = status_widget
257
+ self._safe_streamlit_call(status_widget.write, f"LLM inputs: {messages}")
161
258
 
162
259
  def on_llm_start(self, *args, **kwargs):
163
260
  """ Callback """
@@ -178,16 +275,27 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
178
275
  content = None
179
276
  if chunk:
180
277
  content = chunk.text
181
- self.callback_state[str(run_id)].write(content)
278
+
279
+ llm_run_id = str(run_id)
280
+ if self.callback_state.get(llm_run_id):
281
+ status_widget = self.callback_state[llm_run_id]
282
+ self._safe_streamlit_call(status_widget.write, content)
182
283
 
183
284
  def on_llm_error(self, *args, run_id: UUID, **kwargs):
184
285
  """ Callback """
185
286
  if self.debug:
186
287
  log.error("on_llm_error(%s, %s)", args, kwargs)
187
288
  llm_run_id = str(run_id)
188
- self.callback_state[llm_run_id].write(f"on_llm_error({args}, {kwargs})")
189
- self.callback_state[llm_run_id].update(label=f"Error {kwargs.get('name')}", state="error", expanded=False)
190
- self.callback_state.pop(llm_run_id, None)
289
+ if self.callback_state.get(llm_run_id):
290
+ status_widget = self.callback_state[llm_run_id]
291
+ self._safe_streamlit_call(status_widget.write, f"on_llm_error({args}, {kwargs})")
292
+ self._safe_streamlit_call(
293
+ status_widget.update,
294
+ label=f"Error {kwargs.get('name')}",
295
+ state="error",
296
+ expanded=False
297
+ )
298
+ self.callback_state.pop(llm_run_id, None)
191
299
  #
192
300
  # exception = args[0]
193
301
  # FIXME: should we emit an error here too?
@@ -205,5 +313,13 @@ class AlitaStreamlitCallback(BaseCallbackHandler):
205
313
  if self.debug:
206
314
  log.debug("on_llm_end(%s, %s)", response, kwargs)
207
315
  llm_run_id = str(run_id)
208
- self.callback_state[llm_run_id].update(label=f"Completed LLM call", state="complete", expanded=False)
209
- self.callback_state.pop(llm_run_id, None)
316
+ # Check if callback_state exists and is not None before accessing
317
+ if self.callback_state is not None and self.callback_state.get(llm_run_id):
318
+ status_widget = self.callback_state[llm_run_id]
319
+ self._safe_streamlit_call(
320
+ status_widget.update,
321
+ label=f"Completed LLM call",
322
+ state="complete",
323
+ expanded=False
324
+ )
325
+ self.callback_state.pop(llm_run_id, None)
@@ -436,4 +436,8 @@ STYLES = r"""
436
436
  filter: hue-rotate(180deg) brightness(1.2);
437
437
  }
438
438
  </style>
439
- """
439
+ """
440
+
441
+ TOOLKIT_NAME_META = "toolkit_name"
442
+ TOOL_NAME_META = "tool_name"
443
+ TOOLKIT_TYPE_META = "toolkit_type"