alita-sdk 0.3.257__py3-none-any.whl → 0.3.584__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.

Potentially problematic release.


This version of alita-sdk might be problematic. Click here for more details.

Files changed (281) 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 +3794 -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/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 +72 -12
  30. alita_sdk/community/inventory/__init__.py +236 -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/toolkit_utils.py +176 -0
  58. alita_sdk/community/inventory/visualize.py +1370 -0
  59. alita_sdk/configurations/__init__.py +11 -0
  60. alita_sdk/configurations/ado.py +148 -2
  61. alita_sdk/configurations/azure_search.py +1 -1
  62. alita_sdk/configurations/bigquery.py +1 -1
  63. alita_sdk/configurations/bitbucket.py +94 -2
  64. alita_sdk/configurations/browser.py +18 -0
  65. alita_sdk/configurations/carrier.py +19 -0
  66. alita_sdk/configurations/confluence.py +130 -1
  67. alita_sdk/configurations/delta_lake.py +1 -1
  68. alita_sdk/configurations/figma.py +76 -5
  69. alita_sdk/configurations/github.py +65 -1
  70. alita_sdk/configurations/gitlab.py +81 -0
  71. alita_sdk/configurations/google_places.py +17 -0
  72. alita_sdk/configurations/jira.py +103 -0
  73. alita_sdk/configurations/openapi.py +323 -0
  74. alita_sdk/configurations/postman.py +1 -1
  75. alita_sdk/configurations/qtest.py +72 -3
  76. alita_sdk/configurations/report_portal.py +115 -0
  77. alita_sdk/configurations/salesforce.py +19 -0
  78. alita_sdk/configurations/service_now.py +1 -12
  79. alita_sdk/configurations/sharepoint.py +167 -0
  80. alita_sdk/configurations/sonar.py +18 -0
  81. alita_sdk/configurations/sql.py +20 -0
  82. alita_sdk/configurations/testio.py +101 -0
  83. alita_sdk/configurations/testrail.py +88 -0
  84. alita_sdk/configurations/xray.py +94 -1
  85. alita_sdk/configurations/zephyr_enterprise.py +94 -1
  86. alita_sdk/configurations/zephyr_essential.py +95 -0
  87. alita_sdk/runtime/clients/artifact.py +21 -4
  88. alita_sdk/runtime/clients/client.py +458 -67
  89. alita_sdk/runtime/clients/mcp_discovery.py +342 -0
  90. alita_sdk/runtime/clients/mcp_manager.py +262 -0
  91. alita_sdk/runtime/clients/sandbox_client.py +352 -0
  92. alita_sdk/runtime/langchain/_constants_bkup.py +1318 -0
  93. alita_sdk/runtime/langchain/assistant.py +183 -43
  94. alita_sdk/runtime/langchain/constants.py +647 -1
  95. alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
  96. alita_sdk/runtime/langchain/document_loaders/AlitaExcelLoader.py +209 -31
  97. alita_sdk/runtime/langchain/document_loaders/AlitaImageLoader.py +1 -1
  98. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLinesLoader.py +77 -0
  99. alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +10 -3
  100. alita_sdk/runtime/langchain/document_loaders/AlitaMarkdownLoader.py +66 -0
  101. alita_sdk/runtime/langchain/document_loaders/AlitaPDFLoader.py +79 -10
  102. alita_sdk/runtime/langchain/document_loaders/AlitaPowerPointLoader.py +52 -15
  103. alita_sdk/runtime/langchain/document_loaders/AlitaPythonLoader.py +9 -0
  104. alita_sdk/runtime/langchain/document_loaders/AlitaTableLoader.py +1 -4
  105. alita_sdk/runtime/langchain/document_loaders/AlitaTextLoader.py +15 -2
  106. alita_sdk/runtime/langchain/document_loaders/ImageParser.py +30 -0
  107. alita_sdk/runtime/langchain/document_loaders/constants.py +189 -41
  108. alita_sdk/runtime/langchain/interfaces/llm_processor.py +4 -2
  109. alita_sdk/runtime/langchain/langraph_agent.py +493 -105
  110. alita_sdk/runtime/langchain/utils.py +118 -8
  111. alita_sdk/runtime/llms/preloaded.py +2 -6
  112. alita_sdk/runtime/models/mcp_models.py +61 -0
  113. alita_sdk/runtime/skills/__init__.py +91 -0
  114. alita_sdk/runtime/skills/callbacks.py +498 -0
  115. alita_sdk/runtime/skills/discovery.py +540 -0
  116. alita_sdk/runtime/skills/executor.py +610 -0
  117. alita_sdk/runtime/skills/input_builder.py +371 -0
  118. alita_sdk/runtime/skills/models.py +330 -0
  119. alita_sdk/runtime/skills/registry.py +355 -0
  120. alita_sdk/runtime/skills/skill_runner.py +330 -0
  121. alita_sdk/runtime/toolkits/__init__.py +28 -0
  122. alita_sdk/runtime/toolkits/application.py +14 -4
  123. alita_sdk/runtime/toolkits/artifact.py +25 -9
  124. alita_sdk/runtime/toolkits/datasource.py +13 -6
  125. alita_sdk/runtime/toolkits/mcp.py +782 -0
  126. alita_sdk/runtime/toolkits/planning.py +178 -0
  127. alita_sdk/runtime/toolkits/skill_router.py +238 -0
  128. alita_sdk/runtime/toolkits/subgraph.py +11 -6
  129. alita_sdk/runtime/toolkits/tools.py +314 -70
  130. alita_sdk/runtime/toolkits/vectorstore.py +11 -5
  131. alita_sdk/runtime/tools/__init__.py +24 -0
  132. alita_sdk/runtime/tools/application.py +16 -4
  133. alita_sdk/runtime/tools/artifact.py +367 -33
  134. alita_sdk/runtime/tools/data_analysis.py +183 -0
  135. alita_sdk/runtime/tools/function.py +100 -4
  136. alita_sdk/runtime/tools/graph.py +81 -0
  137. alita_sdk/runtime/tools/image_generation.py +218 -0
  138. alita_sdk/runtime/tools/llm.py +1032 -177
  139. alita_sdk/runtime/tools/loop.py +3 -1
  140. alita_sdk/runtime/tools/loop_output.py +3 -1
  141. alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
  142. alita_sdk/runtime/tools/mcp_remote_tool.py +181 -0
  143. alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
  144. alita_sdk/runtime/tools/planning/__init__.py +36 -0
  145. alita_sdk/runtime/tools/planning/models.py +246 -0
  146. alita_sdk/runtime/tools/planning/wrapper.py +607 -0
  147. alita_sdk/runtime/tools/router.py +2 -1
  148. alita_sdk/runtime/tools/sandbox.py +375 -0
  149. alita_sdk/runtime/tools/skill_router.py +776 -0
  150. alita_sdk/runtime/tools/tool.py +3 -1
  151. alita_sdk/runtime/tools/vectorstore.py +69 -65
  152. alita_sdk/runtime/tools/vectorstore_base.py +163 -90
  153. alita_sdk/runtime/utils/AlitaCallback.py +137 -21
  154. alita_sdk/runtime/utils/constants.py +5 -1
  155. alita_sdk/runtime/utils/mcp_client.py +492 -0
  156. alita_sdk/runtime/utils/mcp_oauth.py +361 -0
  157. alita_sdk/runtime/utils/mcp_sse_client.py +434 -0
  158. alita_sdk/runtime/utils/mcp_tools_discovery.py +124 -0
  159. alita_sdk/runtime/utils/streamlit.py +41 -14
  160. alita_sdk/runtime/utils/toolkit_utils.py +28 -9
  161. alita_sdk/runtime/utils/utils.py +48 -0
  162. alita_sdk/tools/__init__.py +135 -37
  163. alita_sdk/tools/ado/__init__.py +2 -2
  164. alita_sdk/tools/ado/repos/__init__.py +16 -19
  165. alita_sdk/tools/ado/repos/repos_wrapper.py +12 -20
  166. alita_sdk/tools/ado/test_plan/__init__.py +27 -8
  167. alita_sdk/tools/ado/test_plan/test_plan_wrapper.py +56 -28
  168. alita_sdk/tools/ado/wiki/__init__.py +28 -12
  169. alita_sdk/tools/ado/wiki/ado_wrapper.py +114 -40
  170. alita_sdk/tools/ado/work_item/__init__.py +28 -12
  171. alita_sdk/tools/ado/work_item/ado_wrapper.py +95 -11
  172. alita_sdk/tools/advanced_jira_mining/__init__.py +13 -8
  173. alita_sdk/tools/aws/delta_lake/__init__.py +15 -11
  174. alita_sdk/tools/aws/delta_lake/tool.py +5 -1
  175. alita_sdk/tools/azure_ai/search/__init__.py +14 -8
  176. alita_sdk/tools/base/tool.py +5 -1
  177. alita_sdk/tools/base_indexer_toolkit.py +454 -110
  178. alita_sdk/tools/bitbucket/__init__.py +28 -19
  179. alita_sdk/tools/bitbucket/api_wrapper.py +285 -27
  180. alita_sdk/tools/bitbucket/cloud_api_wrapper.py +5 -5
  181. alita_sdk/tools/browser/__init__.py +41 -16
  182. alita_sdk/tools/browser/crawler.py +3 -1
  183. alita_sdk/tools/browser/utils.py +15 -6
  184. alita_sdk/tools/carrier/__init__.py +18 -17
  185. alita_sdk/tools/carrier/backend_reports_tool.py +8 -4
  186. alita_sdk/tools/carrier/excel_reporter.py +8 -4
  187. alita_sdk/tools/chunkers/__init__.py +3 -1
  188. alita_sdk/tools/chunkers/code/codeparser.py +1 -1
  189. alita_sdk/tools/chunkers/sematic/json_chunker.py +2 -1
  190. alita_sdk/tools/chunkers/sematic/markdown_chunker.py +97 -6
  191. alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
  192. alita_sdk/tools/chunkers/universal_chunker.py +270 -0
  193. alita_sdk/tools/cloud/aws/__init__.py +12 -7
  194. alita_sdk/tools/cloud/azure/__init__.py +12 -7
  195. alita_sdk/tools/cloud/gcp/__init__.py +12 -7
  196. alita_sdk/tools/cloud/k8s/__init__.py +12 -7
  197. alita_sdk/tools/code/linter/__init__.py +10 -8
  198. alita_sdk/tools/code/loaders/codesearcher.py +3 -2
  199. alita_sdk/tools/code/sonar/__init__.py +21 -13
  200. alita_sdk/tools/code_indexer_toolkit.py +199 -0
  201. alita_sdk/tools/confluence/__init__.py +22 -14
  202. alita_sdk/tools/confluence/api_wrapper.py +197 -58
  203. alita_sdk/tools/confluence/loader.py +14 -2
  204. alita_sdk/tools/custom_open_api/__init__.py +12 -5
  205. alita_sdk/tools/elastic/__init__.py +11 -8
  206. alita_sdk/tools/elitea_base.py +546 -64
  207. alita_sdk/tools/figma/__init__.py +60 -11
  208. alita_sdk/tools/figma/api_wrapper.py +1400 -167
  209. alita_sdk/tools/figma/figma_client.py +73 -0
  210. alita_sdk/tools/figma/toon_tools.py +2748 -0
  211. alita_sdk/tools/github/__init__.py +18 -17
  212. alita_sdk/tools/github/api_wrapper.py +9 -26
  213. alita_sdk/tools/github/github_client.py +81 -12
  214. alita_sdk/tools/github/schemas.py +2 -1
  215. alita_sdk/tools/github/tool.py +5 -1
  216. alita_sdk/tools/gitlab/__init__.py +19 -13
  217. alita_sdk/tools/gitlab/api_wrapper.py +256 -80
  218. alita_sdk/tools/gitlab_org/__init__.py +14 -10
  219. alita_sdk/tools/google/bigquery/__init__.py +14 -13
  220. alita_sdk/tools/google/bigquery/tool.py +5 -1
  221. alita_sdk/tools/google_places/__init__.py +21 -11
  222. alita_sdk/tools/jira/__init__.py +22 -11
  223. alita_sdk/tools/jira/api_wrapper.py +315 -168
  224. alita_sdk/tools/keycloak/__init__.py +11 -8
  225. alita_sdk/tools/localgit/__init__.py +9 -3
  226. alita_sdk/tools/localgit/local_git.py +62 -54
  227. alita_sdk/tools/localgit/tool.py +5 -1
  228. alita_sdk/tools/memory/__init__.py +38 -14
  229. alita_sdk/tools/non_code_indexer_toolkit.py +7 -2
  230. alita_sdk/tools/ocr/__init__.py +11 -8
  231. alita_sdk/tools/openapi/__init__.py +491 -106
  232. alita_sdk/tools/openapi/api_wrapper.py +1357 -0
  233. alita_sdk/tools/openapi/tool.py +20 -0
  234. alita_sdk/tools/pandas/__init__.py +20 -12
  235. alita_sdk/tools/pandas/api_wrapper.py +40 -45
  236. alita_sdk/tools/pandas/dataframe/generator/base.py +3 -1
  237. alita_sdk/tools/postman/__init__.py +11 -11
  238. alita_sdk/tools/postman/api_wrapper.py +19 -8
  239. alita_sdk/tools/postman/postman_analysis.py +8 -1
  240. alita_sdk/tools/pptx/__init__.py +11 -10
  241. alita_sdk/tools/qtest/__init__.py +22 -14
  242. alita_sdk/tools/qtest/api_wrapper.py +1784 -88
  243. alita_sdk/tools/rally/__init__.py +13 -10
  244. alita_sdk/tools/report_portal/__init__.py +23 -16
  245. alita_sdk/tools/salesforce/__init__.py +22 -16
  246. alita_sdk/tools/servicenow/__init__.py +21 -16
  247. alita_sdk/tools/servicenow/api_wrapper.py +1 -1
  248. alita_sdk/tools/sharepoint/__init__.py +17 -14
  249. alita_sdk/tools/sharepoint/api_wrapper.py +179 -39
  250. alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
  251. alita_sdk/tools/sharepoint/utils.py +8 -2
  252. alita_sdk/tools/slack/__init__.py +13 -8
  253. alita_sdk/tools/sql/__init__.py +22 -19
  254. alita_sdk/tools/sql/api_wrapper.py +71 -23
  255. alita_sdk/tools/testio/__init__.py +21 -13
  256. alita_sdk/tools/testrail/__init__.py +13 -11
  257. alita_sdk/tools/testrail/api_wrapper.py +214 -46
  258. alita_sdk/tools/utils/__init__.py +28 -4
  259. alita_sdk/tools/utils/content_parser.py +241 -55
  260. alita_sdk/tools/utils/text_operations.py +254 -0
  261. alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +83 -27
  262. alita_sdk/tools/xray/__init__.py +18 -14
  263. alita_sdk/tools/xray/api_wrapper.py +58 -113
  264. alita_sdk/tools/yagmail/__init__.py +9 -3
  265. alita_sdk/tools/zephyr/__init__.py +12 -7
  266. alita_sdk/tools/zephyr_enterprise/__init__.py +16 -9
  267. alita_sdk/tools/zephyr_enterprise/api_wrapper.py +30 -15
  268. alita_sdk/tools/zephyr_essential/__init__.py +16 -10
  269. alita_sdk/tools/zephyr_essential/api_wrapper.py +297 -54
  270. alita_sdk/tools/zephyr_essential/client.py +6 -4
  271. alita_sdk/tools/zephyr_scale/__init__.py +13 -8
  272. alita_sdk/tools/zephyr_scale/api_wrapper.py +39 -31
  273. alita_sdk/tools/zephyr_squad/__init__.py +12 -7
  274. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/METADATA +184 -37
  275. alita_sdk-0.3.584.dist-info/RECORD +452 -0
  276. alita_sdk-0.3.584.dist-info/entry_points.txt +2 -0
  277. alita_sdk/tools/bitbucket/tools.py +0 -304
  278. alita_sdk-0.3.257.dist-info/RECORD +0 -343
  279. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/WHEEL +0 -0
  280. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/licenses/LICENSE +0 -0
  281. {alita_sdk-0.3.257.dist-info → alita_sdk-0.3.584.dist-info}/top_level.txt +0 -0
