alita-sdk 0.3.462__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 (261) hide show
  1. alita_sdk/cli/agent/__init__.py +5 -0
  2. alita_sdk/cli/agent/default.py +258 -0
  3. alita_sdk/cli/agent_executor.py +15 -3
  4. alita_sdk/cli/agent_loader.py +56 -8
  5. alita_sdk/cli/agent_ui.py +93 -31
  6. alita_sdk/cli/agents.py +2274 -230
  7. alita_sdk/cli/callbacks.py +96 -25
  8. alita_sdk/cli/cli.py +10 -1
  9. alita_sdk/cli/config.py +162 -9
  10. alita_sdk/cli/context/__init__.py +30 -0
  11. alita_sdk/cli/context/cleanup.py +198 -0
  12. alita_sdk/cli/context/manager.py +731 -0
  13. alita_sdk/cli/context/message.py +285 -0
  14. alita_sdk/cli/context/strategies.py +289 -0
  15. alita_sdk/cli/context/token_estimation.py +127 -0
  16. alita_sdk/cli/input_handler.py +419 -0
  17. alita_sdk/cli/inventory.py +1073 -0
  18. alita_sdk/cli/testcases/__init__.py +94 -0
  19. alita_sdk/cli/testcases/data_generation.py +119 -0
  20. alita_sdk/cli/testcases/discovery.py +96 -0
  21. alita_sdk/cli/testcases/executor.py +84 -0
  22. alita_sdk/cli/testcases/logger.py +85 -0
  23. alita_sdk/cli/testcases/parser.py +172 -0
  24. alita_sdk/cli/testcases/prompts.py +91 -0
  25. alita_sdk/cli/testcases/reporting.py +125 -0
  26. alita_sdk/cli/testcases/setup.py +108 -0
  27. alita_sdk/cli/testcases/test_runner.py +282 -0
  28. alita_sdk/cli/testcases/utils.py +39 -0
  29. alita_sdk/cli/testcases/validation.py +90 -0
  30. alita_sdk/cli/testcases/workflow.py +196 -0
  31. alita_sdk/cli/toolkit.py +14 -17
  32. alita_sdk/cli/toolkit_loader.py +35 -5
  33. alita_sdk/cli/tools/__init__.py +36 -2
  34. alita_sdk/cli/tools/approval.py +224 -0
  35. alita_sdk/cli/tools/filesystem.py +910 -64
  36. alita_sdk/cli/tools/planning.py +389 -0
  37. alita_sdk/cli/tools/terminal.py +414 -0
  38. alita_sdk/community/__init__.py +72 -12
  39. alita_sdk/community/inventory/__init__.py +236 -0
  40. alita_sdk/community/inventory/config.py +257 -0
  41. alita_sdk/community/inventory/enrichment.py +2137 -0
  42. alita_sdk/community/inventory/extractors.py +1469 -0
  43. alita_sdk/community/inventory/ingestion.py +3172 -0
  44. alita_sdk/community/inventory/knowledge_graph.py +1457 -0
  45. alita_sdk/community/inventory/parsers/__init__.py +218 -0
  46. alita_sdk/community/inventory/parsers/base.py +295 -0
  47. alita_sdk/community/inventory/parsers/csharp_parser.py +907 -0
  48. alita_sdk/community/inventory/parsers/go_parser.py +851 -0
  49. alita_sdk/community/inventory/parsers/html_parser.py +389 -0
  50. alita_sdk/community/inventory/parsers/java_parser.py +593 -0
  51. alita_sdk/community/inventory/parsers/javascript_parser.py +629 -0
  52. alita_sdk/community/inventory/parsers/kotlin_parser.py +768 -0
  53. alita_sdk/community/inventory/parsers/markdown_parser.py +362 -0
  54. alita_sdk/community/inventory/parsers/python_parser.py +604 -0
  55. alita_sdk/community/inventory/parsers/rust_parser.py +858 -0
  56. alita_sdk/community/inventory/parsers/swift_parser.py +832 -0
  57. alita_sdk/community/inventory/parsers/text_parser.py +322 -0
  58. alita_sdk/community/inventory/parsers/yaml_parser.py +370 -0
  59. alita_sdk/community/inventory/patterns/__init__.py +61 -0
  60. alita_sdk/community/inventory/patterns/ast_adapter.py +380 -0
  61. alita_sdk/community/inventory/patterns/loader.py +348 -0
  62. alita_sdk/community/inventory/patterns/registry.py +198 -0
  63. alita_sdk/community/inventory/presets.py +535 -0
  64. alita_sdk/community/inventory/retrieval.py +1403 -0
  65. alita_sdk/community/inventory/toolkit.py +173 -0
  66. alita_sdk/community/inventory/toolkit_utils.py +176 -0
  67. alita_sdk/community/inventory/visualize.py +1370 -0
  68. alita_sdk/configurations/__init__.py +1 -1
  69. alita_sdk/configurations/ado.py +141 -20
  70. alita_sdk/configurations/bitbucket.py +0 -3
  71. alita_sdk/configurations/confluence.py +76 -42
  72. alita_sdk/configurations/figma.py +76 -0
  73. alita_sdk/configurations/gitlab.py +17 -5
  74. alita_sdk/configurations/openapi.py +329 -0
  75. alita_sdk/configurations/qtest.py +72 -1
  76. alita_sdk/configurations/report_portal.py +96 -0
  77. alita_sdk/configurations/sharepoint.py +148 -0
  78. alita_sdk/configurations/testio.py +83 -0
  79. alita_sdk/runtime/clients/artifact.py +3 -3
  80. alita_sdk/runtime/clients/client.py +353 -48
  81. alita_sdk/runtime/clients/sandbox_client.py +0 -21
  82. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  83. alita_sdk/runtime/langchain/assistant.py +123 -26
  84. alita_sdk/runtime/langchain/constants.py +642 -1
  85. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +103 -60
  86. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  87. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +6 -3
  88. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +226 -7
  89. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +5 -2
  90. alita_sdk/runtime/langchain/document_loaders/constants.py +12 -7
  91. alita_sdk/runtime/langchain/langraph_agent.py +279 -73
  92. alita_sdk/runtime/langchain/utils.py +82 -15
  93. alita_sdk/runtime/llms/preloaded.py +2 -6
  94. alita_sdk/runtime/skills/__init__.py +91 -0
  95. alita_sdk/runtime/skills/callbacks.py +498 -0
  96. alita_sdk/runtime/skills/discovery.py +540 -0
  97. alita_sdk/runtime/skills/executor.py +610 -0
  98. alita_sdk/runtime/skills/input_builder.py +371 -0
  99. alita_sdk/runtime/skills/models.py +330 -0
  100. alita_sdk/runtime/skills/registry.py +355 -0
  101. alita_sdk/runtime/skills/skill_runner.py +330 -0
  102. alita_sdk/runtime/toolkits/__init__.py +7 -0
  103. alita_sdk/runtime/toolkits/application.py +21 -9
  104. alita_sdk/runtime/toolkits/artifact.py +15 -5
  105. alita_sdk/runtime/toolkits/datasource.py +13 -6
  106. alita_sdk/runtime/toolkits/mcp.py +139 -251
  107. alita_sdk/runtime/toolkits/mcp_config.py +1048 -0
  108. alita_sdk/runtime/toolkits/planning.py +178 -0
  109. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  110. alita_sdk/runtime/toolkits/subgraph.py +251 -6
  111. alita_sdk/runtime/toolkits/tools.py +238 -32
  112. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  113. alita_sdk/runtime/tools/__init__.py +3 -1
  114. alita_sdk/runtime/tools/application.py +20 -6
  115. alita_sdk/runtime/tools/artifact.py +511 -28
  116. alita_sdk/runtime/tools/data_analysis.py +183 -0
  117. alita_sdk/runtime/tools/function.py +43 -15
  118. alita_sdk/runtime/tools/image_generation.py +50 -44
  119. alita_sdk/runtime/tools/llm.py +852 -67
  120. alita_sdk/runtime/tools/loop.py +3 -1
  121. alita_sdk/runtime/tools/loop_output.py +3 -1
  122. alita_sdk/runtime/tools/mcp_remote_tool.py +25 -10
  123. alita_sdk/runtime/tools/mcp_server_tool.py +7 -6
  124. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  125. alita_sdk/runtime/tools/planning/models.py +246 -0
  126. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  127. alita_sdk/runtime/tools/router.py +2 -4
  128. alita_sdk/runtime/tools/sandbox.py +9 -6
  129. alita_sdk/runtime/tools/skill_router.py +776 -0
  130. alita_sdk/runtime/tools/tool.py +3 -1
  131. alita_sdk/runtime/tools/vectorstore.py +7 -2
  132. alita_sdk/runtime/tools/vectorstore_base.py +51 -11
  133. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  134. alita_sdk/runtime/utils/constants.py +5 -1
  135. alita_sdk/runtime/utils/mcp_client.py +492 -0
  136. alita_sdk/runtime/utils/mcp_oauth.py +202 -5
  137. alita_sdk/runtime/utils/mcp_sse_client.py +36 -7
  138. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  139. alita_sdk/runtime/utils/serialization.py +155 -0
  140. alita_sdk/runtime/utils/streamlit.py +6 -10
  141. alita_sdk/runtime/utils/toolkit_utils.py +16 -5
  142. alita_sdk/runtime/utils/utils.py +36 -0
  143. alita_sdk/tools/__init__.py +113 -29
  144. alita_sdk/tools/ado/repos/__init__.py +51 -33
  145. alita_sdk/tools/ado/repos/repos_wrapper.py +148 -89
  146. alita_sdk/tools/ado/test_plan/__init__.py +25 -9
  147. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +23 -1
  148. alita_sdk/tools/ado/utils.py +1 -18
  149. alita_sdk/tools/ado/wiki/__init__.py +25 -8
  150. alita_sdk/tools/ado/wiki/ado_wrapper.py +291 -22
  151. alita_sdk/tools/ado/work_item/__init__.py +26 -9
  152. alita_sdk/tools/ado/work_item/ado_wrapper.py +56 -3
  153. alita_sdk/tools/advanced_jira_mining/__init__.py +11 -8
  154. alita_sdk/tools/aws/delta_lake/__init__.py +13 -9
  155. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  156. alita_sdk/tools/azure_ai/search/__init__.py +11 -8
  157. alita_sdk/tools/azure_ai/search/api_wrapper.py +1 -1
  158. alita_sdk/tools/base/tool.py +5 -1
  159. alita_sdk/tools/base_indexer_toolkit.py +170 -45
  160. alita_sdk/tools/bitbucket/__init__.py +17 -12
  161. alita_sdk/tools/bitbucket/api_wrapper.py +59 -11
  162. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +49 -35
  163. alita_sdk/tools/browser/__init__.py +5 -4
  164. alita_sdk/tools/carrier/__init__.py +5 -6
  165. alita_sdk/tools/carrier/backend_reports_tool.py +6 -6
  166. alita_sdk/tools/carrier/run_ui_test_tool.py +6 -6
  167. alita_sdk/tools/carrier/ui_reports_tool.py +5 -5
  168. alita_sdk/tools/chunkers/__init__.py +3 -1
  169. alita_sdk/tools/chunkers/code/treesitter/treesitter.py +37 -13
  170. alita_sdk/tools/chunkers/sematic/json_chunker.py +1 -0
  171. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  172. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  173. alita_sdk/tools/cloud/aws/__init__.py +10 -7
  174. alita_sdk/tools/cloud/azure/__init__.py +10 -7
  175. alita_sdk/tools/cloud/gcp/__init__.py +10 -7
  176. alita_sdk/tools/cloud/k8s/__init__.py +10 -7
  177. alita_sdk/tools/code/linter/__init__.py +10 -8
  178. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  179. alita_sdk/tools/code/sonar/__init__.py +10 -7
  180. alita_sdk/tools/code_indexer_toolkit.py +73 -23
  181. alita_sdk/tools/confluence/__init__.py +21 -15
  182. alita_sdk/tools/confluence/api_wrapper.py +78 -23
  183. alita_sdk/tools/confluence/loader.py +4 -2
  184. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  185. alita_sdk/tools/elastic/__init__.py +11 -8
  186. alita_sdk/tools/elitea_base.py +493 -30
  187. alita_sdk/tools/figma/__init__.py +58 -11
  188. alita_sdk/tools/figma/api_wrapper.py +1235 -143
  189. alita_sdk/tools/figma/figma_client.py +73 -0
  190. alita_sdk/tools/figma/toon_tools.py +2748 -0
  191. alita_sdk/tools/github/__init__.py +13 -14
  192. alita_sdk/tools/github/github_client.py +224 -100
  193. alita_sdk/tools/github/graphql_client_wrapper.py +119 -33
  194. alita_sdk/tools/github/schemas.py +14 -5
  195. alita_sdk/tools/github/tool.py +5 -1
  196. alita_sdk/tools/github/tool_prompts.py +9 -22
  197. alita_sdk/tools/gitlab/__init__.py +15 -11
  198. alita_sdk/tools/gitlab/api_wrapper.py +207 -41
  199. alita_sdk/tools/gitlab_org/__init__.py +10 -8
  200. alita_sdk/tools/gitlab_org/api_wrapper.py +63 -64
  201. alita_sdk/tools/google/bigquery/__init__.py +13 -12
  202. alita_sdk/tools/google/bigquery/tool.py +5 -1
  203. alita_sdk/tools/google_places/__init__.py +10 -8
  204. alita_sdk/tools/google_places/api_wrapper.py +1 -1
  205. alita_sdk/tools/jira/__init__.py +17 -11
  206. alita_sdk/tools/jira/api_wrapper.py +91 -40
  207. alita_sdk/tools/keycloak/__init__.py +11 -8
  208. alita_sdk/tools/localgit/__init__.py +9 -3
  209. alita_sdk/tools/localgit/local_git.py +62 -54
  210. alita_sdk/tools/localgit/tool.py +5 -1
  211. alita_sdk/tools/memory/__init__.py +11 -3
  212. alita_sdk/tools/non_code_indexer_toolkit.py +1 -0
  213. alita_sdk/tools/ocr/__init__.py +11 -8
  214. alita_sdk/tools/openapi/__init__.py +490 -114
  215. alita_sdk/tools/openapi/api_wrapper.py +1368 -0
  216. alita_sdk/tools/openapi/tool.py +20 -0
  217. alita_sdk/tools/pandas/__init__.py +20 -12
  218. alita_sdk/tools/pandas/api_wrapper.py +38 -25
  219. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  220. alita_sdk/tools/postman/__init__.py +11 -11
  221. alita_sdk/tools/pptx/__init__.py +10 -9
  222. alita_sdk/tools/pptx/pptx_wrapper.py +1 -1
  223. alita_sdk/tools/qtest/__init__.py +30 -10
  224. alita_sdk/tools/qtest/api_wrapper.py +430 -13
  225. alita_sdk/tools/rally/__init__.py +10 -8
  226. alita_sdk/tools/rally/api_wrapper.py +1 -1
  227. alita_sdk/tools/report_portal/__init__.py +12 -9
  228. alita_sdk/tools/salesforce/__init__.py +10 -9
  229. alita_sdk/tools/servicenow/__init__.py +17 -14
  230. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  231. alita_sdk/tools/sharepoint/__init__.py +10 -8
  232. alita_sdk/tools/sharepoint/api_wrapper.py +4 -4
  233. alita_sdk/tools/slack/__init__.py +10 -8
  234. alita_sdk/tools/slack/api_wrapper.py +2 -2
  235. alita_sdk/tools/sql/__init__.py +11 -9
  236. alita_sdk/tools/testio/__init__.py +10 -8
  237. alita_sdk/tools/testrail/__init__.py +11 -8
  238. alita_sdk/tools/testrail/api_wrapper.py +1 -1
  239. alita_sdk/tools/utils/__init__.py +9 -4
  240. alita_sdk/tools/utils/content_parser.py +77 -3
  241. alita_sdk/tools/utils/text_operations.py +410 -0
  242. alita_sdk/tools/utils/tool_prompts.py +79 -0
  243. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +17 -13
  244. alita_sdk/tools/xray/__init__.py +12 -9
  245. alita_sdk/tools/yagmail/__init__.py +9 -3
  246. alita_sdk/tools/zephyr/__init__.py +9 -7
  247. alita_sdk/tools/zephyr_enterprise/__init__.py +11 -8
  248. alita_sdk/tools/zephyr_essential/__init__.py +10 -8
  249. alita_sdk/tools/zephyr_essential/api_wrapper.py +30 -13
  250. alita_sdk/tools/zephyr_essential/client.py +2 -2
  251. alita_sdk/tools/zephyr_scale/__init__.py +11 -9
  252. alita_sdk/tools/zephyr_scale/api_wrapper.py +2 -2
  253. alita_sdk/tools/zephyr_squad/__init__.py +10 -8
  254. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/METADATA +147 -7
  255. alita_sdk-0.3.627.dist-info/RECORD +468 -0
  256. alita_sdk-0.3.627.dist-info/entry_points.txt +2 -0
  257. alita_sdk-0.3.462.dist-info/RECORD +0 -384
  258. alita_sdk-0.3.462.dist-info/entry_points.txt +0 -2
  259. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/WHEEL +0 -0
  260. {alita_sdk-0.3.462.dist-info → alita_sdk-0.3.627.dist-info}/licenses/LICENSE +0 -0
  261. {alita_sdk-0.3.462.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
 
@@ -684,8 +686,10 @@ class VectorStoreWrapper(BaseToolApiWrapper):
684
686
  ]
