alita-sdk 0.3.204__py3-none-any.whl → 0.3.206__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.
- alita_sdk/runtime/clients/client.py +45 -5
- alita_sdk/runtime/langchain/assistant.py +22 -21
- alita_sdk/runtime/langchain/interfaces/llm_processor.py +1 -4
- alita_sdk/runtime/toolkits/application.py +5 -10
- alita_sdk/runtime/toolkits/tools.py +0 -1
- alita_sdk/runtime/tools/vectorstore.py +157 -13
- alita_sdk/runtime/utils/streamlit.py +33 -30
- alita_sdk/runtime/utils/utils.py +5 -0
- alita_sdk/tools/__init__.py +4 -0
- alita_sdk/tools/ado/repos/repos_wrapper.py +20 -13
- alita_sdk/tools/aws/__init__.py +7 -0
- alita_sdk/tools/aws/delta_lake/__init__.py +136 -0
- alita_sdk/tools/aws/delta_lake/api_wrapper.py +220 -0
- alita_sdk/tools/aws/delta_lake/schemas.py +20 -0
- alita_sdk/tools/aws/delta_lake/tool.py +35 -0
- alita_sdk/tools/bitbucket/api_wrapper.py +5 -5
- alita_sdk/tools/bitbucket/cloud_api_wrapper.py +54 -29
- alita_sdk/tools/elitea_base.py +55 -5
- alita_sdk/tools/gitlab/__init__.py +22 -10
- alita_sdk/tools/gitlab/api_wrapper.py +278 -253
- alita_sdk/tools/gitlab/tools.py +354 -376
- alita_sdk/tools/google/__init__.py +7 -0
- alita_sdk/tools/google/bigquery/__init__.py +154 -0
- alita_sdk/tools/google/bigquery/api_wrapper.py +502 -0
- alita_sdk/tools/google/bigquery/schemas.py +102 -0
- alita_sdk/tools/google/bigquery/tool.py +34 -0
- alita_sdk/tools/llm/llm_utils.py +0 -6
- alita_sdk/tools/openapi/__init__.py +14 -3
- alita_sdk/tools/sharepoint/__init__.py +2 -1
- alita_sdk/tools/sharepoint/api_wrapper.py +71 -7
- alita_sdk/tools/testrail/__init__.py +9 -1
- alita_sdk/tools/testrail/api_wrapper.py +154 -5
- alita_sdk/tools/utils/content_parser.py +77 -13
- alita_sdk/tools/zephyr_scale/api_wrapper.py +271 -22
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.206.dist-info}/METADATA +3 -1
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.206.dist-info}/RECORD +39 -30
- alita_sdk/runtime/llms/alita.py +0 -259
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.206.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.206.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.204.dist-info → alita_sdk-0.3.206.dist-info}/top_level.txt +0 -0
@@ -10,6 +10,7 @@ from langchain_core.messages import (
|
|
10
10
|
)
|
11
11
|
from langchain_core.tools import ToolException
|
12
12
|
from langgraph.store.base import BaseStore
|
13
|
+
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
|
13
14
|
|
14
15
|
from ..langchain.assistant import Assistant as LangChainAssistant
|
15
16
|
# from ..llamaindex.assistant import Assistant as LLamaAssistant
|
@@ -37,6 +38,7 @@ class AlitaClient:
|
|
37
38
|
|
38
39
|
self.base_url = base_url.rstrip('/')
|
39
40
|
self.api_path = '/api/v1'
|
41
|
+
self.llm_path = '/llm/v1'
|
40
42
|
self.project_id = project_id
|
41
43
|
self.auth_token = auth_token
|
42
44
|
self.headers = {
|
@@ -152,6 +154,35 @@ class AlitaClient:
|
|
152
154
|
return resp.json()
|
153
155
|
return []
|
154
156
|
|
157
|
+
def get_llm(self, model_name: str, model_config: dict) -> ChatOpenAI:
|
158
|
+
"""
|
159
|
+
Get a ChatOpenAI model instance based on the model name and configuration.
|
160
|
+
|
161
|
+
Args:
|
162
|
+
model_name: Name of the model to retrieve
|
163
|
+
model_config: Configuration parameters for the model
|
164
|
+
|
165
|
+
Returns:
|
166
|
+
An instance of ChatOpenAI configured with the provided parameters.
|
167
|
+
"""
|
168
|
+
if not model_name:
|
169
|
+
raise ValueError("Model name must be provided")
|
170
|
+
|
171
|
+
logger.info(f"Creating ChatOpenAI model: {model_name} with config: {model_config}")
|
172
|
+
|
173
|
+
return ChatOpenAI(
|
174
|
+
base_url=f"{self.base_url}{self.llm_path}",
|
175
|
+
model=model_name,
|
176
|
+
api_key=self.auth_token,
|
177
|
+
stream_usage=model_config.get("stream_usage", True),
|
178
|
+
max_tokens=model_config.get("max_tokens", None),
|
179
|
+
top_p=model_config.get("top_p"),
|
180
|
+
temperature=model_config.get("temperature"),
|
181
|
+
max_retries=model_config.get("max_retries", 3),
|
182
|
+
seed=model_config.get("seed", None),
|
183
|
+
)
|
184
|
+
|
185
|
+
|
155
186
|
def get_app_version_details(self, application_id: int, application_version_id: int) -> dict:
|
156
187
|
url = f"{self.application_versions}/{application_id}/{application_version_id}"
|
157
188
|
if self.configurations:
|
@@ -177,11 +208,12 @@ class AlitaClient:
|
|
177
208
|
logger.info(f"Unsecret response: {data}")
|
178
209
|
return data.get('value', None)
|
179
210
|
|
180
|
-
def application(self,
|
211
|
+
def application(self, application_id: int, application_version_id: int,
|
181
212
|
tools: Optional[list] = None, chat_history: Optional[List[Any]] = None,
|
182
213
|
app_type=None, memory=None, runtime='langchain',
|
183
214
|
application_variables: Optional[dict] = None,
|
184
|
-
version_details: Optional[dict] = None, store: Optional[BaseStore] = None
|
215
|
+
version_details: Optional[dict] = None, store: Optional[BaseStore] = None,
|
216
|
+
llm: Optional[ChatOpenAI] = None):
|
185
217
|
if tools is None:
|
186
218
|
tools = []
|
187
219
|
if chat_history is None:
|
@@ -200,7 +232,15 @@ class AlitaClient:
|
|
200
232
|
for var in data.get('variables', {}):
|
201
233
|
if var['name'] in application_variables:
|
202
234
|
var.update(application_variables[var['name']])
|
203
|
-
|
235
|
+
if llm is None:
|
236
|
+
llm = self.get_llm(
|
237
|
+
model_name=data['llm_settings']['model_name'],
|
238
|
+
model_config = {
|
239
|
+
"max_tokens": data['llm_settings']['max_tokens'],
|
240
|
+
"top_p": data['llm_settings']['top_p'],
|
241
|
+
"temperature": data['llm_settings']['temperature']
|
242
|
+
}
|
243
|
+
)
|
204
244
|
if not app_type:
|
205
245
|
app_type = data.get("agent_type", "react")
|
206
246
|
if app_type == "alita":
|
@@ -212,10 +252,10 @@ class AlitaClient:
|
|
212
252
|
elif app_type == 'autogen':
|
213
253
|
app_type = "openai"
|
214
254
|
if runtime == 'nonrunnable':
|
215
|
-
return LangChainAssistant(self, data,
|
255
|
+
return LangChainAssistant(self, data, llm, chat_history, app_type,
|
216
256
|
tools=tools, memory=memory, store=store)
|
217
257
|
if runtime == 'langchain':
|
218
|
-
return LangChainAssistant(self, data,
|
258
|
+
return LangChainAssistant(self, data, llm,
|
219
259
|
chat_history, app_type,
|
220
260
|
tools=tools, memory=memory, store=store).runnable()
|
221
261
|
elif runtime == 'llama':
|
@@ -36,29 +36,30 @@ class Assistant:
|
|
36
36
|
|
37
37
|
logger.debug("Data for agent creation: %s", data)
|
38
38
|
logger.info("App type: %s", app_type)
|
39
|
-
|
39
|
+
|
40
|
+
self.client = client
|
40
41
|
# For predict agents, use the client as-is since it's already configured
|
41
|
-
if app_type == "predict":
|
42
|
-
|
43
|
-
else:
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
42
|
+
# if app_type == "predict":
|
43
|
+
# self.client = client
|
44
|
+
# else:
|
45
|
+
# # For other agent types, configure client from llm_settings
|
46
|
+
# self.client = copy(client)
|
47
|
+
# self.client.max_tokens = data['llm_settings']['max_tokens']
|
48
|
+
# self.client.temperature = data['llm_settings']['temperature']
|
49
|
+
# self.client.top_p = data['llm_settings']['top_p']
|
50
|
+
# self.client.top_k = data['llm_settings']['top_k']
|
51
|
+
# self.client.model_name = data['llm_settings']['model_name']
|
52
|
+
# self.client.integration_uid = data['llm_settings']['integration_uid']
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
54
|
+
# model_type = data["llm_settings"]["indexer_config"]["ai_model"]
|
55
|
+
# model_params = data["llm_settings"]["indexer_config"]["ai_model_params"]
|
56
|
+
# #
|
57
|
+
# target_pkg, target_name = model_type.rsplit(".", 1)
|
58
|
+
# target_cls = getattr(
|
59
|
+
# importlib.import_module(target_pkg),
|
60
|
+
# target_name
|
61
|
+
# )
|
62
|
+
# self.client = target_cls(**model_params)
|
62
63
|
# validate agents compatibility: non-pipeline agents cannot have pipelines as toolkits
|
63
64
|
if app_type not in ["pipeline", "predict"]:
|
64
65
|
tools_to_check = data.get('tools', [])
|
@@ -50,9 +50,6 @@ def get_model(model_type: str, model_params: dict):
|
|
50
50
|
return get_llm(model_type)(**model_params)
|
51
51
|
if model_type == "PreloadedChatModel":
|
52
52
|
return PreloadedChatModel(**model_params)
|
53
|
-
if model_type == "Alita":
|
54
|
-
from ...llms.alita import AlitaClient
|
55
|
-
return AlitaClient(**model_params)
|
56
53
|
if model_type in chat_models:
|
57
54
|
model = getattr(
|
58
55
|
__import__("langchain_community.chat_models", fromlist=[model_type]),
|
@@ -185,7 +182,7 @@ def add_documents(vectorstore, documents):
|
|
185
182
|
texts.append(document.page_content)
|
186
183
|
for key in document.metadata:
|
187
184
|
if isinstance(document.metadata[key], list):
|
188
|
-
document.metadata[key] = "; ".join(document.metadata[key])
|
185
|
+
document.metadata[key] = "; ".join([str(val) for val in document.metadata[key]])
|
189
186
|
if isinstance(document.metadata[key], dict):
|
190
187
|
document.metadata[key] = dumps(document.metadata[key])
|
191
188
|
metadata.append(document.metadata)
|
@@ -21,25 +21,20 @@ class ApplicationToolkit(BaseToolkit):
|
|
21
21
|
)
|
22
22
|
|
23
23
|
@classmethod
|
24
|
-
def get_toolkit(cls, client:
|
24
|
+
def get_toolkit(cls, client: 'AlitaClient', application_id: int, application_version_id: int,
|
25
25
|
selected_tools: list[str] = [], store: Optional[BaseStore] = None):
|
26
|
-
from ..llms.alita import AlitaChatModel
|
27
26
|
|
28
27
|
app_details = client.get_app_details(application_id)
|
29
28
|
version_details = client.get_app_version_details(application_id, application_version_id)
|
30
|
-
|
31
|
-
"deployment": client.base_url,
|
32
|
-
"model": version_details['llm_settings']['model_name'],
|
33
|
-
"api_key": app_api_key,
|
34
|
-
"project_id": client.project_id,
|
35
|
-
"integration_uid": version_details['llm_settings']['integration_uid'],
|
29
|
+
model_settings = {
|
36
30
|
"max_tokens": version_details['llm_settings']['max_tokens'],
|
37
31
|
"top_p": version_details['llm_settings']['top_p'],
|
38
|
-
"top_k": version_details['llm_settings']['top_k'],
|
39
32
|
"temperature": version_details['llm_settings']['temperature'],
|
40
33
|
}
|
41
34
|
|
42
|
-
app = client.application(
|
35
|
+
app = client.application(application_id, application_version_id, store=store,
|
36
|
+
llm=client.get_llm(version_details['llm_settings']['model_name'],
|
37
|
+
model_settings))
|
43
38
|
return cls(tools=[Application(name=app_details.get("name"),
|
44
39
|
description=app_details.get("description"),
|
45
40
|
application=app,
|
@@ -54,7 +54,6 @@ def get_tools(tools_list: list, alita_client, llm, memory_store: BaseStore = Non
|
|
54
54
|
alita_client,
|
55
55
|
application_id=int(tool['settings']['application_id']),
|
56
56
|
application_version_id=int(tool['settings']['application_version_id']),
|
57
|
-
app_api_key=alita_client.auth_token,
|
58
57
|
selected_tools=[]
|
59
58
|
).get_tools())
|
60
59
|
elif tool['type'] == 'application' and tool.get('agent_type', '') == 'pipeline':
|
@@ -1,13 +1,18 @@
|
|
1
1
|
import json
|
2
|
-
|
3
|
-
|
2
|
+
import math
|
3
|
+
import types
|
4
|
+
from typing import Any, Optional, List, Dict, Callable, Generator
|
5
|
+
|
6
|
+
from langchain_core.documents import Document
|
4
7
|
from pydantic import BaseModel, model_validator, Field
|
5
|
-
from langchain_core.tools import ToolException
|
6
8
|
from ..langchain.tools.vector import VectorAdapter
|
7
9
|
from langchain_core.messages import HumanMessage
|
8
10
|
from alita_sdk.tools.elitea_base import BaseToolApiWrapper
|
9
11
|
from logging import getLogger
|
10
12
|
|
13
|
+
from ..utils.logging import dispatch_custom_event
|
14
|
+
from ..utils.utils import IndexerKeywords
|
15
|
+
|
11
16
|
logger = getLogger(__name__)
|
12
17
|
|
13
18
|
class IndexDocumentsModel(BaseModel):
|
@@ -139,6 +144,7 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
139
144
|
vectoradapter: Any = None
|
140
145
|
pg_helper: Any = None
|
141
146
|
embeddings: Any = None
|
147
|
+
process_document_func: Optional[Callable] = None
|
142
148
|
|
143
149
|
@model_validator(mode='before')
|
144
150
|
@classmethod
|
@@ -182,18 +188,132 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
182
188
|
except Exception as e:
|
183
189
|
logger.error(f"Failed to initialize PGVectorSearch: {str(e)}")
|
184
190
|
|
185
|
-
def
|
191
|
+
def _get_indexed_data(self, store):
|
192
|
+
""" Get all indexed data from vectorstore """
|
193
|
+
|
194
|
+
# get already indexed data
|
195
|
+
result = {}
|
196
|
+
try:
|
197
|
+
self._log_data("Retrieving already indexed data from vectorstore",
|
198
|
+
tool_name="index_documents")
|
199
|
+
data = store.get(include=['documents', 'metadatas'])
|
200
|
+
# re-structure data to be more usable
|
201
|
+
for doc_str, meta, db_id in zip(data['documents'], data['metadatas'], data['ids']):
|
202
|
+
doc = json.loads(doc_str)
|
203
|
+
doc_id = str(meta['id'])
|
204
|
+
dependent_docs = meta.get(IndexerKeywords.DEPENDENT_DOCS.value, [])
|
205
|
+
parent_id = meta.get(IndexerKeywords.PARENT.value, -1)
|
206
|
+
result[doc_id] = {
|
207
|
+
'metadata': meta,
|
208
|
+
'document': doc,
|
209
|
+
'id': db_id,
|
210
|
+
IndexerKeywords.DEPENDENT_DOCS.value: dependent_docs,
|
211
|
+
IndexerKeywords.PARENT.value: parent_id
|
212
|
+
}
|
213
|
+
except Exception as e:
|
214
|
+
logger.error(f"Failed to get indexed data from vectorstore: {str(e)}. Continuing with empty index.")
|
215
|
+
return result
|
216
|
+
|
217
|
+
def _reduce_duplicates(self, documents: Generator[Document, None, None], store) -> List[Any]:
|
218
|
+
"""Remove documents already indexed in the vectorstore based on metadata 'id' and 'updated_on' fields."""
|
219
|
+
|
220
|
+
self._log_data("Verification of documents to index started", tool_name="index_documents")
|
221
|
+
|
222
|
+
indexed_data = self._get_indexed_data(store)
|
223
|
+
indexed_ids = set(indexed_data.keys())
|
224
|
+
if not indexed_ids:
|
225
|
+
self._log_data("Vectorstore is empty, indexing all incoming documents", tool_name="index_documents")
|
226
|
+
return list(documents)
|
227
|
+
|
228
|
+
final_docs = []
|
229
|
+
docs_to_remove = []
|
230
|
+
|
231
|
+
for document in documents:
|
232
|
+
doc_id = document.metadata.get('id')
|
233
|
+
# get document's metadata and id and check if already indexed
|
234
|
+
if doc_id in indexed_ids:
|
235
|
+
# document has been indexed already, then verify `updated_on`
|
236
|
+
to_index_updated_on = document.metadata.get('updated_on')
|
237
|
+
indexed_meta = indexed_data[doc_id]['metadata']
|
238
|
+
indexed_updated_on = indexed_meta.get('updated_on')
|
239
|
+
if to_index_updated_on and indexed_updated_on and to_index_updated_on == indexed_updated_on:
|
240
|
+
# same updated_on, skip indexing
|
241
|
+
continue
|
242
|
+
# if updated_on is missing or different, we will re-index the document and remove old one
|
243
|
+
# parent doc removal
|
244
|
+
docs_to_remove.append(indexed_data[doc_id]['id'])
|
245
|
+
# mark dependent docs for removal
|
246
|
+
for dependent_doc_id in indexed_data[doc_id][IndexerKeywords.DEPENDENT_DOCS.value]:
|
247
|
+
docs_to_remove.append(indexed_data[dependent_doc_id]['id'])
|
248
|
+
else:
|
249
|
+
final_docs.append(document)
|
250
|
+
|
251
|
+
if docs_to_remove:
|
252
|
+
self._log_data(
|
253
|
+
f"Removing {len(docs_to_remove)} documents from vectorstore that are already indexed with different updated_on.",
|
254
|
+
tool_name="index_documents"
|
255
|
+
)
|
256
|
+
store.delete(ids=docs_to_remove)
|
257
|
+
|
258
|
+
return final_docs
|
259
|
+
|
260
|
+
def index_documents(self, documents: Generator[Document, None, None], progress_step: int = 20, clean_index: bool = True):
|
261
|
+
""" Index documents in the vectorstore.
|
262
|
+
|
263
|
+
Args:
|
264
|
+
documents (Any): Generator or list of documents to index.
|
265
|
+
document_processing_func (Optional[Callable]): Function to process documents after duplicates removal and before indexing.
|
266
|
+
progress_step (int): Step for progress reporting, default is 20.
|
267
|
+
clean_index (bool): If True, clean the index before re-indexing all documents.
|
268
|
+
"""
|
269
|
+
|
186
270
|
from ..langchain.interfaces.llm_processor import add_documents
|
271
|
+
|
272
|
+
# pre-process documents if needed (find duplicates, etc.)
|
273
|
+
if clean_index:
|
274
|
+
logger.info("Cleaning index before re-indexing all documents.")
|
275
|
+
self._log_data("Cleaning index before re-indexing all documents. Previous index will be removed", tool_name="index_documents")
|
276
|
+
try:
|
277
|
+
self.vectoradapter.delete_dataset(self.dataset)
|
278
|
+
self.vectoradapter.persist()
|
279
|
+
self.vectoradapter.vacuum()
|
280
|
+
self._log_data("Previous index has been removed",
|
281
|
+
tool_name="index_documents")
|
282
|
+
except Exception as e:
|
283
|
+
logger.warning(f"Failed to clean index: {str(e)}. Continuing with re-indexing.")
|
284
|
+
if isinstance(documents, types.GeneratorType):
|
285
|
+
documents = list(documents)
|
286
|
+
else:
|
287
|
+
# remove duplicates based on metadata 'id' and 'updated_on' fields
|
288
|
+
documents = self._reduce_duplicates(documents, self.vectoradapter.vectorstore)
|
289
|
+
|
290
|
+
|
291
|
+
if not documents or len(documents) == 0:
|
292
|
+
logger.info("No new documents to index after duplicate check.")
|
293
|
+
return {"status": "ok", "message": "No new documents to index."}
|
294
|
+
|
295
|
+
# if func is provided, apply it to documents
|
296
|
+
# used for processing of documents before indexing,
|
297
|
+
# e.g. to avoid time-consuming operations for documents that are already indexed
|
298
|
+
dependent_docs_generator = self.process_document_func(documents) if self.process_document_func else []
|
299
|
+
|
300
|
+
# notify user about missed required metadata fields: id, updated_on
|
301
|
+
# it is not required to have them, but it is recommended to have them for proper re-indexing and duplicate detection
|
302
|
+
for doc in documents:
|
303
|
+
if 'id' not in doc.metadata or 'updated_on' not in doc.metadata:
|
304
|
+
logger.warning(f"Document is missing required metadata field 'id' or 'updated_on': {doc.metadata}")
|
305
|
+
|
187
306
|
logger.debug(f"Indexing documents: {documents}")
|
188
307
|
logger.debug(self.vectoradapter)
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
#
|
193
|
-
self.vectoradapter.vacuum()
|
194
|
-
#
|
308
|
+
|
309
|
+
documents = documents + list(dependent_docs_generator)
|
310
|
+
total_docs = len(documents)
|
195
311
|
documents_count = 0
|
196
312
|
_documents = []
|
313
|
+
|
314
|
+
# set default progress step to 20 if out of 0...100 or None
|
315
|
+
progress_step = 20 if progress_step not in range(0, 100) else progress_step
|
316
|
+
next_progress_point = progress_step
|
197
317
|
for document in documents:
|
198
318
|
documents_count += 1
|
199
319
|
# logger.debug(f"Indexing document: {document}")
|
@@ -203,7 +323,14 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
203
323
|
add_documents(vectorstore=self.vectoradapter.vectorstore, documents=_documents)
|
204
324
|
self.vectoradapter.persist()
|
205
325
|
_documents = []
|
206
|
-
|
326
|
+
|
327
|
+
percent = math.floor((documents_count / total_docs) * 100)
|
328
|
+
if percent >= next_progress_point:
|
329
|
+
msg = f"Indexing progress: {percent}%. Processed {documents_count} of {total_docs} documents."
|
330
|
+
logger.debug(msg)
|
331
|
+
self._log_data(msg)
|
332
|
+
next_progress_point += progress_step
|
333
|
+
except Exception:
|
207
334
|
from traceback import format_exc
|
208
335
|
logger.error(f"Error: {format_exc()}")
|
209
336
|
return {"status": "error", "message": f"Error: {format_exc()}"}
|
@@ -383,9 +510,11 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
383
510
|
combined_items = [item for item in combined_items if abs(item[1]) >= cut_off]
|
384
511
|
|
385
512
|
# Sort by score and limit results
|
386
|
-
|
513
|
+
|
514
|
+
# for chroma we want ascending order (lower score is better), for others descending
|
515
|
+
combined_items.sort(key=lambda x: x[1], reverse= self.vectorstore_type.lower() != 'chroma')
|
387
516
|
combined_items = combined_items[:search_top]
|
388
|
-
|
517
|
+
|
389
518
|
# Format output based on doctype
|
390
519
|
if doctype == 'code':
|
391
520
|
return code_format(combined_items)
|
@@ -498,6 +627,21 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
498
627
|
])
|
499
628
|
return result.content
|
500
629
|
|
630
|
+
def _log_data(self, message: str, tool_name: str = "index_data"):
|
631
|
+
"""Log data and dispatch custom event for indexing progress"""
|
632
|
+
|
633
|
+
try:
|
634
|
+
dispatch_custom_event(
|
635
|
+
name="thinking_step",
|
636
|
+
data={
|
637
|
+
"message": message,
|
638
|
+
"tool_name": tool_name,
|
639
|
+
"toolkit": "vectorstore",
|
640
|
+
},
|
641
|
+
)
|
642
|
+
except Exception as e:
|
643
|
+
logger.warning(f"Failed to dispatch progress event: {str(e)}")
|
644
|
+
|
501
645
|
def get_available_tools(self):
|
502
646
|
return [
|
503
647
|
{
|
@@ -65,7 +65,8 @@ def pil_to_base64_string(pil_image):
|
|
65
65
|
return None
|
66
66
|
|
67
67
|
|
68
|
-
from alita_sdk.runtime.llms.alita import AlitaChatModel
|
68
|
+
# from alita_sdk.runtime.llms.alita import AlitaChatModel
|
69
|
+
from alita_sdk.runtime.clients.client import AlitaClient
|
69
70
|
from alita_sdk.runtime.utils.AlitaCallback import AlitaStreamlitCallback
|
70
71
|
from alita_sdk.runtime.toolkits.tools import get_toolkits, get_tools
|
71
72
|
from alita_sdk.community.utils import check_schema
|
@@ -369,7 +370,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
369
370
|
st.rerun()
|
370
371
|
|
371
372
|
# Determine login form title and expansion state
|
372
|
-
if st.session_state.
|
373
|
+
if st.session_state.client:
|
373
374
|
login_title = "✅ Elitea Login (Connected)"
|
374
375
|
# Collapse after successful login, but allow expansion
|
375
376
|
if st.session_state.login_form_expanded is True:
|
@@ -385,13 +386,13 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
385
386
|
deployment_secret = environ.get('XSECRET', 'secret')
|
386
387
|
api_key_value = environ.get('API_KEY', None)
|
387
388
|
project_id_value = int(environ.get('PROJECT_ID', 0))
|
388
|
-
if st.session_state.
|
389
|
-
deployment_value = st.session_state.
|
390
|
-
api_key_value = st.session_state.
|
391
|
-
project_id_value = st.session_state.
|
392
|
-
|
389
|
+
if st.session_state.client:
|
390
|
+
deployment_value = st.session_state.client.base_url
|
391
|
+
api_key_value = st.session_state.client.auth_token
|
392
|
+
project_id_value = st.session_state.client.project_id
|
393
|
+
|
393
394
|
# Show current connection status
|
394
|
-
if st.session_state.
|
395
|
+
if st.session_state.client:
|
395
396
|
st.success(f"Connected to: {deployment_value}")
|
396
397
|
st.info(f"Project ID: {project_id_value}")
|
397
398
|
|
@@ -403,22 +404,24 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
403
404
|
deployment_secret = st.text_input("Deployment Secret", placeholder="Enter Deployment Secret", value=deployment_secret)
|
404
405
|
|
405
406
|
# Change button text based on login status
|
406
|
-
button_text = "Re-Login" if st.session_state.
|
407
|
+
button_text = "Re-Login" if st.session_state.client else "Login"
|
407
408
|
submitted = st.form_submit_button(button_text)
|
408
409
|
|
409
410
|
if submitted:
|
410
411
|
with st.spinner("Logging to Alita..."):
|
411
412
|
try:
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
413
|
+
|
414
|
+
st.session_state.client = AlitaClient(
|
415
|
+
base_url=deployment,
|
416
|
+
project_id=project_id,
|
417
|
+
auth_token=api_key,
|
418
|
+
api_extra_headers={"X-SECRET": deployment_secret}
|
419
|
+
)
|
420
|
+
|
418
421
|
|
419
422
|
# Fetch specific project secret for pgvector connection
|
420
423
|
try:
|
421
|
-
pgvector_connstr = client.unsecret('pgvector_project_connstr')
|
424
|
+
pgvector_connstr = st.session_state.client.unsecret('pgvector_project_connstr')
|
422
425
|
if pgvector_connstr:
|
423
426
|
st.session_state.project_secrets = {'pgvector_project_connstr': pgvector_connstr}
|
424
427
|
logger.info("Successfully retrieved pgvector connection string from project secrets")
|
@@ -429,7 +432,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
429
432
|
logger.warning(f"Could not retrieve pgvector connection string: {str(e)}")
|
430
433
|
st.session_state.project_secrets = {}
|
431
434
|
|
432
|
-
integrations = client.all_models_and_integrations()
|
435
|
+
integrations = st.session_state.client.all_models_and_integrations()
|
433
436
|
unique_models = set()
|
434
437
|
models_list = []
|
435
438
|
for entry in integrations:
|
@@ -438,7 +441,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
438
441
|
if model.get('capabilities', {}).get('chat_completion') and model['name'] not in unique_models:
|
439
442
|
unique_models.add(model['name'])
|
440
443
|
models_list.append({'name': model['name'], 'integration_id': entry['uid']})
|
441
|
-
st.session_state.agents = client.get_list_of_apps()
|
444
|
+
st.session_state.agents = st.session_state.client.get_list_of_apps()
|
442
445
|
st.session_state.models = models_list
|
443
446
|
clear_chat_history()
|
444
447
|
|
@@ -454,6 +457,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
454
457
|
except Exception as e:
|
455
458
|
logger.error(f"Error loggin to ELITEA: {format_exc()}")
|
456
459
|
st.session_state.agents = None
|
460
|
+
st.session_state.client = None
|
457
461
|
st.session_state.models = None
|
458
462
|
st.session_state.llm = None
|
459
463
|
st.session_state.project_secrets = None
|
@@ -463,7 +467,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
463
467
|
llmconfig, toolkit_config = st.tabs(["Alita Agents", "Toolkit Testing"])
|
464
468
|
|
465
469
|
with llmconfig:
|
466
|
-
if st.session_state.
|
470
|
+
if st.session_state.client:
|
467
471
|
st.title("Available Agents")
|
468
472
|
st.write("This one will load latest version of agent")
|
469
473
|
with st.form("agents_form", clear_on_submit=False):
|
@@ -477,7 +481,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
477
481
|
agent = next((a for a in st.session_state.agents if a['name'] == options), None)
|
478
482
|
if agent:
|
479
483
|
agent_id = agent['id']
|
480
|
-
agent_details = st.session_state.
|
484
|
+
agent_details = st.session_state.client.get_app_details(agent_id)
|
481
485
|
latest_version = next((v for v in agent_details['versions'] if v['name'] == agent_version_name), None)
|
482
486
|
if latest_version:
|
483
487
|
agent_version_id = latest_version['id']
|
@@ -504,11 +508,11 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
504
508
|
|
505
509
|
# Try to get the complete agent configuration
|
506
510
|
try:
|
507
|
-
agent_version_details = st.session_state.
|
511
|
+
agent_version_details = st.session_state.client.get_app_version_details(agent_id, agent_version_id)
|
508
512
|
agent_full_config = agent_version_details
|
509
513
|
except AttributeError:
|
510
514
|
try:
|
511
|
-
agent_version_details = st.session_state.
|
515
|
+
agent_version_details = st.session_state.client.get_application_version_details(agent_id, agent_version_id)
|
512
516
|
agent_full_config = agent_version_details
|
513
517
|
except AttributeError:
|
514
518
|
# Use the version details we already have
|
@@ -630,8 +634,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
630
634
|
st.session_state.agent_toolkit_configs = {}
|
631
635
|
st.session_state.agent_raw_config = None
|
632
636
|
|
633
|
-
st.session_state.agent_executor = st.session_state.
|
634
|
-
client=st.session_state.llm,
|
637
|
+
st.session_state.agent_executor = st.session_state.client.application(
|
635
638
|
application_id=agent_id,
|
636
639
|
application_version_id=agent_version_id,
|
637
640
|
app_type=agent_type if agent_type else None,
|
@@ -658,7 +661,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
658
661
|
""")
|
659
662
|
|
660
663
|
# Check if user is logged in
|
661
|
-
if not st.session_state.
|
664
|
+
if not st.session_state.client:
|
662
665
|
st.warning("⚠️ **Please log in first!**")
|
663
666
|
st.info("""
|
664
667
|
📋 **To use Toolkit Testing:**
|
@@ -671,7 +674,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
671
674
|
st.stop()
|
672
675
|
|
673
676
|
# User is logged in, proceed with toolkit testing
|
674
|
-
if st.session_state.
|
677
|
+
if st.session_state.client:
|
675
678
|
# Show project secrets status with detailed debugging
|
676
679
|
secrets_status = st.session_state.project_secrets
|
677
680
|
|
@@ -685,7 +688,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
685
688
|
# Debug info (can be removed later)
|
686
689
|
with st.expander("🔍 Debug Info", expanded=False):
|
687
690
|
st.write(f"**Project Secrets Status:** {type(secrets_status)} - {secrets_status}")
|
688
|
-
st.write(f"**LLM Status:** {'Connected' if st.session_state.llm else 'Not Connected'}")
|
691
|
+
# st.write(f"**LLM Status:** {'Connected' if st.session_state.llm else 'Not Connected'}")
|
689
692
|
|
690
693
|
# Toolkit selection and configuration
|
691
694
|
st.markdown("---")
|
@@ -1021,7 +1024,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
1021
1024
|
st.markdown("👈 Please use the **Alita Login Form** in the sidebar to authenticate.")
|
1022
1025
|
|
1023
1026
|
# Main content area
|
1024
|
-
if st.session_state.
|
1027
|
+
if st.session_state.client and st.session_state.agent_executor and st.session_state.agent_chat:
|
1025
1028
|
try:
|
1026
1029
|
st.title(st.session_state.agent_name)
|
1027
1030
|
except:
|
@@ -1043,7 +1046,7 @@ def run_streamlit(st, ai_icon=None, user_icon=None):
|
|
1043
1046
|
st.session_state.thread_id = response.get("thread_id", None)
|
1044
1047
|
st.session_state.messages.append({"role": "assistant", "content": response["output"]})
|
1045
1048
|
|
1046
|
-
elif st.session_state.
|
1049
|
+
elif st.session_state.client and st.session_state.show_toolkit_testing and st.session_state.configured_toolkit:
|
1047
1050
|
# Toolkit Testing Main View
|
1048
1051
|
st.title("🚀 Toolkit Testing Interface")
|
1049
1052
|
|
@@ -1281,7 +1284,7 @@ Please explain how you would use these tools to help the user, even though I can
|
|
1281
1284
|
st.rerun()
|
1282
1285
|
|
1283
1286
|
else:
|
1284
|
-
if st.session_state.
|
1287
|
+
if st.session_state.client:
|
1285
1288
|
st.title("🎯 Alita SDK Toolkit Interface")
|
1286
1289
|
st.markdown("""
|
1287
1290
|
### Welcome to the Alita SDK!
|
alita_sdk/runtime/utils/utils.py
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
import re
|
2
|
+
from enum import Enum
|
2
3
|
|
3
4
|
TOOLKIT_SPLITTER = "___"
|
4
5
|
|
6
|
+
class IndexerKeywords(Enum):
|
7
|
+
DEPENDENT_DOCS = 'dependent_docs'
|
8
|
+
PARENT = 'parent_id'
|
9
|
+
|
5
10
|
# This pattern matches characters that are NOT alphanumeric, underscores, or hyphens
|
6
11
|
clean_string_pattern = re.compile(r'[^a-zA-Z0-9_.-]')
|
7
12
|
|
alita_sdk/tools/__init__.py
CHANGED
@@ -2,6 +2,7 @@ import logging
|
|
2
2
|
from importlib import import_module
|
3
3
|
from typing import Optional
|
4
4
|
|
5
|
+
from langchain_core.tools import ToolException
|
5
6
|
from langgraph.store.base import BaseStore
|
6
7
|
|
7
8
|
logger = logging.getLogger(__name__)
|
@@ -80,6 +81,8 @@ _safe_import_tool('postman', 'postman', 'get_tools', 'PostmanToolkit')
|
|
80
81
|
_safe_import_tool('memory', 'memory', 'get_tools', 'MemoryToolkit')
|
81
82
|
_safe_import_tool('zephyr_squad', 'zephyr_squad', 'get_tools', 'ZephyrSquadToolkit')
|
82
83
|
_safe_import_tool('slack', 'slack', 'get_tools', 'SlackToolkit')
|
84
|
+
_safe_import_tool('bigquery', 'google.bigquery', 'get_tools', 'BigQueryToolkit')
|
85
|
+
_safe_import_tool('delta_lake', 'aws.delta_lake', 'get_tools', 'DeltaLakeToolkit')
|
83
86
|
|
84
87
|
# Log import summary
|
85
88
|
available_count = len(AVAILABLE_TOOLS)
|
@@ -111,6 +114,7 @@ def get_tools(tools_list, alita, llm, store: Optional[BaseStore] = None, *args,
|
|
111
114
|
|
112
115
|
except Exception as e:
|
113
116
|
logger.error(f"Error getting tools for {tool_type}: {e}")
|
117
|
+
raise ToolException(f"Error getting tools for {tool_type}: {e}")
|
114
118
|
|
115
119
|
# Handle ADO repos special case (it might be requested as azure_devops_repos)
|
116
120
|
elif tool_type in ['ado_repos', 'azure_devops_repos'] and 'ado_repos' in AVAILABLE_TOOLS:
|