@@ -1,19 +1,23 @@
1
1
  import json
2
- import math
2
+ from collections import OrderedDict
3
3
  from logging import getLogger
4
4
  from typing import Any, Optional, List, Dict, Generator
5
5
 
6
+ import math
6
7
  from langchain_core.documents import Document
7
8
  from langchain_core.messages import HumanMessage
9
+ from langchain_core.tools import ToolException
10
+ from psycopg.errors import DataException
8
11
  from pydantic import BaseModel, model_validator, Field
9
12
 
10
13
  from alita_sdk.tools.elitea_base import BaseToolApiWrapper
11
14
  from alita_sdk.tools.vector_adapters.VectorStoreAdapter import VectorStoreAdapterFactory
12
- from ..langchain.tools.vector import VectorAdapter
13
- from ..utils.logging import dispatch_custom_event
15
+ from ...runtime.utils.utils import IndexerKeywords
16
+ from ...runtime.langchain.utils import extract_text_from_completion
14
17
 
15
18
  logger = getLogger(__name__)
16
19
 
20
+
17
21
  class IndexDocumentsModel(BaseModel):
18
22
  documents: Any = Field(description="Generator of documents to index")
19
23
 
@@ -132,16 +136,12 @@ How did you come up with the answer?
132
136
 
133
137
  class VectorStoreWrapperBase(BaseToolApiWrapper):