685
687
  )
686
688
  ])
689
+ # Extract text content safely (handles both string and list content from thinking models)
690
+ search_query = extract_text_from_completion(result)
687
691
  search_results = self.search_documents(
688
- result.content, doctype, filter, cut_off, search_top,
692
+ search_query, doctype, filter, cut_off, search_top,
689
693
  full_text_search=full_text_search,
690
694
  reranking_config=reranking_config,
691
695
  extended_search=extended_search
@@ -714,7 +718,8 @@ class VectorStoreWrapper(BaseToolApiWrapper):
714
718
  ]
715
719
  )
716
720
  ])
717
- return result.content
721
+ # Extract text content safely (handles both string and list content from thinking models)
722
+ return extract_text_from_completion(result)
718
723
 
719
724
  def _log_data(self, message: str, tool_name: str = "index_data"):
720
725
  """Log data and dispatch custom event for indexing progress"""
@@ -13,9 +13,11 @@ from pydantic import BaseModel, model_validator, Field
13
13
  from alita_sdk.tools.elitea_base import BaseToolApiWrapper
14
14
  from alita_sdk.tools.vector_adapters.VectorStoreAdapter import VectorStoreAdapterFactory
15
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,19 +243,21 @@ 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
259
  def get_indexed_count(self, index_name: str) -> int:
