alita-sdk 0.3.205__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 +25 -11
- alita_sdk/runtime/utils/streamlit.py +33 -30
- alita_sdk/runtime/utils/utils.py +5 -0
- alita_sdk/tools/__init__.py +2 -0
- alita_sdk/tools/ado/repos/repos_wrapper.py +20 -13
- 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 +9 -4
- 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/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 +11 -3
- alita_sdk/tools/testrail/api_wrapper.py +39 -16
- alita_sdk/tools/utils/content_parser.py +77 -13
- {alita_sdk-0.3.205.dist-info → alita_sdk-0.3.206.dist-info}/METADATA +1 -1
- {alita_sdk-0.3.205.dist-info → alita_sdk-0.3.206.dist-info}/RECORD +27 -28
- alita_sdk/runtime/llms/alita.py +0 -259
- {alita_sdk-0.3.205.dist-info → alita_sdk-0.3.206.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.205.dist-info → alita_sdk-0.3.206.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.205.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,6 +1,9 @@
|
|
1
1
|
import json
|
2
2
|
import math
|
3
|
-
|
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
8
|
from ..langchain.tools.vector import VectorAdapter
|
6
9
|
from langchain_core.messages import HumanMessage
|
@@ -8,6 +11,7 @@ from alita_sdk.tools.elitea_base import BaseToolApiWrapper
|
|
8
11
|
from logging import getLogger
|
9
12
|
|
10
13
|
from ..utils.logging import dispatch_custom_event
|
14
|
+
from ..utils.utils import IndexerKeywords
|
11
15
|
|
12
16
|
logger = getLogger(__name__)
|
13
17
|
|
@@ -197,25 +201,29 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
197
201
|
for doc_str, meta, db_id in zip(data['documents'], data['metadatas'], data['ids']):
|
198
202
|
doc = json.loads(doc_str)
|
199
203
|
doc_id = str(meta['id'])
|
204
|
+
dependent_docs = meta.get(IndexerKeywords.DEPENDENT_DOCS.value, [])
|
205
|
+
parent_id = meta.get(IndexerKeywords.PARENT.value, -1)
|
200
206
|
result[doc_id] = {
|
201
207
|
'metadata': meta,
|
202
208
|
'document': doc,
|
203
|
-
'id': db_id
|
209
|
+
'id': db_id,
|
210
|
+
IndexerKeywords.DEPENDENT_DOCS.value: dependent_docs,
|
211
|
+
IndexerKeywords.PARENT.value: parent_id
|
204
212
|
}
|
205
213
|
except Exception as e:
|
206
214
|
logger.error(f"Failed to get indexed data from vectorstore: {str(e)}. Continuing with empty index.")
|
207
215
|
return result
|
208
216
|
|
209
|
-
def _reduce_duplicates(self, documents, store) -> List[Any]:
|
217
|
+
def _reduce_duplicates(self, documents: Generator[Document, None, None], store) -> List[Any]:
|
210
218
|
"""Remove documents already indexed in the vectorstore based on metadata 'id' and 'updated_on' fields."""
|
211
219
|
|
212
220
|
self._log_data("Verification of documents to index started", tool_name="index_documents")
|
213
221
|
|
214
|
-
|
215
|
-
indexed_ids = set(
|
222
|
+
indexed_data = self._get_indexed_data(store)
|
223
|
+
indexed_ids = set(indexed_data.keys())
|
216
224
|
if not indexed_ids:
|
217
225
|
self._log_data("Vectorstore is empty, indexing all incoming documents", tool_name="index_documents")
|
218
|
-
return documents
|
226
|
+
return list(documents)
|
219
227
|
|
220
228
|
final_docs = []
|
221
229
|
docs_to_remove = []
|
@@ -226,13 +234,17 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
226
234
|
if doc_id in indexed_ids:
|
227
235
|
# document has been indexed already, then verify `updated_on`
|
228
236
|
to_index_updated_on = document.metadata.get('updated_on')
|
229
|
-
indexed_meta =
|
237
|
+
indexed_meta = indexed_data[doc_id]['metadata']
|
230
238
|
indexed_updated_on = indexed_meta.get('updated_on')
|
231
239
|
if to_index_updated_on and indexed_updated_on and to_index_updated_on == indexed_updated_on:
|
232
240
|
# same updated_on, skip indexing
|
233
241
|
continue
|
234
242
|
# if updated_on is missing or different, we will re-index the document and remove old one
|
235
|
-
|
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'])
|
236
248
|
else:
|
237
249
|
final_docs.append(document)
|
238
250
|
|
@@ -245,7 +257,7 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
245
257
|
|
246
258
|
return final_docs
|
247
259
|
|
248
|
-
def index_documents(self, documents, progress_step: int = 20, clean_index: bool = True):
|
260
|
+
def index_documents(self, documents: Generator[Document, None, None], progress_step: int = 20, clean_index: bool = True):
|
249
261
|
""" Index documents in the vectorstore.
|
250
262
|
|
251
263
|
Args:
|
@@ -269,6 +281,8 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
269
281
|
tool_name="index_documents")
|
270
282
|
except Exception as e:
|
271
283
|
logger.warning(f"Failed to clean index: {str(e)}. Continuing with re-indexing.")
|
284
|
+
if isinstance(documents, types.GeneratorType):
|
285
|
+
documents = list(documents)
|
272
286
|
else:
|
273
287
|
# remove duplicates based on metadata 'id' and 'updated_on' fields
|
274
288
|
documents = self._reduce_duplicates(documents, self.vectoradapter.vectorstore)
|
@@ -281,7 +295,7 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
281
295
|
# if func is provided, apply it to documents
|
282
296
|
# used for processing of documents before indexing,
|
283
297
|
# e.g. to avoid time-consuming operations for documents that are already indexed
|
284
|
-
self.process_document_func(documents) if self.process_document_func else
|
298
|
+
dependent_docs_generator = self.process_document_func(documents) if self.process_document_func else []
|
285
299
|
|
286
300
|
# notify user about missed required metadata fields: id, updated_on
|
287
301
|
# it is not required to have them, but it is recommended to have them for proper re-indexing and duplicate detection
|
@@ -292,7 +306,7 @@ class VectorStoreWrapper(BaseToolApiWrapper):
|
|
292
306
|
logger.debug(f"Indexing documents: {documents}")
|
293
307
|
logger.debug(self.vectoradapter)
|
294
308
|
|
295
|
-
documents = list(
|
309
|
+
documents = documents + list(dependent_docs_generator)
|
296
310
|
total_docs = len(documents)
|
297
311
|
documents_count = 0
|
298
312
|
_documents = []
|
@@ -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__)
|
@@ -113,6 +114,7 @@ def get_tools(tools_list, alita, llm, store: Optional[BaseStore] = None, *args,
|
|
113
114
|
|
114
115
|
except Exception as e:
|
115
116
|
logger.error(f"Error getting tools for {tool_type}: {e}")
|
117
|
+
raise ToolException(f"Error getting tools for {tool_type}: {e}")
|
116
118
|
|
117
119
|
# Handle ADO repos special case (it might be requested as azure_devops_repos)
|
118
120
|
elif tool_type in ['ado_repos', 'azure_devops_repos'] and 'ado_repos' in AVAILABLE_TOOLS:
|
@@ -250,6 +250,7 @@ class ReposApiWrapper(BaseCodeToolApiWrapper):
|
|
250
250
|
token: Optional[SecretStr]
|
251
251
|
_client: Optional[GitClient] = PrivateAttr()
|
252
252
|
|
253
|
+
llm: Optional[Any] = None
|
253
254
|
# Vector store configuration
|
254
255
|
connection_string: Optional[SecretStr] = None
|
255
256
|
collection_name: Optional[str] = None
|
@@ -303,24 +304,30 @@ class ReposApiWrapper(BaseCodeToolApiWrapper):
|
|
303
304
|
|
304
305
|
def _get_files(
|
305
306
|
self,
|
306
|
-
|
307
|
-
|
307
|
+
path: str = "",
|
308
|
+
branch: str = None,
|
308
309
|
recursion_level: str = "Full",
|
309
310
|
) -> str:
|
311
|
+
"""Get list of files from a repository path and branch.
|
312
|
+
|
313
|
+
Args:
|
314
|
+
path (str): Path within the repository to list files from
|
315
|
+
branch (str): Branch to get files from. Defaults to base_branch if None.
|
316
|
+
recursion_level (str): OneLevel - includes immediate children, Full - includes all items, None - no recursion
|
317
|
+
|
318
|
+
Returns:
|
319
|
+
List[str]: List of file paths
|
310
320
|
"""
|
311
|
-
|
312
|
-
recursion_level: OneLevel - includes immediate children, Full - includes all items, None - no recursion
|
313
|
-
"""
|
314
|
-
branch_name = branch_name if branch_name else self.base_branch
|
321
|
+
branch = branch if branch else self.base_branch
|
315
322
|
files: List[str] = []
|
316
323
|
try:
|
317
324
|
version_descriptor = GitVersionDescriptor(
|
318
|
-
version=
|
325
|
+
version=branch, version_type="branch"
|
319
326
|
)
|
320
327
|
items = self._client.get_items(
|
321
328
|
repository_id=self.repository_id,
|
322
329
|
project=self.project,
|
323
|
-
scope_path=
|
330
|
+
scope_path=path,
|
324
331
|
recursion_level=recursion_level,
|
325
332
|
version_descriptor=version_descriptor,
|
326
333
|
include_content_metadata=True,
|
@@ -334,7 +341,7 @@ class ReposApiWrapper(BaseCodeToolApiWrapper):
|
|
334
341
|
item = items.pop(0)
|
335
342
|
if item.git_object_type == "blob":
|
336
343
|
files.append(item.path)
|
337
|
-
return str
|
344
|
+
return files # Changed to return list directly instead of str
|
338
345
|
|
339
346
|
def set_active_branch(self, branch_name: str) -> str:
|
340
347
|
"""
|
@@ -389,7 +396,7 @@ class ReposApiWrapper(BaseCodeToolApiWrapper):
|
|
389
396
|
logger.error(msg)
|
390
397
|
return ToolException(msg)
|
391
398
|
|
392
|
-
def list_files(self, directory_path: str = "", branch_name: str = None) -> str:
|
399
|
+
def list_files(self, directory_path: str = "", branch_name: str = None) -> List[str]:
|
393
400
|
"""
|
394
401
|
Recursively fetches files from a directory in the repo.
|
395
402
|
|
@@ -398,12 +405,12 @@ class ReposApiWrapper(BaseCodeToolApiWrapper):
|
|
398
405
|
branch_name (str): The name of the branch where the files to be received.
|
399
406
|
|
400
407
|
Returns:
|
401
|
-
str: List of file paths, or an error message.
|
408
|
+
List[str]: List of file paths, or an error message.
|
402
409
|
"""
|
403
410
|
self.active_branch = branch_name if branch_name else self.active_branch
|
404
411
|
return self._get_files(
|
405
|
-
|
406
|
-
|
412
|
+
path=directory_path,
|
413
|
+
branch=self.active_branch if self.active_branch else self.base_branch,
|
407
414
|
)
|
408
415
|
|
409
416
|
def parse_pull_request_comments(
|