134
138
  llm: Any
135
- embedding_model: str
136
- embedding_model_params: dict
137
- vectorstore_type: str
138
- vectorstore_params: dict
139
- max_docs_per_add: int = 100
140
- dataset: str = None
141
- embedding: Any = None
139
+ embedding_model: Optional[str] = None
140
+ vectorstore_type: Optional[str] = None
141
+ vectorstore_params: Optional[dict] = None
142
+ max_docs_per_add: int = 20
143
+ dataset: Optional[str] = None
142
144
  vectorstore: Any = None
143
- # Review usage of old adapter
144
- vectoradapter: Any = None
145
145
  pg_helper: Any = None
146
146
  embeddings: Any = None
147
147
  # New adapter for vector database operations
@@ -150,34 +150,52 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
150
150
  @model_validator(mode='before')
151
151
  @classmethod
152
152
  def validate_toolkit(cls, values):
153
- from ..langchain.interfaces.llm_processor import get_embeddings, get_vectorstore
153
+ from ..langchain.interfaces.llm_processor import get_vectorstore
154
154
  logger.debug(f"Validating toolkit: {values}")
155
- if not values.get('vectorstore_type'):
156
- raise ValueError("Vectorstore type is required.")
157
- if not values.get('embedding_model'):
158
- raise ValueError("Embedding model is required.")
159
- if not values.get('vectorstore_params'):
160
- raise ValueError("Vectorstore parameters are required.")
161
- if not values.get('embedding_model_params'):
162
- raise ValueError("Embedding model parameters are required.")
163
- values["dataset"] = values.get('vectorstore_params').get('collection_name')
164
- if not values["dataset"]:
165
- raise ValueError("Collection name is required.")
166
- if not values.get('embeddings'):
167
- values['embeddings'] = get_embeddings(values['embedding_model'], values['embedding_model_params'])
168
- values['vectorstore'] = get_vectorstore(values['vectorstore_type'], values['vectorstore_params'], embedding_func=values['embeddings'])
169
- values['vectoradapter'] = VectorAdapter(
170
- vectorstore=values['vectorstore'],
171
- embeddings=values['embeddings'],
172
- quota_params=None,
173
- )
174
- # Initialize the new vector adapter
175
- values['vector_adapter'] = VectorStoreAdapterFactory.create_adapter(values['vectorstore_type'])
176
- logger.debug(f"Vectorstore wrapper initialized: {values}")
155
+ values["dataset"] = values.get('collection_name')
156
+
157
+ if values.get('alita') and values.get('embedding_model'):
158
+ values['embeddings'] = values.get('alita').get_embeddings(values.get('embedding_model'))
159
+
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
177
162
  return values