260
+ self._ensure_vectorstore_initialized()
226
261
  from sqlalchemy.orm import Session
227
262
  from sqlalchemy import func, or_
228
263
 
@@ -237,15 +272,16 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
237
272
  )
238
273
  ).count()
239
274
 
240
- def _clean_collection(self, index_name: str = ''):
275
+ def _clean_collection(self, index_name: str = '', including_index_meta: bool = False):
241
276
  """
242
277
  Clean the vectorstore collection by deleting all indexed data.
243
278
  """
279
+ self._ensure_vectorstore_initialized()
244
280
  self._log_tool_event(
245
281
  f"Cleaning collection '{self.dataset}'",
246
282
  tool_name="_clean_collection"
247
283
  )
248
- self.vector_adapter.clean_collection(self, index_name)
284
+ self.vector_adapter.clean_collection(self, index_name, including_index_meta)
249
285
  self._log_tool_event(
250
286
  f"Collection '{self.dataset}' has been cleaned. ",
251
287
  tool_name="_clean_collection"
@@ -259,6 +295,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
259
295
  progress_step (int): Step for progress reporting, default is 20.
260
296
  clean_index (bool): If True, clean the index before re-indexing all documents.
261
297
  """
298
+ self._ensure_vectorstore_initialized()
262
299
  if clean_index:
263
300
  self._clean_index(index_name)
264
301
 
@@ -268,7 +305,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
268
305
  logger.info("Cleaning index before re-indexing all documents.")
269
306
  self._log_tool_event("Cleaning index before re-indexing all documents. Previous index will be removed", tool_name="index_documents")
270
307
  try:
271
- self._clean_collection(index_name)
308
+ self._clean_collection(index_name, including_index_meta=False)
272
309
  self._log_tool_event("Previous index has been removed",
273
310
  tool_name="index_documents")
274
311
  except Exception as e:
@@ -590,8 +627,10 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
590
627
  ]
591
628
  )
592
629
  ])
630
+ # Extract text content safely (handles both string and list content from thinking models)
631
+ search_query = extract_text_from_completion(result)
593
632
  search_results = self.search_documents(
594
- result.content, doctype, filter, cut_off, search_top,
633
+ search_query, doctype, filter, cut_off, search_top,
595
634
  full_text_search=full_text_search,
596
635
  reranking_config=reranking_config,
597
636
  extended_search=extended_search
@@ -620,7 +659,8 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
620
659
  ]
621
660
  )
622
661
  ])
623
- return result.content
662
+ # Extract text content safely (handles both string and list content from thinking models)
663
+ return extract_text_from_completion(result)
624
664
 
625
665
  def get_available_tools(self):
626
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"