Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
23b2898
Update Finetuner search metadata functional tests (#172)
lucas-aixplain May 2, 2024
208a081
Downgrade dataclasses-json for compatibility (#170)
thiago-aixplain May 2, 2024
a837e1a
Fix model cost parameters (#179)
thiago-aixplain May 10, 2024
754f478
Treat label URLs (#176)
thiago-aixplain May 15, 2024
f1c9935
Add new metric test (#181)
thiago-aixplain Jun 4, 2024
a48ccfd
LLMModel class and parameters (#184)
thiago-aixplain Jun 5, 2024
c7f59ce
Gpus (#185)
mikelam-us-aixplain Jun 5, 2024
16eb2e1
Create and get Pipelines with api key as input parameter (#187)
thiago-aixplain Jun 7, 2024
2849d6f
Merge branch 'test' into development
thiago-aixplain Jun 11, 2024
04246b1
M 6769474660 save pipelines (#191)
thiago-aixplain Jun 17, 2024
73021a7
M 6769474660 save pipelines (#192)
thiago-aixplain Jun 18, 2024
474602b
Solving bug when LLM parameters are set on data (#196)
thiago-aixplain Jun 26, 2024
c471703
Merge branch 'test' into development
thiago-aixplain Jun 26, 2024
3695686
Fix pipeline functional test (#200)
lucas-aixplain Jul 3, 2024
9014061
M 6656407247 agentification (#197)
thiago-aixplain Jul 13, 2024
e9091c2
Fixing circular import in the SDK (#211)
thiago-aixplain Jul 30, 2024
f437815
create model/pipeline tools from AgentFactory (#214)
thiago-aixplain Aug 2, 2024
8457087
Merge branch 'test' into development
thiago-aixplain Aug 6, 2024
03009c6
Set model ID as a parameter (#216)
thiago-aixplain Aug 7, 2024
02f7482
Content inputs to be processed according to the query. (#215)
thiago-aixplain Aug 7, 2024
4947959
ENG-1: programmatic api introduced (#219)
kadirpekel Aug 9, 2024
ef16dd5
Updated image upload tests (#213)
mikelam-us-aixplain Aug 12, 2024
d0ad51d
Eng 217 local path (#220)
thiago-aixplain Aug 13, 2024
dca1a37
Eng 389 fix tests (#222)
thiago-aixplain Aug 13, 2024
d43f67f
Merge branch 'test' into development
thiago-aixplain Aug 13, 2024
b113368
Tool Validation when creating agents (#226)
xainaz Aug 19, 2024
0032947
Eng 398 sdk get users credits - Initial (#232)
xainaz Aug 20, 2024
a567535
Eng 398 sdk get users credits (#234)
thiago-aixplain Aug 20, 2024
e919fab
Removed wallet_factoy.py (#235)
xainaz Aug 21, 2024
9ffe3f7
Merge branch 'test' into development
thiago-aixplain Aug 22, 2024
115bf13
Adding supervisor/planning options into SDK (#233)
thiago-aixplain Aug 22, 2024
3357e56
Adjustments to get user credits (#237)
xainaz Aug 23, 2024
ee76afd
Put conditions inside try statements according to changes required. (…
xainaz Aug 23, 2024
1660f5f
Fixing none credit (#238)
xainaz Aug 27, 2024
ed20ba7
Merge branch 'test' into development
thiago-aixplain Aug 27, 2024
481dab2
Merge branch 'test' into development
thiago-aixplain Aug 27, 2024
9a89f52
Update click dependency (#241)
thiago-aixplain Aug 28, 2024
cb0d313
Added input and output attributes to model (#244)
xainaz Sep 2, 2024
716d898
Eng 467 ai xplain sdk update finetune functional tests to cover all n…
xainaz Sep 3, 2024
50d7c6a
Merge branch 'test' into development
thiago-aixplain Sep 4, 2024
f3d89ed
Added name to update (#245)
xainaz Sep 5, 2024
1700304
ENG-504: Make the agent architecture configurable (#243)
thiago-aixplain Sep 6, 2024
357e10d
Eng 544 ai xplain sdk update llm functional tests to cover all new ll…
xainaz Sep 9, 2024
731a150
Eng 399 - Introducing Metric Nodes in Designer (#247)
kadirpekel Sep 10, 2024
0e62774
Add TeamAgent factory and module. Fix typos in code comments (#227)
lucas-aixplain Sep 12, 2024
7f64955
Merge branch 'test' into development
ikxplain Sep 19, 2024
b93a706
Add fileMetadata information in script node (#251)
thiago-aixplain Sep 20, 2024
ea846e6
Merge branch 'test' into development
thiago-aixplain Sep 23, 2024
f7bd983
Name Validation of Agents and Team Agents (#253)
thiago-aixplain Sep 23, 2024
b886287
Fixes in pipeline design and reconstructor node (#255)
thiago-aixplain Sep 26, 2024
8bd6460
Add get method to agent and team agent tests (#259)
lucas-aixplain Sep 29, 2024
4bd9bc0
initial API key factory (#261)
xainaz Oct 7, 2024
63e0f82
BUG-177: Fixed pipeline validation (#262)
kadirpekel Oct 7, 2024
0208be1
Get usage limit (#264)
thiago-aixplain Oct 8, 2024
662420e
Update SDK version (#266)
thiago-aixplain Oct 9, 2024
08c925e
Merge branch 'test' into development
thiago-aixplain Oct 9, 2024
828bdee
Eng 739 get api key (#268)
thiago-aixplain Oct 9, 2024
5ece957
Merge branch 'test' into development
thiago-aixplain Oct 9, 2024
b13c21d
Fix Update API Key Bug (#272)
thiago-aixplain Oct 10, 2024
2a2a476
Merge branch 'test' into development
thiago-aixplain Oct 10, 2024
8182293
Eng 735 ai xplain sdk improve error log messages (#271)
xainaz Oct 16, 2024
ecba34f
Max tokens and iterations in agents/teams (#276)
thiago-aixplain Oct 17, 2024
2127cc5
Update model running endpoints from v1 to v2 (#275)
thiago-aixplain Oct 17, 2024
736a7b1
Eng 711 new model endpoints (#278)
thiago-aixplain Oct 17, 2024
fa33531
Group of Improvements in API Key CRUD (#277)
thiago-aixplain Oct 25, 2024
349ea60
Bug 149 - Path validation removed and decision node output param hand…
kadirpekel Oct 25, 2024
dd46dcf
Changed function to required field (#283)
xainaz Oct 29, 2024
b86d5e7
BUG-206: Fixed passthrough parameter reflection to next node (#284)
kadirpekel Oct 29, 2024
0dbfab1
Get model description (#286)
thiago-aixplain Oct 29, 2024
2eebc27
Merge branch 'test' into development
thiago-aixplain Oct 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions aixplain/factories/api_key_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,20 @@
from datetime import datetime
from typing import Text, List, Optional, Dict, Union
from aixplain.utils.file_utils import _request_with_retry
from aixplain.modules.api_key import APIKey, APIKeyGlobalLimits, APIKeyUsageLimit
from aixplain.modules.api_key import APIKey, APIKeyLimits, APIKeyUsageLimit


class APIKeyFactory:
backend_url = config.BACKEND_URL

@classmethod
def get(cls, api_key: Text) -> APIKey:
"""Get an API key"""
for api_key_obj in cls.list():
if str(api_key_obj.access_key).startswith(api_key[:4]) and str(api_key_obj.access_key).endswith(api_key[-4:]):
return api_key_obj
raise Exception(f"API Key Error: API key {api_key} not found")

@classmethod
def list(cls) -> List[APIKey]:
"""List all API keys"""
Expand All @@ -30,7 +38,7 @@ def list(cls) -> List[APIKey]:
name=key["name"],
budget=key["budget"] if "budget" in key else None,
global_limits=key["globalLimits"] if "globalLimits" in key else None,
asset_limits=key["assetLimits"] if "assetLimits" in key else [],
asset_limits=key["assetsLimits"] if "assetsLimits" in key else [],
expires_at=key["expiresAt"] if "expiresAt" in key else None,
access_key=key["accessKey"],
is_admin=key["isAdmin"],
Expand All @@ -46,8 +54,8 @@ def create(
cls,
name: Text,
budget: int,
global_limits: Union[Dict, APIKeyGlobalLimits],
asset_limits: List[Union[Dict, APIKeyGlobalLimits]],
global_limits: Union[Dict, APIKeyLimits],
asset_limits: List[Union[Dict, APIKeyLimits]],
expires_at: datetime,
) -> APIKey:
"""Create a new API key"""
Expand Down Expand Up @@ -84,6 +92,7 @@ def create(
@classmethod
def update(cls, api_key: APIKey) -> APIKey:
"""Update an existing API key"""
api_key.validate()
try:
resp = "Unspecified error"
url = f"{cls.backend_url}/sdk/api-keys/{api_key.id}"
Expand Down Expand Up @@ -112,12 +121,10 @@ def update(cls, api_key: APIKey) -> APIKey:
raise Exception(f"API Key Update Error: Failed to update API key with ID {api_key.id}. Error: {str(resp)}")

@classmethod
def get_usage_limit(cls, api_key: Text = config.TEAM_API_KEY, asset_id: Optional[Text] = None) -> APIKeyUsageLimit:
"""Get API key usage limit"""
def get_usage_limits(cls, api_key: Text = config.TEAM_API_KEY, asset_id: Optional[Text] = None) -> List[APIKeyUsageLimit]:
"""Get API key usage limits"""
try:
url = f"{config.BACKEND_URL}/sdk/api-keys/usage-limits"
if asset_id is not None:
url += f"?assetId={asset_id}"
headers = {"Authorization": f"Token {api_key}", "Content-Type": "application/json"}
logging.info(f"Start service for GET API Key Usage - {url} - {headers}")
r = _request_with_retry("GET", url, headers=headers)
Expand All @@ -128,11 +135,16 @@ def get_usage_limit(cls, api_key: Text = config.TEAM_API_KEY, asset_id: Optional
raise Exception(f"{message}")

if 200 <= r.status_code < 300:
return APIKeyUsageLimit(
request_count=resp["requestCount"],
request_count_limit=resp["requestCountLimit"],
token_count=resp["tokenCount"],
token_count_limit=resp["tokenCountLimit"],
)
return [
APIKeyUsageLimit(
daily_request_count=limit["requestCount"],
daily_request_limit=limit["requestCountLimit"],
daily_token_count=limit["tokenCount"],
daily_token_limit=limit["tokenCountLimit"],
model=limit["assetId"] if "assetId" in limit else None,
)
for limit in resp
if asset_id is None or ("assetId" in limit and limit["assetId"] == asset_id)
]
else:
raise Exception(f"API Key Usage Error: Failed to get usage. Error: {str(resp)}")
5 changes: 3 additions & 2 deletions aixplain/factories/model_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def _create_model_from_response(cls, response: Dict) -> Model:
return ModelClass(
response["id"],
response["name"],
description=response.get("description", ""),
supplier=response["supplier"],
api_key=response["api_key"],
cost=response["pricing"],
Expand Down Expand Up @@ -221,8 +222,8 @@ def _get_assets_from_page(
@classmethod
def list(
cls,
function: Function,
query: Optional[Text] = "",
function: Optional[Function] = None,
suppliers: Optional[Union[Supplier, List[Supplier]]] = None,
source_languages: Optional[Union[Language, List[Language]]] = None,
target_languages: Optional[Union[Language, List[Language]]] = None,
Expand All @@ -236,7 +237,7 @@ def list(
"""Gets the first k given models based on the provided task and language filters

Args:
function (Optional[Function], optional): function filter. Defaults to None.
function (Function): function filter.
source_languages (Optional[Union[Language, List[Language]]], optional): language filter of input data. Defaults to None.
target_languages (Optional[Union[Language, List[Language]]], optional): language filter of output data. Defaults to None.
is_finetunable (Optional[bool], optional): can be finetuned or not. Defaults to None.
Expand Down
17 changes: 16 additions & 1 deletion aixplain/factories/pipeline_factory/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,23 @@ def build_from_response(response: Dict, load_architecture: bool = False) -> Pipe
node.label = node_json["label"]
pipeline.add_node(node)

# Decision nodes' output parameters are defined based on their
# input parameters linked. So here we have to make sure that
# decision nodes (having passthrough parameter) should be first
# linked
link_jsons = response["links"][:]
decision_links = []
for link_json in link_jsons:
for pm in link_json["paramMapping"]:
if pm["to"] == "passthrough":
decision_link_index = link_jsons.index(link_json)
decision_link = link_jsons.pop(decision_link_index)
decision_links.append(decision_link)

link_jsons = decision_links + link_jsons

# instantiating links
for link_json in response["links"]:
for link_json in link_jsons:
for param_mapping in link_json["paramMapping"]:
link = Link(
from_node=pipeline.get_node(link_json["from"]),
Expand Down
2 changes: 1 addition & 1 deletion aixplain/modules/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,4 @@
from .agent import Agent
from .agent.tool import Tool
from .team_agent import TeamAgent
from .api_key import APIKey, APIKeyGlobalLimits, APIKeyUsageLimit
from .api_key import APIKey, APIKeyLimits, APIKeyUsageLimit
97 changes: 72 additions & 25 deletions aixplain/modules/api_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from typing import Dict, List, Optional, Text, Union


class APIKeyGlobalLimits:
class APIKeyLimits:
def __init__(
self,
token_per_minute: int,
Expand All @@ -27,19 +27,31 @@ def __init__(


class APIKeyUsageLimit:
def __init__(self, request_count: int, request_count_limit: int, token_count: int, token_count_limit: int):
"""Get the usage limits of an API key
def __init__(
self,
daily_request_count: int,
daily_request_limit: int,
daily_token_count: int,
daily_token_limit: int,
model: Optional[Union[Text, Model]] = None,
):
"""Get the usage limits of an API key globally (model equals to None) or for a specific model.

Args:
request_count (int): number of requests made
request_count_limit (int): limit of requests
token_count (int): number of tokens used
token_count_limit (int): limit of tokens
daily_request_count (int): number of requests made
daily_request_limit (int): limit of requests
daily_token_count (int): number of tokens used
daily_token_limit (int): limit of tokens
model (Optional[Union[Text, Model]], optional): Model which the limits apply. Defaults to None.
"""
self.request_count = request_count
self.request_count_limit = request_count_limit
self.token_count = token_count
self.token_count_limit = token_count_limit
self.daily_request_count = daily_request_count
self.daily_request_limit = daily_request_limit
self.daily_token_count = daily_token_count
self.daily_token_limit = daily_token_limit
if model is not None and isinstance(model, str):
from aixplain.factories import ModelFactory

self.model = ModelFactory.get(model)


class APIKey:
Expand All @@ -48,8 +60,8 @@ def __init__(
name: Text,
expires_at: Optional[Union[datetime, Text]] = None,
budget: Optional[float] = None,
asset_limits: List[APIKeyGlobalLimits] = [],
global_limits: Optional[Union[Dict, APIKeyGlobalLimits]] = None,
asset_limits: List[APIKeyLimits] = [],
global_limits: Optional[Union[Dict, APIKeyLimits]] = None,
id: int = "",
access_key: Optional[Text] = None,
is_admin: bool = False,
Expand All @@ -59,7 +71,7 @@ def __init__(
self.budget = budget
self.global_limits = global_limits
if global_limits is not None and isinstance(global_limits, dict):
self.global_limits = APIKeyGlobalLimits(
self.global_limits = APIKeyLimits(
token_per_minute=global_limits["tpm"],
token_per_day=global_limits["tpd"],
request_per_minute=global_limits["rpm"],
Expand All @@ -68,7 +80,7 @@ def __init__(
self.asset_limits = asset_limits
for i, asset_limit in enumerate(self.asset_limits):
if isinstance(asset_limit, dict):
self.asset_limits[i] = APIKeyGlobalLimits(
self.asset_limits[i] = APIKeyLimits(
token_per_minute=asset_limit["tpm"],
token_per_day=asset_limit["tpd"],
request_per_minute=asset_limit["rpm"],
Expand Down Expand Up @@ -110,7 +122,7 @@ def to_dict(self) -> Dict:
"id": self.id,
"name": self.name,
"budget": self.budget,
"assetLimits": [],
"assetsLimits": [],
"expiresAt": self.expires_at,
}

Expand All @@ -126,7 +138,7 @@ def to_dict(self) -> Dict:
}

for i, asset_limit in enumerate(self.asset_limits):
payload["assetLimits"].append(
payload["assetsLimits"].append(
{
"tpm": asset_limit.token_per_minute,
"tpd": asset_limit.token_per_day,
Expand Down Expand Up @@ -157,8 +169,6 @@ def get_usage(self, asset_id: Optional[Text] = None) -> APIKeyUsageLimit:
url = f"{config.BACKEND_URL}/sdk/api-keys/{self.id}/usage-limits"
headers = {"Authorization": f"Token {config.TEAM_API_KEY}", "Content-Type": "application/json"}
logging.info(f"Start service for GET API Key Usage - {url} - {headers}")
if asset_id is not None:
url += f"?assetId={asset_id}"
r = _request_with_retry("GET", url, headers=headers)
resp = r.json()
except Exception:
Expand All @@ -167,11 +177,48 @@ def get_usage(self, asset_id: Optional[Text] = None) -> APIKeyUsageLimit:
raise Exception(f"{message}")

if 200 <= r.status_code < 300:
return APIKeyUsageLimit(
request_count=resp["requestCount"],
request_count_limit=resp["requestCountLimit"],
token_count=resp["tokenCount"],
token_count_limit=resp["tokenCountLimit"],
)
return [
APIKeyUsageLimit(
daily_request_count=limit["requestCount"],
daily_request_limit=limit["requestCountLimit"],
daily_token_count=limit["tokenCount"],
daily_token_limit=limit["tokenCountLimit"],
model=limit["assetId"] if "assetId" in limit else None,
)
for limit in resp
if asset_id is None or ("assetId" in limit and limit["assetId"] == asset_id)
]
else:
raise Exception(f"API Key Usage Error: Failed to get usage. Error: {str(resp)}")

def __set_limit(self, limit: int, model: Optional[Union[Text, Model]], limit_type: Text) -> None:
"""Set a limit for an API key"""
if model is None:
setattr(self.global_limits, limit_type, limit)
else:
if isinstance(model, Model):
model = model.id
is_found = False
for i, asset_limit in enumerate(self.asset_limits):
if asset_limit.model.id == model:
setattr(self.asset_limits[i], limit_type, limit)
is_found = True
break
if is_found is False:
raise Exception(f"Limit for Model {model} not found in the API key.")

def set_token_per_day(self, token_per_day: int, model: Optional[Union[Text, Model]] = None) -> None:
"""Set the token per day limit of an API key"""
self.__set_limit(token_per_day, model, "token_per_day")

def set_token_per_minute(self, token_per_minute: int, model: Optional[Union[Text, Model]] = None) -> None:
"""Set the token per minute limit of an API key"""
self.__set_limit(token_per_minute, model, "token_per_minute")

def set_request_per_day(self, request_per_day: int, model: Optional[Union[Text, Model]] = None) -> None:
"""Set the request per day limit of an API key"""
self.__set_limit(request_per_day, model, "request_per_day")

def set_request_per_minute(self, request_per_minute: int, model: Optional[Union[Text, Model]] = None) -> None:
"""Set the request per minute limit of an API key"""
self.__set_limit(request_per_minute, model, "request_per_minute")
5 changes: 3 additions & 2 deletions aixplain/modules/model/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,9 @@ def call_run_endpoint(url: Text, api_key: Text, payload: Dict) -> Dict:
"error_message": "Model Run: An error occurred while processing your request.",
}
else:
response = {"status": status, "data": data, "completed": True}
response = resp
else:
resp = resp["error"] if "error" in resp else resp
if r.status_code == 401:
error = f"Unauthorized API key: Please verify the spelling of the API key and its current validity. Details: {resp}"
elif 460 <= r.status_code < 470:
Expand All @@ -66,7 +67,7 @@ def call_run_endpoint(url: Text, api_key: Text, payload: Dict) -> Dict:
elif 480 <= r.status_code < 490:
error = f"Supplier-related error: Please ensure that the selected supplier provides the model you are trying to access. Details: {resp}"
elif 490 <= r.status_code < 500:
error = f"Validation-related error: Please ensure all required fields are provided and correctly formatted. Details: {resp}"
error = f"{resp}"
else:
status_code = str(r.status_code)
error = f"Status {status_code} - Unspecified error: {resp}"
Expand Down
34 changes: 25 additions & 9 deletions aixplain/modules/pipeline/designer/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,34 @@ def __init__(
pipeline: "DesignerPipeline" = None,
):

assert from_param in from_node.outputs, "Invalid from param"
assert to_param in to_node.inputs, "Invalid to param"

if isinstance(from_param, Param):
from_param = from_param.code
if isinstance(to_param, Param):
to_param = to_param.code

assert from_param in from_node.outputs, (
"Invalid from param. "
"Make sure all input params are already linked accordingly"
)

fp_instance = from_node.outputs[from_param]
from .nodes import Decision

if (
isinstance(to_node, Decision)
and to_param == to_node.inputs.passthrough.code
):
if from_param not in to_node.outputs:
to_node.outputs.create_param(
from_param,
fp_instance.data_type,
is_required=fp_instance.is_required,
)
else:
to_node.outputs[from_param].data_type = fp_instance.data_type

assert to_param in to_node.inputs, "Invalid to param"

self.from_node = from_node
self.to_node = to_node
self.from_param = from_param
Expand Down Expand Up @@ -233,9 +253,7 @@ def __init__(self, node: "Node", *args, **kwargs):
def add_param(self, param: Param) -> None:
# check if param already registered
if param in self:
raise ValueError(
f"Parameter with code '{param.code}' already exists."
)
raise ValueError(f"Parameter with code '{param.code}' already exists.")
self._params.append(param)
# also set attribute on the node dynamically if there's no
# any attribute with the same name
Expand Down Expand Up @@ -353,9 +371,7 @@ def attach_to(self, pipeline: "DesignerPipeline"):
:param pipeline: the pipeline
"""
assert not self.pipeline, "Node already attached to a pipeline"
assert (
self not in pipeline.nodes
), "Node already attached to a pipeline"
assert self not in pipeline.nodes, "Node already attached to a pipeline"
assert self.type, "Node type not set"

self.pipeline = pipeline
Expand Down
Loading