178
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
+
179
196
  def _init_pg_helper(self, language='english'):
180
197
  """Initialize PGVector helper if needed and not already initialized"""
198
+ self._ensure_vectorstore_initialized()
181
199
  if self.pg_helper is None and hasattr(self.vectorstore, 'connection_string') and hasattr(self.vectorstore, 'collection_name'):
182
200
  try:
183
201
  from .pgvector_search import PGVectorSearch
@@ -191,26 +209,85 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
191
209
  except Exception as e:
192
210
  logger.error(f"Failed to initialize PGVectorSearch: {str(e)}")
193
211
 
212
+ def _similarity_search_with_score(self, query: str, filter: dict = None, k: int = 10):
213
+ """
214
+ Perform similarity search with proper exception handling for DataException.
215
+
216
+ Args:
217
+ query: Search query string
218
+ filter: Optional filter dictionary
219
+ k: Number of results to return
220
+
221
+ Returns:
222
+ List of (Document, score) tuples
223
+
224
+ Raises:
225
+ ToolException: When DataException occurs or other search errors
226
+ """
227
+ self._ensure_vectorstore_initialized()
228
+ try:
229
+ return self.vectorstore.similarity_search_with_score(
230
+ query, filter=filter, k=k
231
+ )
232
+ except DataException as dimException:
233
+ exception_str = str(dimException)
234
+ if 'different vector dimensions' in exception_str:
235
+ logger.error(f"Data exception: {exception_str}")
236
+ raise ToolException(f"Global search cannot be completed since collections were indexed using "
237
+ f"different embedding models. Use search within a single collection."
238
+ f"\nDetails: {exception_str}")
239
+ raise ToolException(f"Data exception during search. Possibly invalid filter: {exception_str}")
240
+ except Exception as e:
241
+ logger.error(f"Error during similarity search: {str(e)}")
242
+ raise ToolException(f"Search failed: {str(e)}")
243
+
194
244
  def list_collections(self) -> List[str]:
195
245
  """List all collections in the vectorstore."""
246
+ self._ensure_vectorstore_initialized()
247
+ collections = self.vector_adapter.list_collections(self)
248
+ if not collections:
249
+ return "No indexed collections"
250
+ return collections
251
+
252
+ def get_index_meta(self, index_name: str):
253
+ self._ensure_vectorstore_initialized()
254
+ index_metas = self.vector_adapter.get_index_meta(self, index_name)
255
+ if len(index_metas) > 1:
256
+ raise RuntimeError(f"Multiple index_meta documents found: {index_metas}")
257
+ return index_metas[0] if index_metas else None
258
+
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()
196
274
 
197
- return self.vector_adapter.list_collections(self)
198
-
199
- def _clean_collection(self, collection_suffix: str = ''):
275
+ def _clean_collection(self, index_name: str = '', including_index_meta: bool = False):
200
276
  """
201
277
  Clean the vectorstore collection by deleting all indexed data.
202
278
  """
203
- self._log_data(
279
+ self._ensure_vectorstore_initialized()
280
+ self._log_tool_event(
204
281
  f"Cleaning collection '{self.dataset}'",
205
282
  tool_name="_clean_collection"
206
283
  )
207
- self.vector_adapter.clean_collection(self, collection_suffix)
208
- self._log_data(
284
+ self.vector_adapter.clean_collection(self, index_name, including_index_meta)
285
+ self._log_tool_event(
209
286
  f"Collection '{self.dataset}' has been cleaned. ",
210
287
  tool_name="_clean_collection"
211
288
  )
212
289
 
213
- def index_documents(self, documents: Generator[Document, None, None], collection_suffix: str, progress_step: int = 20, clean_index: bool = True):
290
+ def index_documents(self, documents: Generator[Document, None, None], index_name: str, progress_step: int = 20, clean_index: bool = True):
214
291
  """ Index documents in the vectorstore.
215
292
 
216
293
  Args:
@@ -218,24 +295,23 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
218
295
  progress_step (int): Step for progress reporting, default is 20.
219
296
  clean_index (bool): If True, clean the index before re-indexing all documents.
220
297
  """
298
+ self._ensure_vectorstore_initialized()
221
299
  if clean_index:
222
- self._clean_index(collection_suffix)
300
+ self._clean_index(index_name)
223
301
 
224
- return self._save_index(list(documents), collection_suffix, progress_step)
302
+ return self._save_index(list(documents), index_name, progress_step)
225
303
 
226
- def _clean_index(self, collection_suffix: str):
304
+ def _clean_index(self, index_name: str):
227
305
  logger.info("Cleaning index before re-indexing all documents.")
228
- self._log_data("Cleaning index before re-indexing all documents. Previous index will be removed", tool_name="index_documents")
306
+ self._log_tool_event("Cleaning index before re-indexing all documents. Previous index will be removed", tool_name="index_documents")
229
307
  try:
230
- self._clean_collection(collection_suffix)
231
- self.vectoradapter.persist()
232
- self.vectoradapter.vacuum()
233
- self._log_data("Previous index has been removed",
308
+ self._clean_collection(index_name, including_index_meta=False)
309
+ self._log_tool_event("Previous index has been removed",
234
310
  tool_name="index_documents")
235
311
  except Exception as e:
236
312
  logger.warning(f"Failed to clean index: {str(e)}. Continuing with re-indexing.")
237
313
 
238
- def _save_index(self, documents: list[Document], collection_suffix: Optional[str] = None, progress_step: int = 20):
314
+ def _save_index(self, documents: list[Document], index_name: Optional[str] = None, progress_step: int = 20):
239
315
  from ..langchain.interfaces.llm_processor import add_documents
240
316
  #
241
317
  for doc in documents:
@@ -243,15 +319,14 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
243
319
  logger.warning(f"Document is missing required metadata field 'id' or 'updated_on': {doc.metadata}")
244
320
 
245
321
  logger.debug(f"Indexing documents: {documents}")
246
- logger.debug(self.vectoradapter)
247
322
 
248
- # if collection_suffix is provided, add it to metadata of each document
249
- if collection_suffix:
323
+ # if index_name is provided, add it to metadata of each document
324
+ if index_name:
250
325
  for doc in documents:
251
326
  if not doc.metadata.get('collection'):
252
- doc.metadata['collection'] = collection_suffix
327
+ doc.metadata['collection'] = index_name
253
328
  else:
254
- doc.metadata['collection'] += f";{collection_suffix}"
329
+ doc.metadata['collection'] += f";{index_name}"
255
330
 
256
331
  total_docs = len(documents)
257
332
  documents_count = 0
@@ -261,6 +336,10 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
261
336
  progress_step = 20 if progress_step not in range(0, 100) else progress_step
262
337
  next_progress_point = progress_step
263
338
  for document in documents:
339
+ if not document.page_content:
340
+ # To avoid case when all documents have empty content
341
+ # See llm_processor.add_documents which exclude metadata of docs with empty content
342
+ continue
264
343
  documents_count += 1
265
344
  # logger.debug(f"Indexing document: {document}")
266
345
  try:
@@ -273,7 +352,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
273
352
  if percent >= next_progress_point:
274
353
  msg = f"Indexing progress: {percent}%. Processed {documents_count} of {total_docs} documents."
275
354
  logger.debug(msg)
276
- self._log_data(msg)
355
+ self._log_tool_event(msg)
277
356
  next_progress_point += progress_step
278
357
  except Exception:
279
358
  from traceback import format_exc
@@ -281,7 +360,8 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
281
360
  return {"status": "error", "message": f"Error: {format_exc()}"}
282
361
  if _documents:
283
362
  add_documents(vectorstore=self.vectorstore, documents=_documents)
284
- 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"}
285
365
 
286
366
  def search_documents(self, query:str, doctype: str = 'code',
287
367
  filter:dict|str={}, cut_off: float=0.5,
@@ -315,7 +395,7 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
315
395
  }
316
396
 
317
397
  try:
318
- document_items = self.vectorstore.similarity_search_with_score(
398
+ document_items = self._similarity_search_with_score(
319
399
  query, filter=document_filter, k=search_top
320
400
  )
321
401
  # Add document results to unique docs
@@ -348,18 +428,16 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
348
428
  }
349
429
 
350
430
  try:
351
- chunk_items = self.vectorstore.similarity_search_with_score(
431
+ chunk_items = self._similarity_search_with_score(
352
432
  query, filter=chunk_filter, k=search_top
353
433
  )
354
-
355
- logger.debug(f"Chunk items for {chunk_type}: {chunk_items[0]}")
356
-
434
+
357
435
  for doc, score in chunk_items:
358
436
  # Create unique identifier for document
359
437
  source = doc.metadata.get('source')
360
438
  chunk_id = doc.metadata.get('chunk_id')
361
439
  doc_id = f"{source}_{chunk_id}" if source and chunk_id else str(doc.metadata.get('id', id(doc)))
362
-
440
+
363
441
  # Store document and its score
364
442
  if doc_id not in unique_docs:
365
443
  unique_docs[doc_id] = doc
@@ -379,9 +457,9 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
379
457
  doc_filter = {
380
458
  "$and": doc_filter_parts
381
459
  }
382
-
460
+
383
461
  try:
384
- fetch_items = self.vectorstore.similarity_search_with_score(
462
+ fetch_items = self._similarity_search_with_score(
385
463
  query, filter=doc_filter, k=1
386
464
  )
387
465
  if fetch_items:
@@ -395,18 +473,25 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
395
473
  else:
396
474
  # Default search behavior (unchanged)
397
475
  max_search_results = 30 if search_top * 3 > 30 else search_top * 3
398
- vector_items = self.vectorstore.similarity_search_with_score(
476
+ vector_items = self._similarity_search_with_score(
399
477
  query, filter=filter, k=max_search_results
400
478
  )
401
479
 
402
480
  # Initialize document map for tracking by ID
403
481
  doc_map = {
404
- f"{doc.metadata.get('id', f'idx_{i}')}_{doc.metadata['chunk_id']}"
405
- if 'chunk_id' in doc.metadata
406
- else doc.metadata.get('id', f"idx_{i}"): (doc, score)
482
+ (
483
+ f"{doc.metadata.get('id', f'idx_{i}')}_{doc.metadata['chunk_id']}"
484
+ if 'chunk_id' in doc.metadata
485
+ else doc.metadata.get('id', f"idx_{i}")
486
+ ): (doc, 1 - score)
407
487
  for i, (doc, score) in enumerate(vector_items)
408
488
  }
409
-
489
+
490
+ # Sort the items by the new score in descending order
491
+ doc_map = OrderedDict(
492
+ sorted(doc_map.items(), key=lambda x: x[1][1], reverse=True)
493
+ )
494
+
410
495
  # Process full-text search if configured
411
496
  if full_text_search and full_text_search.get('enabled') and full_text_search.get('fields'):
412
497
  language = full_text_search.get('language', 'english')
@@ -419,12 +504,12 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
419
504
  for field_name in full_text_search.get('fields', []):
420
505
  try:
421
506
  text_results = self.pg_helper.full_text_search(field_name, query)
422
-
507
+
423
508
  # Combine text search results with vector results
424
509
  for result in text_results:
425
510
  doc_id = result['id']
426
511
  text_score = result['text_score']
427
-
512
+
428
513
  if doc_id in doc_map:
429
514
  # Document exists in vector results, combine scores
430
515
  doc, vector_score = doc_map[doc_id]
@@ -542,8 +627,10 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
542
627
  ]
543
628
  )
544
629
  ])
630
+ # Extract text content safely (handles both string and list content from thinking models)
631
+ search_query = extract_text_from_completion(result)
545
632
  search_results = self.search_documents(
546
- result.content, doctype, filter, cut_off, search_top,
633
+ search_query, doctype, filter, cut_off, search_top,
547
634
  full_text_search=full_text_search,
548
635
  reranking_config=reranking_config,
549
636
  extended_search=extended_search
@@ -572,22 +659,8 @@ class VectorStoreWrapperBase(BaseToolApiWrapper):
572
659
  ]
573
660
  )
574
661
  ])
575
- return result.content
576
-
577
- def _log_data(self, message: str, tool_name: str = "index_data"):
578
- """Log data and dispatch custom event for indexing progress"""
579
-
580
- try:
581
- dispatch_custom_event(
582
- name="thinking_step",
583
- data={
584
- "message": message,
585
- "tool_name": tool_name,
586
- "toolkit": "vectorstore",
587
- },
588
- )
589
- except Exception as e:
590
- logger.warning(f"Failed to dispatch progress event: {str(e)}")
662
+ # Extract text content safely (handles both string and list content from thinking models)
663
+ return extract_text_from_completion(result)
591
664
 
592
665
  def get_available_tools(self):
593
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"